What is C2Rust?

C2Rust helps you migrate C99-compliant code to Rust. It provides:

  • a C to Rust translator
  • a Rust code refactoring tool
  • tools to cross-check execution of the C code against the new Rust code

The translator (or transpiler), produces unsafe Rust code that closely mirrors the input C code. The primary goal of the translator is to produce code that is functionally identical to the input C code. Generating safe or idomatic Rust is not a goal for the translator. Rather, we think the best approach is to gradually rewrite the translated Rust code using dedicated refactoring tools. To this end, we are building a refactoring tool that rewrites unsafe auto-translated Rust into safer idioms.

Some refactoring will have to be done by hand which may introduce errors. We provide plugins for clang and rustc so you can compile and run two binaries and check that they behave identically (at the level of function calls). For details on cross-checking see the cross-checks directory and the cross checking tutorial.

Here's the big picture:

C2Rust overview

To learn more, check out our RustConf'18 talk on YouTube and try the C2Rust translator online at www.c2rust.com.

Installation

Prerequisites

C2Rust requires LLVM 6 or 7 and its corresponding libraries and clang compiler. Python 3.4 or later, CMake 3.4.3 or later, and openssl (1.0) are also required. These prerequisites may be installed with the following commands, depending on your platform:

  • Ubuntu 16.04, 18.04 & 18.10:

      apt install build-essential llvm-6.0 clang-6.0 libclang-6.0-dev cmake libssl-dev pkg-config
    
  • Arch Linux:

      pacman -S base-devel llvm clang cmake openssl
    
  • OS X: XCode command-line tools and recent LLVM (we recommend the Homebrew version) are required.

      xcode-select --install
      brew install llvm python3 cmake openssl
    

Finally, a rust installation with Rustup is required on all platforms. You will also need to install rustfmt:

    rustup component add rustfmt-preview

Building C2Rust

cargo build --release

This builds the c2rust tool in the target/release/ directory.

On OS X with Homebrew LLVM, you need to point the build system at the LLVM installation as follows:

LLVM_CONFIG_PATH=/usr/local/opt/llvm/bin/llvm-config cargo build

If you have trouble with cargo build, the developer docs provide more details on the build system.

Translating C to Rust

To translate C files specified in compile_commands.json (see below), run the c2rust tool with the transpile subcommand:

c2rust transpile compile_commands.json

(The c2rust refactor tool is also available for refactoring Rust code, see refactoring).

The translator requires the exact compiler commands used to build the C code. To provide this information, you will need a standard compile_commands.json file. Many build systems can automatically generate this file, as it is used by many other tools, but see below for recommendations on how to generate this file for common build processes.

Once you have a compile_commands.json file describing the C build, translate the C code to Rust with the following command:

c2rust transpile path/to/compile_commands.json

To generate a Cargo.toml template for a Rust library, add the -e option:

c2rust transpile --emit-build-files path/to/compile_commands.json

To generate a Cargo.toml template for a Rust binary, do this:

c2rust transpile --main myprog path/to/compile_commands.json

Where --main myprog tells the transpiler to use the main method from myprog.rs as the entry point.

The translated Rust files will not depend directly on each other like normal Rust modules. They will export and import functions through the C API. These modules can be compiled together into a single static Rust library or binary.

There are several known limitations in this translator. The translator will emit a warning and attempt to skip function definitions that cannot be translated.

Generating compile_commands.json files

The compile_commands.json file can be automatically created using either cmake, intercept-build, or bear.

It may be a good idea to remove optimizations(-OX) from the compile commands file, as there are optimization builtins which we do not support translating.

... with cmake

When creating the initial build directory with cmake specify -DCMAKE_EXPORT_COMPILE_COMMANDS=1. This only works on projects configured to be built by cmake. This works on Linux and MacOS.

cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 ...

... with intercept-build

intercept-build (part of the scan-build tool) is recommended for non-cmake projects. intercept-build is bundled with clang under tools/scan-build-py but a standalone version can be easily installed via PIP with:

pip install scan-build

Usage:

intercept-build <build command>

You can also use intercept-build to generate a compilation database for compiling a single C file, for example:

intercept-build sh -c "cc program.c"

... with bear (linux only)

If you have bear installed, it can be used similarly to intercept-build:

bear <build command>

C2Rust Transpiler

Basic Usage

The transpiler module is invoked using the transpile sub-command of c2rust:

c2rust transpile [args] compile_commands.json [-- extra-clang-args]

The following arguments control the basic transpiler behavior:

  • --emit-modules - Emit each translated Rust file as a module (the default is to make each file its own crate).
  • --fail-on-error - Fail instead of warning if a source file cannot be fully translated.
  • --reduce-type-annotations - Do not emit explicit type annotations when unnecessary.
  • --translate-asm - Translate C inline assembly into corresponding Rust inline assembly. The translated assembly is unlikely to work as-is due to differences between GCC and LLVM (used in Rust) inline assembly styles, but it can provide a starting point for manual translation.
  • -f <regex>, --filter <regex> - Only translate files based on the regular expression used.

Creating cargo build files

The transpiler can create skeleton cargo build files for the translated Rust sources, controlled by the following options:

  • -e, --emit-build-files - Emit cargo build files to build the translated Rust code as a library. Build files are emitted in the directory specified by --output-dir, or if not specified, the directory containing compile_commands.json. This will not overwrite existing files, so remove these build files before re-creating build files. (implies --emit-modules)
  • -m <main_module>, --main <main_module> - Emit cargo build files to build the translated Rust code as a binary. The main function must be found in the specified module (C source file) <main_module>. <main_module> should be the bare module name, not including the .rs extension. Build files are emitted in the directory specified by --output-dir, or if not specified, the directory containing compile_commands.json. This will not overwrite existing files, so remove this build file directory before re-creating build files. (implies --emit-build-files)

Cross-check instrumentation

The transpiler can instrument the transpiled Rust code for cross-checking. The following options control this instrumentation:

  • -x, --cross-checks - Add macros and build files for cross-checking.
  • --use-fakechecks - Link against the fakechecks library for cross-checking instead of using the default online checks.
  • -X <config>, --cross-check-config <config> - Use the given config file as the cross-checking config.

For Developers

The c2rust-transpile library uses the c2rust-ast-exporter library to translate C code to Rust. The ast-exporter library links against the native clang compiler front end to parse C code and exports the AST for use in the transpiler, which is then implemented purely in Rust.

Known Limitations of Translation

This document tracks things that we know the translator can't handle, as well as things it probably won't ever handle.

Unimplemented

  • variadic function definitions (blocking Rust issue)
  • preserving comments (work in progress)
  • long double and _Complex types (partially blocked by Rust language)
  • Non x86/64 SIMD function/types and x86/64 SIMD function/types which have no rust equivalent

Unimplemented, might be implementable but very low priority

  • GNU packed structs (Rust has #[repr(packed)] compatible with #[repr(C)])
  • inline functions (Rust has #[inline])
  • restrict pointers (Rust has references)
  • inline assembly
  • macros

Likely won't ever support

  • longjmp/setjmp Although there are LLVM intrinsics for these, it is unclear how these interact with Rust (esp. idiomatic Rust).
  • jumps into and out of statement expressions We support GNU C statement expressions, but we can not handle jumping into or out of these. Both entry and exit into the expression have to be through the usual fall-through evaluation of the expression.

C2Rust-Bitfields Crate

C2Rust-Bitfields enables you to write structs containing bitfields. It has three primary goals:

  • Byte compatibility with equivalent C bitfield structs
  • The ability to take references/pointers to non bitfield fields
  • Provide methods to read from and write to bitfields

We currently provides a single custom derive, BitfieldStruct, as well as a dependent field attribute bitfield.

Requirements

  • Rust 1.30+
  • Rust Stable, Beta, or Nightly
  • Little Endian Architecture

Example

Suppose you want to write a super compact date struct which only takes up three bytes. In C this would look like this:

struct date {
    unsigned char day: 5;
    unsigned char month: 4;
    unsigned short year: 15;
} __attribute__((packed));

Clang helpfully provides us with this information:

*** Dumping AST Record Layout
         0 | struct date
     0:0-4 |   unsigned char day
     0:5-8 |   unsigned char month
    1:1-15 |   unsigned short year
           | [sizeof=3, align=1]

And this is enough to build our rust struct:

extern crate libc;

#[repr(C, align(1))]
#[derive(BitfieldStruct)]
struct Date {
    #[bitfield(name = "day", ty = "libc::c_uchar", bits = "0..=4")]
    #[bitfield(name = "month", ty = "libc::c_uchar", bits = "5..=8")]
    #[bitfield(name = "year", ty = "libc::c_ushort", bits = "9..=23")]
    day_month_year: [u8; 3]
}

fn main() {
    let mut date = Date {
        day_month_year: [0; 3]
    };

    date.set_day(18);
    date.set_month(7);
    date.set_year(2000);

    assert_eq!(date.day(), 18);
    assert_eq!(date.month(), 7);
    assert_eq!(date.year(), 2000);
}

Furthermore, C bitfield rules for overflow and signed integers are taken into account.

This crate can generate no_std compatible code when the no_std feature flag is provided.

Tests

Since rust doesn't support a build.rs exclusively for tests, you must manually compile the c test code and link it in.

$ clang tests/bitfields.c -c -fPIC -o tests/bitfields.o
$ ar -rc tests/libtest.a tests/bitfields.o
$ RUSTFLAGS="-L `pwd`/tests" cargo test

Acknowledgements

This crate is inspired by the rust-bitfield, packed_struct, and bindgen crates.

C2Rust Refactoring Tool

This is a refactoring tool for Rust programs, aimed at removing unsafety from automatically-generated Rust code.

Usage

c2rust refactor command line usage is as follows:

c2rust refactor [flags] <command> [command args] -- <input file> [rustc flags]

Flags for c2rust refactor are described by c2rust refactor --help.

See the command documentation for a list of commands, including complete usage and descriptions. Multiple commands can be separated by an argument consisting of a single semicolon, as in c2rust refactor cmd1 arg1 \; cmd2 arg2. (Note the semicolon needs to be escaped to prevent it from being interpreted by the shell.)

c2rust refactor requires rustc command line arguments for the program to be refactored, so that it can use rustc to load and typecheck the source code. For projects built with cargo, pass the --cargo flag to c2rust refactor and it will obtain the right arguments from cargo automatically. Otherwise, you must provide the rustc arguments on the c2rust refactor command line, after a -- separator.

Marks

Some commands require the user to "mark" some AST nodes for it to operate on. For example, the rename_struct command requires that the user mark the declaration of the struct that should be renamed.

Each mark associates a "label" with a specific AST node (identified by its NodeId). Labels are used to distinguish different types of marks, and a single node can have any number of marks with distinct labels. For example, when running the func_to_method command, which turns functions into methods in an inherent impl, the user must mark the functions to move with the target label and must mark the destination impl with the dest label. Nodes marked with other labels will be ignored. The set of labels recognized by a command is described in the command's documentation; by default, most commands that use marks operate on target.

The most flexible way of marking nodes is by using the select command. See the command documentation and src/select/mod.rs for details. Note that marks are not preserved across c2rust refactor invocations, so you usually want to run select followed by the command of interest using the ; separator mentioned above.

Refactoring Commands

abstract

Usage: abstract SIG PAT [BODY]

Replace all instances of pat with calls to a new function whose name and signature is given by sig. Example:

Input:

 1 + 2

After running abstract 'add(x: u32, y: u32) -> u32' 'x + y':

 add(1, 2)

 // Elsewhere:
 fn add(x: u32, y: u32) -> u32 { x + y }

All type and value parameter names in sig act as bindings when matching pat. The captured exprs and types are passed as parameters when building the new call expression. The body of the function is body, if provided, otherwise pat itself.

Non-ident patterns in sig are not supported. It is also an error for any type parameter's name to collide with any value parameter.

If matching with pat fails to capture expressions for any of the value parameters of sig, it is an error. If it fails to capture for a type parameter, the parameter is filled in with _ (infer).

autoretype

Usage: autoretype 'A: T'...

Marks: A... (specified in command)

Change the type of nodes with mark A to the new type T, propagating changes and inserting casts when possible to satisfy type checking. Multiple simultaneous retypings can be specified in this command as separate arguments. Each argument should be of the form: label: type where label is a mark label and type can be parsed as a valid rust type.

bitcast_retype

Usage: bitcast_retype PAT REPL

Marks: may read marks depending on PAT

For every type in the crate matching PAT, change the type to REPL. PAT and REPL are types, and can use placeholders in the manner of rewrite_ty. For each definitions whose type has changed, it also inserts mem::transmute calls at each use of the definition to fix discrepancies between the old and new types. (This implies that the original type and its replacement must be transmutable to each other.)

bytestr_to_str

Usage: bytestr_to_str

Marks: target

Convert bytestring literal expressions marked target to string literal expressions.

Note the mark must be placed on the expression, as it is currently difficult to mark a literal node.

canonicalize_externs

Usage: canonicalize_externs MOD_PATH

Marks: target

Replace foreign items ("externs") with references to externs in a different crate or module.

For each foreign fn or static marked target, if a foreign item with the same symbol exists in the module at MOD_PATH (which can be part of an external crate), it deletes the marked foreign item and replaces all its uses with uses of the matching foreign item in MOD_PATH. If a replacement item has a different type than the original, it also inserts the necessary casts at each use of the item.

canonicalize_structs

Usage: canonicalize_structs

Marks: target

For each type definition marked target, delete all other type definitions with the same name, and replace their uses with uses of the target type.

This only works when all the identically-named types have the same definition, such as when all are generated from #includes of the same C header.

Example:

 mod a {
     pub struct Foo { ... }  // Foo: target
 }

 mod b {
     struct Foo { ... }  // same as ::a::Foo

     unsafe fn use_foo(x: &Foo) { ... }
 }

After running canonicalize_structs:

 mod a {
     pub struct Foo { ... }
 }

 mod b {
     // 1. `struct Foo` has been deleted
     // 2. `use_foo` now references `::a::Foo` directly
     unsafe fn use_foo(x: &::a::Foo) { ... }
 }

Note that this transform does not check or adjust item visibility. If the target type is not visible throughout the crate, this may introduce compile errors.

char_literals

Obsolete - the translator now does this automatically.

Usage: char_literals

Replace integer literals cast to libc::c_char with actual char literals. For example, replaces 65 as libc::c_char with 'A' as libc::c_char.

clear_marks

Usage: clear_marks

Marks: clears all marks

Remove all marks from all nodes.

commit

Usage: commit

Write the current crate to disk (by rewriting the original source files), then read it back in, clearing all mark. This can be useful as a "checkpoint" between two sets of transformations, if applying both sets of changes at once proves to be too much for the rewriter.

This is only useful when the rewrite mode is inplace. Otherwise the "write" part of the operation won't actually change the original source files, and the "read" part will revert the crate to its original form.

convert_cast_as_ptr

convert_format_args

Usage: convert_format_args

Marks: target

For each function call, if one of its argument expressions is marked target, then parse that argument as a printf format string, with the subsequent arguments as the format args. Replace both the format string and the args with an invocation of the Rust format_args! macro.

This transformation applies casts to the remaining arguments to account for differences in argument conversion behavior between C-style and Rust-style string formatting. However, it does not attempt to convert the format_args! output into something compatible with the original C function. This results in a type error, so this pass should usually be followed up by an additional rewrite to change the function being called.

Example:

 printf("hello %d\n", 123);

If the string "hello %d\n" is marked target, then running convert_format_string will replace this call with

 printf(format_args!("hello {:}\n", 123 as i32));

At this point, it would be wise to replace the printf expression with a function that accepts the std::fmt::Arguments produced by format_args!.

convert_printfs

copy_marks

Usage: copy_marks OLD_MARK NEW_MARK

Marks: reads OLD_MARK; sets NEW_MARK

For every node bearing OLD_MARK, also apply NEW_MARK.

create_item

Usage: create_item ITEMS <inside/after> [MARK]

Marks: MARK/target

Parse ITEMS as item definitions, and insert the parsed items either inside (as the first child) or after (as a sibling) of the AST node bearing MARK (default: target). Supports adding items to both mods and blocks.

Note that other itemlikes, such as impl and trait items, are not handled by this command.

delete_items

Usage: delete_items

Marks: target

Delete all items marked target from the AST. This handles items in both mods and blocks, but doesn't handle other itemlikes.

delete_marks

Usage: delete_marks MARK

Marks: clears MARK

Remove MARK from every node where it appears.

fix_unused_unsafe

Usage: fix_unused_unsafe

Find unused unsafe blocks and turn them into ordinary blocks.

fold_let_assign

Usage: fold_let_assign

Fold together lets with no initializer or a trivial one, and subsequent assignments. For example, replace let x; x = 10; with let x = 10;.

func_to_method

Usage: func_to_method

Marks: target, dest

Turn functions marked target into static methods (no self) in the impl block marked dest. Turn functions that have an argument marked target into methods, replacing the named argument with self. Rewrite all uses of marked functions to call the new method versions.

Marked arguments of type T, &T, and &mut T (where T is the Self type of the dest impl) will be converted to self, &self, and &mut self respectively.

generalize_items

Usage: generalize_items VAR [TY]

Marks: target

Replace marked types with generic type parameters.

Specifically: add a new type parameter called VAR to each item marked target, replacing type annotations inside that item that are marked target with uses of the type parameter. Also update all uses of target items, passing TY as the new type argument when used inside a non-target item, and passing the type variable VAR when used inside a target item.

If TY is not provided, it defaults to a copy of the first type annotation that was replaced with VAR.

Example:

 struct Foo {    // Foo: target
     x: i32,     // i32: target
     y: i32,
 }

 fn f(foo: Foo) { ... }  // f: target

 fn main() {
     f(...);
 }

After running generalize_items T:

 // 1. Foo gains a new type parameter `T`
 struct Foo<T> {
     // 2. Marked type annotations become `T`
     x: T,
     y: i32,
 }

 // 3. `f` gains a new type parameter `T`, and passes
 // it through to uses of `Foo`
 fn f<T>(foo: Foo<T>) { ... }
 struct Bar<T> {
     foo: Foo<T>,
 }

 fn main() {
     // 4. Uses outside target items use `i32`, the
     // first type that was replaced with `T`.
     f::<i32>(...);
 }

ionize

Usage: ionize

Marks: target

Convert each union marked target to a type-safe Rust enum. The generated enums will have as_variant and as_variant_mut methods for each union field, which panic if the enum is not the named variant. Also updates assignments to union variables to assign one of the new enum variants, and updates uses of union fields to call the new methods instead.

let_x_uninitialized

Obsolete - the translator now does this automatically.

Usage: let_x_uninitialized

For each local variable that is uninitialized (let x;), add mem::uninitialized() as an initializer expression.

link_funcs

Usage: link_funcs

Link up function declarations and definitions with matching symbols across modules. For every foreign fn whose symbol matches a fn definition elsewhere in the program, it replaces all uses of the foreign fn with a direct call of the fn definition, and deletes the foreign fn.

Example:

 mod a {
     #[no_mangle]
     unsafe extern "C" fn foo() { ... }
 }

 mod b {
     extern "C" {
         // This resolves to `a::foo` during linking.
         fn foo();
     }

     unsafe fn use_foo() {
         foo();
     }
 }

After running link_funcs:

 mod a {
     #[no_mangle]
     unsafe extern "C" fn foo() { ... }
 }

 mod b {
     // 1. Foreign fn `foo` has been deleted
     unsafe fn use_foo() {
         // 2. `use_foo` now calls `foo` directly
         ::a::foo();
     }
 }

link_incomplete_types

Usage: link_incomplete_types

Link up type declarations and definitions with matching names across modules. For every foreign type whose name matches a type definition elsewhere in the program, it replaces all uses of the foreign type with the type definition, and deletes the foreign type.

Example:

 mod a {
     struct Foo { ... }
 }

 mod b {
     extern "C" {
         type Foo;
     }

     unsafe fn use_foo(x: &Foo) { ... }
 }

After running link_incomplete_types:

 mod a {
     struct Foo { ... }
 }

 mod b {
     // 1. Foreign fn `Foo` has been deleted
     // 2. `use_foo` now references `Foo` directly
     unsafe fn use_foo(x: &::a::Foo) { ... }
 }

mark_arg_uses

Usage: mark_arg_uses ARG_IDX MARK

Marks: reads MARK; sets/clears MARK

For every fn definition bearing MARK, apply MARK to expressions passed in as argument ARG_IDX in calls to that function. Removes MARK from the original function.

mark_callers

Usage: mark_callers MARK

Marks: reads MARK; sets/clears MARK

For every fn definition bearing MARK, apply MARK to call expressions that call that function. Removes MARK from the original function.

mark_field_uses

Obsolete - use select with match_expr!(typed!(::TheStruct).field) instead

Usage: mark_field_uses FIELD MARK

Marks: reads MARK; sets/clears MARK

For every struct definition bearing MARK, apply MARK to expressions that use FIELD of that struct. Removes MARK from the original struct.

mark_pub_in_mod

Obsolete - use select instead.

Usage: mark_pub_in_mod MARK

Marks: reads MARK; sets MARK

In each mod bearing MARK, apply MARK to every public item in the module.

mark_related_types

Usage: mark_related_types [MARK]

Marks: MARK/target

For each type annotation bearing MARK (default: target), apply MARK to all other type annotations that must be the same type according to (a simplified version of) Rust's typing rules.

For example, in this code:

 fn f(x: i32, y: i32) -> i32 {
     x
 }

The i32 annotations on x and the return type of f are related, because changing these annotations to two unequal types would produce a type error. But the i32 annotation on y is unrelated, and can be changed independently of the other two.

mark_uses

Usage: mark_uses MARK

Marks: reads MARK; sets/clears MARK

For every top-level definition bearing MARK, apply MARK to uses of that definition. Removes MARK from the original definitions.

ownership_annotate

Usage: ownership_annotate [MARK]

Marks: MARK/target

Run ownership analysis on functions bearing MARK (default: target), and add attributes to each function describing its inferred ownership properties. See analysis/ownership/README.md for details on ownership inference.

ownership_mark_pointers

Usage: ownership_mark_pointers [MARK]

Marks: reads MARK/target; sets ref, mut, and box

Run ownership analysis on functions bearing MARK (default: target), then for pointer type appearing in their argument and return types, apply one of the marks ref, mut, or box, reflecting the results of the ownership analysis. See analysis/ownership/README.md for details on ownership inference.

ownership_split_variants

Usage: ownership_split_variants [MARK]

Marks: MARK/target

Run ownership analysis on functions bearing MARK (default: target), and split each ownership-polymorphic functions into multiple monomorphic variants. See analysis/ownership/README.md for details on ownership inference.

pick_node

Test command - not intended for general use.

Usage: pick_node KIND FILE LINE COL

Find a node of kind KIND at location FILE:LINE:COL. If successful, logs the node's ID and span at level info.

print_marks

Test command - not intended for general use.

Usage: print_marks

Marks: reads all

Logs the ID and label of every mark, at level info.

print_spans

Test command - not intended for general use.

Usage: print_spans

Print IDs, spans, and pretty-printed source for all exprs, pats, tys, stmts, and items.

reconstruct_for_range

Usage: reconstruct_for_range

Replaces i = start; while i < end { ...; i += step; } with for i in (start .. end).step_by(step) { ...; }.

reconstruct_while

Obsolete - the translator now does this automatically.

Usage: reconstruct_while

Replaces all instances of loop { if !cond { break; } ... } with while loops.

remove_null_terminator

Usage: remove_null_terminator

Marks: target

Remove a trailing \0 character from marked string and bytestring literal expressions.

Note the mark must be placed on the expression, as it is currently difficult to mark a literal node.

remove_redundant_casts

remove_redundant_let_types

remove_unused_labels

Usage: remove_unused_labels

Removes loop labels that are not used in a named break or continue.

rename_items_regex

Usage: rename_items_regex PAT REPL [FILTER]

Marks: reads FILTER

Replace PAT (a regular expression) with REPL in all item names. If FILTER is provided, only items bearing the FILTER mark will be renamed.

rename_marks

Usage: rename_marks OLD_MARK NEW_MARK

Marks: reads/clears OLD_MARK; sets NEW_MARK

For every node bearing OLD_MARK, remove OLD_MARK and apply NEW_MARK.

rename_struct

Obsolete - use rename_items_regex instead.

Usage: rename_struct NAME

Marks: target

Rename the struct marked target to NAME. Only supports renaming a single struct at a time.

rename_unnamed

Usage: rename_unnamed

Renames all Idents that have unnamed throughout the Crate, so the Crate can have a completely unique naming scheme for Anonymous Types. This command should be ran after transpiling using c2rust-transpile, and is also mainly to be used when doing the reorganize_definition pass; although this pass can run on any c2rust-transpiled project.

Example:

pub mod foo {
    pub struct unnamed {
        a: i32
    }
}

pub mod bar {
    pub struct unnamed {
        b: usize
    }
}

Becomes:

pub mod foo {
    pub struct unnamed {
        a: i32
    }
}

pub mod bar {
    pub struct unnamed_1 {
        b: usize
    }
}

reoganize_definitions

replace_items

Usage: replace_items

Marks: target, repl

Replace all uses of items marked target with reference to the item marked repl, then remove all target items.

retype_argument

Usage: retype_argument NEW_TY WRAP UNWRAP

Marks: target

For each argument marked target, change the type of the argument to NEW_TY, and use WRAP and UNWRAP to convert values to and from the original type of the argument at call sites and within the function body.

WRAP should contain an expression placeholder __old, and should convert __old from the argument's original type to NEW_TY. UNWRAP should contain an expression placeholder __new, and should perform the opposite conversion.

retype_return

Usage: retype_return NEW_TY WRAP UNWRAP

Marks: target

For each function marked target, change the return type of the function to NEW_TY, and use WRAP and UNWRAP to convert values to and from the original type of the argument at call sites and within the function body.

WRAP should contain an expression placeholder __old, and should convert __old from the function's original return type to NEW_TY. UNWRAP should contain an expression placeholder __new, and should perform the opposite conversion.

retype_static

Usage: retype_static NEW_TY REV_CONV_ASSIGN CONV_RVAL CONV_LVAL [CONV_LVAL_MUT]

Marks: target

For each static marked target, change the type of the static to NEW_TY, using the remaining arguments (which are all all expression templates) to convert between the old and new types at the definition and use sites.

The expression arguments are used as follows:

  • REV_CONV_ASSIGN: In direct assignments to the static and in its initializer expression, the original assigned value is wrapped (as __old) in REV_CONV_ASSIGN to produce a value of type NEW_TY.
  • CONV_RVAL: In rvalue contexts, the static is wrapped (as __new) in CONV_RVAL to produce a value of the static's old type.
  • CONV_LVAL and CONV_LVAL_MUT are similar to CONV_RVAL, but for immutable and mutable lvalue contexts respectively. Especially for CONV_LVAL_MUT, the result of wrapping should be an lvalue expression (such as a dereference or field access), not a temporary, as otherwise updates to the static could be lost. CONV_LVAL_MUT is not required for immutable statics, which cannot appear in mutable lvalue contexts.

rewrite_expr

Usage: rewrite_expr PAT REPL [FILTER]

Marks: reads FILTER, if set; may read other marks depending on PAT

For every expression in the crate matching PAT, replace it with REPL. PAT and REPL are both Rust expressions. PAT can use placeholders to capture nodes from the matched AST, and REPL can refer to those same placeholders to substitute in the captured nodes. See the matcher module for details on AST pattern matching.

If FILTER is provided, only expressions marked FILTER will be rewritten. This usage is obsolete - change PAT to marked!(PAT, FILTER) to get the same behavior.

Example:

 fn double(x: i32) -> i32 {
     x * 2
 }

After running rewrite_expr '$e * 2' '$e + $e':

 fn double(x: i32) -> i32 {
     x + x
 }

Here $e * 2 matches x * 2, capturing x as $e. Then x is substituted for $e in $e + $e, producing the final expression x + x.

rewrite_stmts

rewrite_ty

Usage: rewrite_ty PAT REPL [FILTER]

Marks: reads FILTER, if set; may read other marks depending on PAT

For every type in the crate matching PAT, replace it with REPL. PAT and REPL are both Rust types. PAT can use placeholders to capture nodes from the matched AST, and REPL can refer to those same placeholders to substitute in the captured nodes. See the matcher module for details on AST pattern matching.

If FILTER is provided, only expressions marked FILTER will be rewritten. This usage is obsolete - change PAT to marked!(PAT, FILTER) to get the same behavior.

See the documentation for rewrite_expr for an example of this style of rewriting.

select

Usage: select MARK SCRIPT

Marks: sets MARK; may set/clear other marks depending on SCRIPT

Run node-selection script SCRIPT, and apply MARK to the nodes it selects. See select::SelectOp, select::Filter, and select::parser for details on select script syntax.

select_phase2

Usage: select_phase2 MARK SCRIPT

Marks: sets MARK; may set/clear other marks depending on SCRIPT

Works like select, but stops the compiler's analyses before typechecking happens. This means type information will not available, and script commands that refer to it will fail.

set_mutability

Usage: set_mutability MUT

Marks: target

Set the mutability of all items marked target to MUT. MUT is either imm or mut. This command only affects static items (including extern statics).

set_visibility

Usage: set_visibility VIS

Marks: target

Set the visibility of all items marked target to VIS. VIS is a Rust visibility qualifier such as pub, pub(crate), or the empty string.

Doesn't handle struct field visibility, for now.

sink_lets

Usage: sink_lets

For each local variable with a trivial initializer, move the local's declaration to the innermost block containing all its uses.

"Trivial" is currently defined as no initializer (let x;) or an initializer without any side effects. This transform requires trivial assignments to avoid reordering side effects.

sink_unsafe

Usage: sink_unsafe

Marks: target

For functions marked target, convert unsafe fn f() { ... } into fn () { unsafe { ... } }. Useful once unsafe argument handling has been eliminated from the function.

static_collect_to_struct

Usage: static_collect_to_struct STRUCT VAR

Marks: target

Collect marked statics into a single static struct.

Specifically:

  1. Find all statics marked target. For each one, record its name, type, and initializer expression, then delete it.
  2. Generate a new struct definition named STRUCT. For each marked static, include a field of STRUCT with the same name and type as the static.
  3. Generate a new static mut named VAR whose type is STRUCT. Initialize it using the initializer expressions for the marked statics.
  4. For each marked static foo, replace uses of foo with VAR.foo.

Example:

 static mut FOO: i32 = 100;
 static mut BAR: bool = true;

 unsafe fn f() -> i32 {
     FOO
 }

After running static_collect_to_struct Globals G, with both statics marked:

 struct Globals {
     FOO: i32,
     BAR: bool,
 }

 static mut G: Globals = Globals {
     FOO: 100,
     BAR: true,
 };

 unsafe fn f() -> i32 {
     G.FOO
 }

static_to_local

Usage: static_to_local

Marks: target

Delete each static marked target. For each function that uses a marked static, insert a new local variable definition replicating the marked static.

Example:

 static mut FOO: i32 = 100;  // FOO: target

 unsafe fn f() -> i32 {
     FOO
 }

 unsafe fn g() -> i32 {
     FOO + 1
 }

After running static_to_local:

 // `FOO` deleted

 // `f` gains a new local, replicating `FOO`.
 unsafe fn f() -> i32 {
     let FOO: i32 = 100;
     FOO
 }

 // If multiple functions use `FOO`, each one gets its own copy.
 unsafe fn g() -> i32 {
     let FOO: i32 = 100;
     FOO + 1
 }

static_to_local_ref

Usage: static_to_local_ref

Marks: target, user

For each function marked user, replace uses of statics marked target with uses of newly-introduced reference arguments. Afterward, no user function directly accesses any target static. At call sites of user functions, a reference to the original static is passed in for each new argument if the caller is not itself a user function; otherwise, the caller's own reference argument is passed through. Note this sometimes results in functions gaining arguments corresponding to statics that the function itself does not use, but that its callees do.

Example:

 static mut FOO: i32 = 100;  // FOO: target

 unsafe fn f() -> i32 {  // f: user
     FOO
 }

 unsafe fn g() -> i32 {  // g: user
     f()
 }

 unsafe fn h() -> i32 {
     g()
 }

After running static_to_local_ref:

 static mut FOO: i32 = 100;

 // `f` is a `user` that references `FOO`, so it
 // gains a new argument `FOO_`.
 unsafe fn f(FOO_: &mut i32) -> i32 {
     // References to `FOO` are replaced with `*FOO_`
     *FOO_
 }

 // `g` is a `user` that references `FOO` indirectly,
 // via fellow `user` `f`.
 unsafe fn g(FOO_: &mut i32) -> i32 {
     // `g` passes through its own `FOO_` reference
     // when calling `f`.
     f(FOO_)
 }

 // `h` is not a `user`, so its signature is unchanged.
 unsafe fn h() -> i32 {
     // `h` passes in a reference to the original
     // static `FOO`.
     g(&mut FOO)
 }

struct_assign_to_update

Usage: struct_assign_to_update

Replace all struct field assignments with functional update expressions.

Example:

 let mut x: S = ...;
 x.f = 1;
 x.g = 2;

After running struct_assign_to_update:

 let mut x: S = ...;
 x = S { f: 1, ..x };
 x = S { g: 2, ..x };

struct_merge_updates

Usage: struct_merge_updates

Merge consecutive struct updates into a single update.

Example:

 let mut x: S = ...;
 x = S { f: 1, ..x };
 x = S { g: 2, ..x };

After running struct_assign_to_update:

 let mut x: S = ...;
 x = S { f: 1, g: 2, ..x };

test_analysis_ownership

Test command - not intended for general use.

Usage: test_analysis_ownership

Runs the ownership analysis and dumps the results to stderr.

test_analysis_type_eq

Test command - not intended for general use.

Usage: test_analysis_type_eq

Runs the type_eq analysis and logs the result (at level info).

test_debug_callees

Test command - not intended for general use.

Usage: test_debug_callees

Inspect the details of each Call expression. Used to debug RefactorCtxt::opt_callee_info.

test_f_plus_one

Test command - not intended for general use.

Usage: test_f_plus_one

Replace the expression f(__x) with __x + 1 everywhere it appears.

test_insert_remove_args

Test command - not intended for general use.

Usage: test_insert_remove_args INS REM

In each function marked target, insert new arguments at each index listed in INS (a comma-separated list of integers), then delete the arguments whose original indices are listed in REM.

This is used for testing sequence rewriting of fn argument lists.

test_one_plus_one

Test command - not intended for general use.

Usage: test_one_plus_one

Replace the expression 2 with 1 + 1 everywhere it appears.

test_reflect

Test command - not intended for general use.

Usage: test_reflect

Applies path and ty reflection on every expr in the program.

test_replace_stmts

Test command - not intended for general use.

Usage: test_replace_stmts OLD NEW

Replace statement(s) OLD with NEW everywhere it appears.

test_typeck_loop

Test command - not intended for general use.

Usage: test_typeck_loop

Runs a no-op typechecking loop for three iterations. Used to test the typechecking loop and AST re-analysis code.

type_fix_rules

Usage: type_fix_rules RULE...

Attempts to fix type errors in the crate using the provided rules. Each rule has the form "ectx, actual_ty, expected_ty => cast_expr".

  • ectx is one of rval, lval, lval_mut, or *, and determines in what kinds of expression contexts the rule applies.
  • actual_ty is a pattern to be matched against the (reflected) actual expression type.
  • expected_ty is a pattern to be matched against the (reflected) expected expression type.
  • cast_expr is a template for generating a cast expression.

For expressions in context ectx, whose actual type matches actual_ty and whose expected type matches expected_ty (and where actual != expected), the expr is substituted into cast_expr to replace the original expr with one of the expected type. During substitution, cast_expr has access to variables captured from both actual_ty and expected_ty, as well as __old containing the original (ill-typed) expression.

uninit_to_default

Obsolete - works around translator problems that no longer exist.

Usage: uninit_to_default

In local variable initializers, replace mem::uninitialized() with an appropriate default value of the variable's type.

wrap_api

Usage: wrap_api

Marks: target

For each function foo marked target:

  1. Reset the function's ABI to "Rust" (the default)
  2. Remove any #[no_mangle] or #[export_name] attributes
  3. Generate a new wrapper function called foo_wrapper with foo's old ABI and an #[export_name="foo"] attribute.

Calls to foo are left unchanged. The result is that callers from C use the wrapper function, while internal calls use foo directly, and the signature of foo can be changed freely without affecting external callers.

wrap_extern

Usage: wrap_extern

Marks: target, dest

For each foreign function marked target, generate a wrapper function in the module marked dest, and rewrite all uses of the function to call the wrapper instead.

Example:

 extern "C" {
     fn foo(x: i32) -> i32;
 }

 mod wrappers {
     // empty
 }

 fn main() {
     let x = unsafe { foo(123) };
 }

After transformation, with fn foo marked target and mod wrappers marked dest:

 extern "C" {
     fn foo(x: i32) -> i32;
 }

 mod wrappers {
     unsafe fn foo(x: i32) -> i32 {
         ::foo(x)
     }
 }

 fn main() {
     let x = unsafe { ::wrappers::foo(123) };
 }

Note that this also replaces the function in expressions that take its address, which may cause problem as the wrapper function has a different type that the original (it lacks the extern "C" ABI qualifier).

wrapping_arith_to_normal

Usage: wrapping_arith_to_normal

Replace all uses of wrapping arithmetic methods with ordinary arithmetic operators. For example, replace x.wrapping_add(y) with x + y.

Reference

Module Refactor

Refactoring module

Tables

Stmt AST Stmt
Expr AST Expr

Fields

refactor Global refactoring state

Class RefactorState

RefactorState:run_command (name, args) Run a builtin refactoring command
RefactorState:transform (callback) Run a custom refactoring transformation

Class MatchCtxt

MatchCtxt:parse_stmts (pat) Parse statements and add them to this MatchCtxt
MatchCtxt:parse_expr (pat) Parse an expressiong and add it to this MatchCtxt
MatchCtxt:fold_with (needle, crate, callback) Find matches of pattern within crate and rewrite using callback
MatchCtxt:get_expr (Expression) Get matched binding for an expression variable
MatchCtxt:get_stmt (Statement) Get matched binding for a statement variable
MatchCtxt:try_match (pat, target) Attempt to match target against pat, updating bindings if matched.
MatchCtxt:subst (replacement) Substitute the currently matched AST node with a new AST node

Class TransformCtxt

TransformCtxt:replace_stmts_with (needle, callback) Replace matching statements using given callback
TransformCtxt:replace_expr_with (needle, callback) Replace matching expressions using given callback
TransformCtxt:match () Create a new, empty MatchCtxt
TransformCtxt:get_ast (node) Retrieve a Lua version of an AST node



<h2 class="section-header "><a name="Tables"></a>Tables</h2>

<dl class="function">
<dt>
<a name = "Stmt"></a>
<strong>Stmt</strong>
</dt>
<dd>
AST Stmt


<h3>Fields:</h3>
<ul>
    <li><span class="parameter">type</span>
     "Stmt"
    </li>
    <li><span class="parameter">kind</span>
        <span class="types"><a class="type" href="https://www.lua.org/manual/5.3/manual.html#6.4">string</a></span>
     <code>StmtKind</code> of this statement</p>

StmtKind::Local only:

  • ty AstNode Type of local (optional)
  • init AstNode Initializer of local (optional)
  • pat AstNode Name of local

    StmtKind::Item only:

  • item AstNode Item node

    StmtKind::Semi and StmtKind::Expr only:

  • expr AstNode Expression in this statement
  • Expr
    AST Expr
    <h3>Fields:</h3>
    <ul>
        <li><span class="parameter">type</span>
         "Expr"
        </li>
        <li><span class="parameter">kind</span>
            <span class="types"><a class="type" href="https://www.lua.org/manual/5.3/manual.html#6.4">string</a></span>
         <code>ExprKind</code> of this expression</p>
    

    ExprKind::Lit only:

  • value Literal value of this expression
  • Fields

    <dl class="function">
    <dt>
    <a name = "refactor"></a>
    <strong>refactor</strong>
    </dt>
    <dd>
    Global refactoring state
    
    
    <ul>
        <li><span class="parameter">refactor</span>
         RefactorState object
        </li>
    </ul>
    

    Class RefactorState

          <div class="section-description">
          Refactoring context
          </div>
    <dl class="function">
    <dt>
    <a name = "RefactorState:run_command"></a>
    <strong>RefactorState:run_command (name, args)</strong>
    </dt>
    <dd>
    Run a builtin refactoring command
    
    
    <h3>Parameters:</h3>
    <ul>
        <li><span class="parameter">name</span>
            <span class="types"><a class="type" href="https://www.lua.org/manual/5.3/manual.html#6.4">string</a></span>
         Command to run
        </li>
        <li><span class="parameter">args</span>
            <span class="types"><a class="type" href="https://www.lua.org/manual/5.3/manual.html#6.4">{string,...}</a></span>
         List of arguments for the command
        </li>
    </ul>
    
    RefactorState:transform (callback)
    Run a custom refactoring transformation
    <h3>Parameters:</h3>
    <ul>
        <li><span class="parameter">callback</span>
            <span class="types"><span class="type">function(TransformCtxt,AstNode)</span></span>
         Transformation function called with a fresh <a href="scripting_api.html#TransformCtxt">TransformCtxt</a> and the crate to be transformed.
        </li>
    </ul>
    

    Class MatchCtxt

          <div class="section-description">
          A match context
          </div>
    <dl class="function">
    <dt>
    <a name = "MatchCtxt:parse_stmts"></a>
    <strong>MatchCtxt:parse_stmts (pat)</strong>
    </dt>
    <dd>
    Parse statements and add them to this MatchCtxt
    
    
    <h3>Parameters:</h3>
    <ul>
        <li><span class="parameter">pat</span>
            <span class="types"><a class="type" href="https://www.lua.org/manual/5.3/manual.html#6.4">string</a></span>
         Pattern to parse
        </li>
    </ul>
    
    <h3>Returns:</h3>
    <ol>
    
           <span class="types"><a class="type" href="scripting_api.html#AstNode">AstNode</a></span>
        The parsed statements
    </ol>
    
    MatchCtxt:parse_expr (pat)
    Parse an expressiong and add it to this MatchCtxt
    <h3>Parameters:</h3>
    <ul>
        <li><span class="parameter">pat</span>
            <span class="types"><a class="type" href="https://www.lua.org/manual/5.3/manual.html#6.4">string</a></span>
         Pattern to parse
        </li>
    </ul>
    
    <h3>Returns:</h3>
    <ol>
    
           <span class="types"><a class="type" href="scripting_api.html#AstNode">AstNode</a></span>
        The parsed expression
    </ol>
    
    MatchCtxt:fold_with (needle, crate, callback)
    Find matches of pattern within crate and rewrite using callback
    <h3>Parameters:</h3>
    <ul>
        <li><span class="parameter">needle</span>
            <span class="types"><a class="type" href="scripting_api.html#AstNode">AstNode</a></span>
         Pattern to search for
        </li>
        <li><span class="parameter">crate</span>
            <span class="types"><a class="type" href="scripting_api.html#AstNode">AstNode</a></span>
         Crate to fold over
        </li>
        <li><span class="parameter">callback</span>
            <span class="types"><span class="type">function(AstNode,MatchCtxt)</span></span>
         Function called for each match. Takes the matching node and a new <a href="scripting_api.html#MatchCtxt">MatchCtxt</a> for that match.
        </li>
    </ul>
    
    MatchCtxt:get_expr (Expression)
    Get matched binding for an expression variable
    <h3>Parameters:</h3>
    <ul>
        <li><span class="parameter">Expression</span>
            <span class="types"><a class="type" href="https://www.lua.org/manual/5.3/manual.html#6.4">string</a></span>
         variable pattern
        </li>
    </ul>
    
    <h3>Returns:</h3>
    <ol>
    
           <span class="types"><a class="type" href="scripting_api.html#AstNode">AstNode</a></span>
        Expression matched by this binding
    </ol>
    
    MatchCtxt:get_stmt (Statement)
    Get matched binding for a statement variable
    <h3>Parameters:</h3>
    <ul>
        <li><span class="parameter">Statement</span>
            <span class="types"><a class="type" href="https://www.lua.org/manual/5.3/manual.html#6.4">string</a></span>
         variable pattern
        </li>
    </ul>
    
    <h3>Returns:</h3>
    <ol>
    
           <span class="types"><a class="type" href="scripting_api.html#AstNode">AstNode</a></span>
        Statement matched by this binding
    </ol>
    
    MatchCtxt:try_match (pat, target)
    Attempt to match target against pat, updating bindings if matched.
    <h3>Parameters:</h3>
    <ul>
        <li><span class="parameter">pat</span>
            <span class="types"><a class="type" href="scripting_api.html#AstNode">AstNode</a></span>
         AST (potentially with variable bindings) to match with
        </li>
        <li><span class="parameter">target</span>
            <span class="types"><a class="type" href="scripting_api.html#AstNode">AstNode</a></span>
         AST to match against
        </li>
    </ul>
    
    <h3>Returns:</h3>
    <ol>
    
           <span class="types"><span class="type">bool</span></span>
        true if match was successful
    </ol>
    
    MatchCtxt:subst (replacement)
    Substitute the currently matched AST node with a new AST node
    <h3>Parameters:</h3>
    <ul>
        <li><span class="parameter">replacement</span>
            <span class="types"><a class="type" href="scripting_api.html#AstNode">AstNode</a></span>
         New AST node to replace the currently matched AST. May include variable bindings if these bindings were matched by the search pattern.
        </li>
    </ul>
    
    <h3>Returns:</h3>
    <ol>
    
           <span class="types"><a class="type" href="scripting_api.html#AstNode">AstNode</a></span>
        New AST node with variable bindings replaced by their matched values
    </ol>
    

    Class TransformCtxt

          <div class="section-description">
          Transformation context
          </div>
    <dl class="function">
    <dt>
    <a name = "TransformCtxt:replace_stmts_with"></a>
    <strong>TransformCtxt:replace_stmts_with (needle, callback)</strong>
    </dt>
    <dd>
    Replace matching statements using given callback
    
    
    <h3>Parameters:</h3>
    <ul>
        <li><span class="parameter">needle</span>
            <span class="types"><a class="type" href="https://www.lua.org/manual/5.3/manual.html#6.4">string</a></span>
         Statements pattern to search for, may include variable bindings
        </li>
        <li><span class="parameter">callback</span>
            <span class="types"><span class="type">function(AstNode,MatchCtxt)</span></span>
         Function called for each match. Takes the matching node and a new <a href="scripting_api.html#MatchCtxt">MatchCtxt</a> for that match. See <a href="scripting_api.html#MatchCtxt:fold_with">MatchCtxt:fold_with</a>
        </li>
    </ul>
    
    TransformCtxt:replace_expr_with (needle, callback)
    Replace matching expressions using given callback
    <h3>Parameters:</h3>
    <ul>
        <li><span class="parameter">needle</span>
            <span class="types"><a class="type" href="https://www.lua.org/manual/5.3/manual.html#6.4">string</a></span>
         Expression pattern to search for, may include variable bindings
        </li>
        <li><span class="parameter">callback</span>
            <span class="types"><span class="type">function(AstNode,MatchCtxt)</span></span>
         Function called for each match. Takes the matching node and a new <a href="scripting_api.html#MatchCtxt">MatchCtxt</a> for that match. See <a href="scripting_api.html#MatchCtxt:fold_with">MatchCtxt:fold_with</a>
        </li>
    </ul>
    
    TransformCtxt:match ()
    Create a new, empty MatchCtxt
    <h3>Returns:</h3>
    <ol>
    
           <span class="types"><a class="type" href="scripting_api.html#MatchCtxt">MatchCtxt</a></span>
        New match context
    </ol>
    
    TransformCtxt:get_ast (node)
    Retrieve a Lua version of an AST node
    <h3>Parameters:</h3>
    <ul>
        <li><span class="parameter">node</span>
            <span class="types"><a class="type" href="scripting_api.html#AstNode">AstNode</a></span>
         AST node handle
        </li>
    </ul>
    
    <h3>Returns:</h3>
    <ol>
    
        Struct representation of this AST node. Valid return types are <a href="scripting_api.html#Stmt">Stmt</a>, and <a href="scripting_api.html#Expr">Expr</a>.
    </ol>
    
    generated by LDoc 1.4.6 Last updated 2019-02-21 10:38:12

    c2rust refactor provides a general-purpose rewriting command, rewrite_expr, for transforming expressions. In its most basic form, rewrite_expr replaces one expression with another, everywhere in the crate:

    rewrite_expr '1+1' '2'
    

    1
    fn main() {
    1
    fn main() {
    2
        println!("{}", 1 + 1);
    2
        println!("{}", 2);
    3
        println!("{}", 1 + /*comment*/ 1);
    3
        println!("{}", 2);
    4
        println!("{}", 1 + 11);
    4
        println!("{}", 1 + 11);
    5
    }
    5
    }

    Here, all instances of the expression 1+1 (the "pattern") are replaced with 2 (the "replacement").

    rewrite_expr parses both the pattern and the replacement as Rust expressions, and compares the structure of the expression instead of its raw text when looking for occurrences of the pattern. This lets it recognize that 1 + 1 and 1 + /* comment */ both match the pattern 1+1 (despite being textually distinct), while 1+11 does not (despite being textually similar).

    Metavariables

    In rewrite_expr's expression pattern, any name beginning with double underscores is a metavariable. Just as a variable in an ordinary Rust match expression will match any value (and bind it for later use), a metavariable in an expression pattern will match any Rust code. For example, the expression pattern __x + 1 will match any expression that adds 1 to something:

    rewrite_expr '__x + 1' '11'
    

    1
    fn f() -> i32 {
    1
    fn f() -> i32 {
    2
        123
    2
        123
    3
    }
    3
    }
    4
    4
    5
    fn main() {
    5
    fn main() {
    6
        println!("a = {}", 1 + 1);
    6
        println!("a = {}", 11);
    7
        println!("b = {}", 2 * 3 + 1);
    7
        println!("b = {}", 11);
    8
        println!("c = {}", 4 + 5 + 1);
    8
        println!("c = {}", 11);
    9
        println!("d = {}", f() + 1);
    9
        println!("d = {}", 11);
    10
    }
    10
    }

    In these examples, the __x metavariable matches the expressions 1, 2 * 3, and f().

    Using bindings

    When a metavariable matches against some piece of code, the code it matches is bound to the variable for later use. Specifically, rewrite_expr's replacement argument can refer back to those metavariables to substitute in the matched code:

    rewrite_expr '__x + 1' '11 * __x'
    

    1
    fn f() -> i32 {
    1
    fn f() -> i32 {
    2
        123
    2
        123
    3
    }
    3
    }
    4
    4
    5
    fn main() {
    5
    fn main() {
    6
        println!("a = {}", 1 + 1);
    6
        println!("a = {}", 11 * 1);
    7
        println!("b = {}", 2 * 3 + 1);
    7
        println!("b = {}", 11 * (2 * 3));
    8
        println!("c = {}", 4 + 5 + 1);
    8
        println!("c = {}", 11 * (4 + 5));
    9
        println!("d = {}", f() + 1);
    9
        println!("d = {}", 11 * f());
    10
    }
    10
    }

    In each case, the expression bound to the __x metavariable is substituted into the right-hand side of the multiplication in the replacement.

    Multiple occurences

    Finally, the same metavariable can appear multiple times in the pattern. In that case, the pattern matches only if each occurence of the metavariable matches the same expression. For example:

    rewrite_expr '__x + __x' '2 * __x'
    

    1
    fn f() -> i32 {
    1
    fn f() -> i32 {
    2
        123
    2
        123
    3
    }
    3
    }
    4
    4
    5
    fn main() {
    5
    fn main() {
    6
        let a = 2;
    6
        let a = 2;
    7
        println!("{}", 1 + 1);
    7
        println!("{}", 2 * 1);
    8
        println!("{}", a + a);
    8
        println!("{}", 2 * a);
    9
        println!("{}", f() + f());
    9
        println!("{}", 2 * f());
    10
        println!("{}", f() + 1);
    10
        println!("{}", f() + 1);
    11
    }
    11
    }

    Here a + a and f() + f() are both replaced, but f() + 1 is not because __x cannot match both f() and 1 at the same time.

    Example: adding a function argument

    Suppose we wish to add an argument to an existing function. All current callers of the function should pass a default value of 0 for this new argument. We can update the existing calls like this:

    rewrite_expr 'my_func(__x, __y)' 'my_func(__x, __y, 0)'
    

    1
    fn my_func(x: i32, y: i32) {
    1
    fn my_func(x: i32, y: i32) {
    2
        /* ... */
    2
        /* ... */
    3
    }
    3
    }
    4
    4
    5
    fn main() {
    5
    fn main() {
    6
        my_func(1, 2);
    6
        my_func(1, 2, 0);
    7
        let x = 123;
    7
        let x = 123;
    8
        my_func(x, x);
    8
        my_func(x, x, 0);
    9
        my_func(0, {
    9
        my_func(
    10
            0,
    11
            {
    10
            let y = x;
    12
                let y = x;
    11
            y + y
    13
                y + y
    14
            },
    15
            0,
    12
        });
    16
        );
    13
    }
    17
    }

    Every call to my_func now passes a third argument, and we can update the definition of my_func to match.

    Special matching forms

    rewrite_expr supports several special matching forms that can appear in patterns to add extra restrictions to matching.

    def!

    A pattern such as def!(::foo::f) matches any ident or path expression that resolves to the function whose absolute path is ::foo::f. For example, to replace all expressions referencing the function foo::f with ones referencing foo::g:

    rewrite_expr 'def!(::foo::f)' '::foo::g'
    

    1
    mod foo {
    1
    mod foo {
    2
        fn f() {
    2
        fn f() {
    3
            /* ... */
    3
            /* ... */
    4
        }
    4
        }
    5
        fn g() {
    5
        fn g() {
    6
            /* ... */
    6
            /* ... */
    7
        }
    7
        }
    8
    }
    8
    }
    9
    9
    10
    fn main() {
    10
    fn main() {
    11
        use self::foo::f;
    11
        use self::foo::f;
    12
        // All these calls get rewritten
    12
        // All these calls get rewritten
    13
        f();
    13
        f();
    14
        foo::f();
    15
        ::foo::f();
    14
        ::foo::g();
    15
        ::foo::g();
    16
    }
    16
    }
    17
    17
    18
    mod bar {
    18
    mod bar {
    19
        fn f() {}
    19
        fn f() {}
    20
    20
    21
        fn f_caller() {
    21
        fn f_caller() {
    22
            // This call does not...
    22
            // This call does not...
    23
            f();
    23
            f();
    24
            // But this one still does
    24
            // But this one still does
    25
            super::foo::f();
    25
            ::foo::g();
    26
        }
    26
        }
    27
    }
    27
    }

    This works for all direct references to f, whether by relative path (foo::f), absolute path (::foo::f), or imported identifier (just f, with use foo::f in scope). It can even handle imports under a different name (f2 with use foo::f as f2 in scope), since it checks only the path of the referenced definition, not the syntax used to reference it.

    Under the hood

    When rewrite_expr attempts to match def!(path) against some expression e, it actually completely ignores the content of e itself. Instead, it performs these steps:

    1. Check rustc's name resolution results to find the definition d that e resolves to. (If e doesn't resolve to a definition, then the matching fails.)
    2. Construct an absolute path dpath referring to d. For definitions in the current crate, this path looks like ::mod1::def1. For definitions in other crates, it looks like ::crate1::mod1::def1.
    3. Match dpath against the path pattern provided as the argument of def!. Then e matches def!(path) if dpath matches path, and fails to match otherwise.

    Debugging match failures

    Matching with def! can sometimes fail in surprising ways, since the user-provided path is matched against a generated path that may not appear explicitly anywhere in the source code. For example, this attempt to match HashMap::new does not succeed:

    rewrite_expr
        'def!(::std::collections::hash_map::HashMap::new)()'
        '::std::collections::hash_map::HashMap::with_capacity(10)'
    

    1
    use std::collections::hash_map::HashMap;
    1
    use std::collections::hash_map::HashMap;
    2
    2
    3
    fn main() {
    3
    fn main() {
    4
        let m: HashMap<i32, i32> = HashMap::new();
    4
        let m: HashMap<i32, i32> = HashMap::new();
    5
    }
    5
    }

    The debug_match_expr command exists to diagnose such problems. It takes only a pattern, and prints information about attempts to match it at various points in the crate:

    debug_match_expr 'def!(::std::collections::hash_map::HashMap::new)()'
    

    Here, its output includes this line:

    def!(): trying to match pattern path(::std::collections::hash_map::HashMap::new) against AST path(::std::collections::HashMap::new)
    

    Which reveals the problem: the absolute path def! generates for HashMap::new uses the reexport at std::collections::HashMap, not the canonical definition at std::collections::hash_map::HashMap. Updating the previous rewrite_expr command allows it to succeed:

    rewrite_expr
        'def!(::std::collections::HashMap::new)()'
        '::std::collections::HashMap::with_capacity(10)'
    

    1
    use std::collections::hash_map::HashMap;
    1
    use std::collections::hash_map::HashMap;
    2
    2
    3
    fn main() {
    3
    fn main() {
    4
        let m: HashMap<i32, i32> = HashMap::new();
    4
        let m: HashMap<i32, i32> = ::std::collections::HashMap::with_capacity(10);
    5
    }
    5
    }

    Metavariables

    The argument to def! is a path pattern, which can contain metavariables just like the overall expression pattern. For instance, we can rewrite all calls to functions from the foo module:

    rewrite_expr 'def!(::foo::__name)()' '123'
    

    1
    mod foo {
    1
    mod foo {
    2
        fn f() {
    2
        fn f() {
    3
            /* ... */
    3
            /* ... */
    4
        }
    4
        }
    5
        fn g() {
    5
        fn g() {
    6
            /* ... */
    6
            /* ... */
    7
        }
    7
        }
    8
    }
    8
    }
    9
    9
    10
    mod bar {
    10
    mod bar {
    11
        fn f() {
    11
        fn f() {
    12
            /* ... */
    12
            /* ... */
    13
        }
    13
        }
    14
        fn g() {
    14
        fn g() {
    15
            /* ... */
    15
            /* ... */
    16
        }
    16
        }
    17
    }
    17
    }
    18
    18
    19
    fn main() {
    19
    fn main() {
    20
        foo::f();
    20
        123;
    21
        foo::g();
    21
        123;
    22
    }
    22
    }

    Since every definition in the foo module has an absolute path of the form ::foo::(something), they all match the expression pattern def!(::foo::__name).

    Like any other metavariable, the ones in a def! path pattern can be used in the replacement expression to substitute in the captured name. For example, we can replace all references to items in the foo module with references to the same-named items in the bar module:

    rewrite_expr 'def!(::foo::__name)' '::bar::__name'
    

    1
    mod foo {
    1
    mod foo {
    2
        fn f() {
    2
        fn f() {
    3
            /* ... */
    3
            /* ... */
    4
        }
    4
        }
    5
        fn g() {
    5
        fn g() {
    6
            /* ... */
    6
            /* ... */
    7
        }
    7
        }
    8
    }
    8
    }
    9
    9
    10
    mod bar {
    10
    mod bar {
    11
        fn f() {
    11
        fn f() {
    12
            /* ... */
    12
            /* ... */
    13
        }
    13
        }
    14
        fn g() {
    14
        fn g() {
    15
            /* ... */
    15
            /* ... */
    16
        }
    16
        }
    17
    }
    17
    }
    18
    18
    19
    fn main() {
    19
    fn main() {
    20
        foo::f();
    20
        ::bar::f();
    21
        foo::g();
    21
        ::bar::g();
    22
    }
    22
    }

    Note, however, that each metavariable in a path pattern can match only a single ident. This means foo::__name will not match the path to an item in a submodule, such as foo::one::two. Handling these would require an additional rewrite step, such as rewrite_expr 'def!(::foo::__name1::__name2)' '::bar::__name1::__name2'.

    typed!

    A pattern of the form typed!(e, ty) matches any expression that matches the pattern e, but only if the type of that expression matches the pattern ty. For example, we can perform a rewrite that only affects i32s:

    rewrite_expr 'typed!(__e, i32)' '0'
    

    1
    fn main() {
    1
    fn main() {
    2
        let x = 100_i32;
    2
        let x = 0;

    4
        let z = x + y;
    4
        let z = 0;
    5
    5
    6
        let a = "hello";
    6
        let a = "hello";
    7
        let b = format!("{}, {}", a, "world");
    7
        let b = format!("{}, {}", a, "world");
    8
    }
    8
    }

    Every expression matches the metavariable __e, but only the i32s (whether literals or variables of type i32) are affected by the rewrite.

    Under the hood

    Internally, typed! works much like def!. To match an expression e against typed!(e_pat, ty_pat), rewrite_expr follows these steps:

    1. Consult rustc's typechecking results to get the type of e. Call that type rustc_ty.
    2. rustc_ty is an internal, abstract representation of the type, which is not suitable for matching. Construct a concrete representation of rustc_ty, and call it ty.
    3. Match e against e_pat and ty against ty_pat. Then e matches typed!(e_pat, ty_pat) if both matches succeed, and fails to match otherwise.

    Debugging match failures

    When matching fails unexpectedly, debug_match_expr is once again useful for understanding the problem. For example, this rewriting command has no effect:

    rewrite_expr "typed!(__e, &'static str)" '"hello"'
    

    1
    fn main() {
    1
    fn main() {
    2
        let a = "hello";
    2
        let a = "hello";
    3
        let b = format!("{}, {}", a, "world");
    3
        let b = format!("{}, {}", a, "world");
    4
    }
    4
    }

    Passing the same pattern to debug_match_expr produces output that includes the following:

    typed!(): trying to match pattern type(&'static str) against AST type(&str)
    

    Now the problem is clear: the concrete type representation constructed for matching omits lifetimes. Replacing &'static str with &str in the pattern causes the rewrite to succeed:

    rewrite_expr 'typed!(__e, &str)' '"hello"'
    

    1
    fn main() {
    1
    fn main() {
    2
        let a = "hello";
    2
        let a = "hello";
    3
        let b = format!("{}, {}", a, "world");
    3
        let b = format!("{}, {}", "hello", "hello");
    4
    }
    4
    }

    Metavariables

    The expression pattern and type pattern arguments of typed!(e, ty) are handled using the normal rewrite_expr matching engine, which means they can contain metavariables and other special matching forms. For example, metavariables can capture both parts of the expression and parts of its type for use in the replacement:

    rewrite_expr
        'typed!(Vec::with_capacity(__n), ::std::vec::Vec<__ty>)'
        '::std::iter::repeat(<__ty>::default())
            .take(__n)
            .collect::<Vec<__ty>>()'
    

    1
    fn main() {
    1
    fn main() {
    2
        let v: Vec<&'static str> = Vec::with_capacity(20);
    2
        let v: Vec<&'static str> = ::std::iter::repeat(<&str>::default())
    3
            .take(20)
    4
            .collect::<Vec<&str>>();
    3
    5
    4
        let v: Vec<_> = Vec::with_capacity(10);
    6
        let v: Vec<_> = ::std::iter::repeat(<i32>::default())
    7
            .take(10)
    8
            .collect::<Vec<i32>>();
    5
        // Allow `v`'s element type to be inferred
    9
        // Allow `v`'s element type to be inferred
    6
        let x: i32 = v[0];
    10
        let x: i32 = v[0];
    7
    }
    11
    }

    Notice that the rewritten code has the correct element type in the call to default, even in cases where the type is not written explicitly in the original expression! The matching of typed! obtains the inferred type information from rustc, and those inferred types are captured by metavariables in the type pattern.

    Example: transmute to <*const T>::as_ref

    This example demonstrates usage of def! and typed!.

    Suppose we have some unsafe code that uses transmute to convert a raw pointer that may be null (*const T) into an optional reference (Option<&T>). This conversion is better expressed using the as_ref method of *const T, and we'd like to apply this transformation automatically.

    Initial attempt

    Here is a basic first attempt:

    rewrite_expr 'transmute(__e)' '__e.as_ref()'
    

    1
    use std::mem;
    1
    use std::mem;
    2
    2
    3
    unsafe fn foo(ptr: *const u32) {
    3
    unsafe fn foo(ptr: *const u32) {
    4
        let r: &u32 = mem::transmute::<*const u32, Option<&u32>>(ptr).unwrap();
    4
        let r: &u32 = mem::transmute::<*const u32, Option<&u32>>(ptr).unwrap();
    5
    5
    6
        let opt_r2: Option<&u32> = mem::transmute(ptr);
    6
        let opt_r2: Option<&u32> = mem::transmute(ptr);
    7
        let r2 = opt_r2.unwrap();
    7
        let r2 = opt_r2.unwrap();
    8
        let ptr2: *const u32 = mem::transmute(r2);
    8
        let ptr2: *const u32 = mem::transmute(r2);
    9
    9
    10
        {
    10
        {
    11
            use std::mem::transmute;
    11
            use std::mem::transmute;
    12
            let opt_r3: Option<&u32> = transmute(ptr);
    12
            let opt_r3: Option<&u32> = ptr.as_ref();
    13
            let r3 = opt_r2.unwrap();
    13
            let r3 = opt_r2.unwrap();
    14
        }
    14
        }
    15
    15
    16
        /* ... */
    16
        /* ... */
    17
    }
    17
    }

    This has two major shortcomings, which we will address in order:

    1. It works only on code that calls exactly transmute(foo). The instances that import std::mem and call mem::transmute(foo) do not get rewritten.
    2. It rewrites transmutes between any types, not just *const T to Option<&T>. Only transmutes between those types should be replaced with as_ref.

    Identifying transmute calls with def!

    We want to rewrite calls to std::mem::transmute, regardless of how those calls are written. This is a perfect use case for def!:

    rewrite_expr 'def!(::std::intrinsics::transmute)(__e)' '__e.as_ref()'
    

    1
    use std::mem;
    1
    use std::mem;
    2
    2
    3
    unsafe fn foo(ptr: *const u32) {
    3
    unsafe fn foo(ptr: *const u32) {
    4
        let r: &u32 = mem::transmute::<*const u32, Option<&u32>>(ptr).unwrap();
    4
        let r: &u32 = ptr.as_ref().unwrap();
    5
    5
    6
        let opt_r2: Option<&u32> = mem::transmute(ptr);
    6
        let opt_r2: Option<&u32> = ptr.as_ref();
    7
        let r2 = opt_r2.unwrap();
    7
        let r2 = opt_r2.unwrap();
    8
        let ptr2: *const u32 = mem::transmute(r2);
    8
        let ptr2: *const u32 = r2.as_ref();
    9
    9
    10
        {
    10
        {
    11
            use std::mem::transmute;
    11
            use std::mem::transmute;
    12
            let opt_r3: Option<&u32> = transmute(ptr);
    12
            let opt_r3: Option<&u32> = ptr.as_ref();
    13
            let r3 = opt_r2.unwrap();
    13
            let r3 = opt_r2.unwrap();
    14
        }
    14
        }
    15
    15
    16
        /* ... */
    16
        /* ... */
    17
    }
    17
    }

    Now our rewrite catches all uses of transmute, whether they're written as transmute(foo), mem::transmute(foo), or even ::std::mem::transmute(foo).

    Notice that we refer to transmute as std::intrinsics::transmute: this is the location of its original definition, which is re-exported in std::mem. See the "def!: debugging match failures" section for an explanation of how we discovered this.

    Filtering transmute calls by type

    We now have a command for rewriting all transmute calls, but we'd like it to rewrite only transmutes from *const T to Option<&T>. We can achieve this by filtering the input and output types with typed!:

    rewrite_expr '
        typed!(
            def!(::std::intrinsics::transmute)(
                typed!(__e, *const __ty)
            ),
            ::std::option::Option<&__ty>
        )
    ' '__e.as_ref()'
    

    1
    use std::mem;
    1
    use std::mem;
    2
    2
    3
    unsafe fn foo(ptr: *const u32) {
    3
    unsafe fn foo(ptr: *const u32) {
    4
        let r: &u32 = mem::transmute::<*const u32, Option<&u32>>(ptr).unwrap();
    4
        let r: &u32 = ptr.as_ref().unwrap();
    5
    5
    6
        let opt_r2: Option<&u32> = mem::transmute(ptr);
    6
        let opt_r2: Option<&u32> = ptr.as_ref();
    7
        let r2 = opt_r2.unwrap();
    7
        let r2 = opt_r2.unwrap();
    8
        let ptr2: *const u32 = mem::transmute(r2);
    8
        let ptr2: *const u32 = mem::transmute(r2);
    9
    9
    10
        {
    10
        {
    11
            use std::mem::transmute;
    11
            use std::mem::transmute;
    12
            let opt_r3: Option<&u32> = transmute(ptr);
    12
            let opt_r3: Option<&u32> = ptr.as_ref();
    13
            let r3 = opt_r2.unwrap();
    13
            let r3 = opt_r2.unwrap();
    14
        }
    14
        }
    15
    15
    16
        /* ... */
    16
        /* ... */
    17
    }
    17
    }

    Now only those transmutes that turn *const T into Option<&T> are affected by the rewrite. And because typed! has access to the results of type inference, this works even on transmute calls that are not fully annotated (transmute(foo), not just transmute::<*const T, Option<&T>>(foo)).

    marked!

    The marked! form is simple: marked!(e, label) matches an expression only if e matches the expression and the expression is marked with the given label. See the documentation on marks and select for more information.

    Other commands

    Several other refactoring commands use the same pattern-matching engine as rewrite_expr:

    • rewrite_ty PAT REPL (docs) works like rewrite_expr, except it matches and replaces type annotations instead of expressions.
    • abstract SIG PAT (docs) replaces expressions matching a pattern with calls to a newly-created function.
    • type_fix_rules (docs) uses type patterns to find the appropriate rule to fix each type error.
    • select's match_expr (docs) and similar filters use syntax patterns to identify nodes to mark.

    Many refactoring commands in c2rust refactor are designed to work only on selected portions of the crate, rather than affecting the entire crate uniformly. To support this, c2rust refactor has a mark system, which allows marking AST nodes (such as functions, expressions, or type annotations) with simple string labels. Certain commands add or remove marks, while others check the existing marks to identify nodes to transform.

    For example, in a program containing several byte string literals, you can use select to mark a specific one:

    select target 'item(B2); desc(expr);'
    

    1
    static B1: &'static [u8] = b"123";
    1
    static B1: &'static [u8] = b"123";
    2
    static B2: &'static [u8] = b"abc";
    2
    static B2: &'static [u8] = b"abc";
    3
    static B3: &'static [u8] = b"!!!";
    3
    static B3: &'static [u8] = b"!!!";

    Then, you can use bytestr_to_str to change only the marked byte string to an ordinary string literal, leaving the others unaffected:

    bytestr_to_str
    

    1
    static B1: &'static [u8] = b"123";
    1
    static B1: &'static [u8] = b"123";
    2
    static B2: &'static [u8] = b"abc";
    2
    static B2: &'static [u8] = "abc";
    3
    static B3: &'static [u8] = b"!!!";
    3
    static B3: &'static [u8] = b"!!!";

    This ability to limit transformations to specific parts of the program is useful for refactoring a large codebase incrementally, on a module-by-module or function-by-function basis.

    The remainder of this tutorial describes select and related mark-manipulation commands. For details of how marks affect various transformation commands, see the command documentation or read about the marked! pattern for rewrite_expr and other pattern-matching commands.

    Marks

    A "mark" is a short string label that is associated with a node in the AST. Marks can be applied to nodes of most kinds, including items, expressions, patterns, type annotations, and so on. The mark string can be any valid Rust identifier, though most commands that process marks use short words such as target, dest, or new. It's possible to apply multiple distinct marks to the same node, and it's also possible to mark children of marked nodes separately from their parents (for example, to mark an expression and one of its subexpressions).

    Here are some examples.

    select target 'crate; desc(match_expr(2 + 2));'
    

    1
    fn f() -> Option<i32> {
    1
    fn f() -> Option<i32> {
    2
        Some(2 + 2)
    2
        Some(2 + 2)
    3
    }
    3
    }
    4
    4
    5
    fn g() -> i32 {
    5
    fn g() -> i32 {
    6
        match f() {
    6
        match f() {
    7
            Some(x) => x,
    7
            Some(x) => x,
    8
            None => 0,
    8
            None => 0,
    9
        }
    9
        }
    10
    }
    10
    }

    The â–¶ ... â—€ indicators in the diff show that the expression 2 + 2 has been marked. Hover over the indicators for more details, such as the label of the added mark.

    As mentioned above, most kinds of nodes can be marked, not only expressions. Here we mark a function, a pattern, and a type annotation:

    select a 'item(f);' ;
    select b 'item(g); desc(match_ty(i32));' ;
    select c 'item(g); desc(match_pat(Some(x)));' ;
    

    1
    fn f() -> Option<i32> {
    1
    fn f() -> Option<i32> {
    2
        Some(2 + 2)
    2
        Some(2 + 2)
    3
    }
    3
    }
    4
    4
    5
    fn g() -> i32 {
    5
    fn g() -> i32 {
    6
        match f() {
    6
        match f() {
    7
            Some(x) => x,
    7
            Some(x) => x,
    8
            None => 0,
    8
            None => 0,
    9
        }
    9
        }
    10
    }
    10
    }

    As mentioned above, it's possible to mark the same node twice with different labels. (Marking it twice with the same label is no different from marking it once.) Here's an example of marking a function multiple times:

    select a 'item(f);' ;
    select a 'item(f);' ;
    select b 'item(f);' ;
    

    1
    fn f() -> Option<i32> {
    1
    fn f() -> Option<i32> {
    2
        Some(2 + 2)
    2
        Some(2 + 2)
    3
    }
    3
    }
    4
    4
    5
    fn g() -> i32 {
    5
    fn g() -> i32 {
    6
        match f() {
    6
        match f() {
    7
            Some(x) => x,
    7
            Some(x) => x,
    8
            None => 0,
    8
            None => 0,
    9
        }
    9
        }
    10
    }
    10
    }

    As you can see by hovering over the indicators, labels a and b were both added to the function f.

    Marks on a node have no connection to marks on its parent or child nodes. We can, for example, mark an expression like 2 + 2, then separately mark its subexpressions with either the same or different labels:

    select a 'item(f); desc(match_expr(2 + 2));' ;
    select a 'item(f); desc(match_expr(2)); first;' ;
    select b 'item(f); desc(match_expr(2)); last;' ;
    

    1
    fn f() -> Option<i32> {
    1
    fn f() -> Option<i32> {
    2
        Some(2 + 2)
    2
        Some(2 + 2)
    3
    }
    3
    }
    4
    4
    5
    fn g() -> i32 {
    5
    fn g() -> i32 {
    6
        match f() {
    6
        match f() {
    7
            Some(x) => x,
    7
            Some(x) => x,
    8
            None => 0,
    8
            None => 0,
    9
        }
    9
        }
    10
    }
    10
    }

    Hovering over the mark indicators shows precisely what has happened: we marked both 2 + 2 and the first 2 with the label a, and marked the second 2 with the label b.

    The select command

    The select command provides a simple scripting language for applying marks to specific nodes. The basic syntax of the command is:

    select LABEL SCRIPT
    

    select runs a SCRIPT (written in the language described below) to obtain a set of AST nodes, then marks every node in the set with LABEL, which should be a single identifier such as target.

    More concretely, when running the script, select maintains a "current selection", which is a set of AST nodes. Script operations (described below) can extend or modify the current selection. At the end of the script, select marks every node in the current selection with LABEL.

    We next describe a few common select script patterns, followed by details on the available operations and filters.

    Common patterns

    Selecting an item by path

    For items such as functions, type declarations, or traits, the item(path) operation selects the item by its path:

    select target 'item(f);' ;
    select target 'item(T);' ;
    select target 'item(S);' ;
    select target 'item(m::g);' ;
    

    1
    fn f() {}
    1
    fn f() {}
    2
    trait T {}
    2
    trait T {}
    3
    struct S {}
    3
    struct S {}
    4
    mod m {
    4
    mod m {
    5
        fn g() {}
    5
        fn g() {}
    6
    }
    6
    }

    Note that this only works for the kinds of items that can be imported via use. It doesn't handle other kinds of item-like nodes, such as impl methods, which cannot be imported directly.

    Selecting all nodes matching a filter

    The operations crate; desc(filter); together select all nodes (or, equivalently, all descendants of the crate) that match a filter. For example, we can select all expressions matching the pattern 2 + 2 using a match_expr filter:

    select target 'crate; desc(match_expr(2 + 2));'
    

    1
    fn f() -> i32 {
    1
    fn f() -> i32 {
    2
        2 + 2
    2
        2 + 2
    3
    }
    3
    }
    4
    4
    5
    const FOUR: i32 = 2 + 2;
    5
    const FOUR: i32 = 2 + 2;
    6
    6
    7
    static ARRAY: [u8; 2 + 2] = [1, 2, 3, 4];
    7
    static ARRAY: [u8; 2 + 2] = [1, 2, 3, 4];

    Here we see that crate; desc(filter); can find matching items anywhere in the crate: inside function bodies, constant declarations, and even inside the length expression of an array type annotation.

    Selecting filtered nodes inside a parent node

    In the previous example, crate; desc(filter); is made up of two separate script operations. crate selects the entire crate:

    select target 'crate;'
    

    1
    fn f() -> i32 {
    1
    fn f() -> i32 {
    2
        2 + 2
    2
        2 + 2
    3
    }
    3
    }
    4
    4
    5
    const FOUR: i32 = 2 + 2;
    5
    const FOUR: i32 = 2 + 2;
    6
    6
    7
    static ARRAY: [u8; 2 + 2] = [1, 2, 3, 4];
    7
    static ARRAY: [u8; 2 + 2] = [1, 2, 3, 4];

    Then desc(filter) looks for descendants of selected nodes that match filter, and replaces the current selection with the nodes it finds:

    clear_marks ;
    select target 'crate; desc(match_expr(2 + 2));'
    

    1
    fn f() -> i32 {
    1
    fn f() -> i32 {
    2
        2 + 2
    2
        2 + 2
    3
    }
    3
    }
    4
    4
    5
    const FOUR: i32 = 2 + 2;
    5
    const FOUR: i32 = 2 + 2;
    6
    6
    7
    static ARRAY: [u8; 2 + 2] = [1, 2, 3, 4];
    7
    static ARRAY: [u8; 2 + 2] = [1, 2, 3, 4];

    (Note: we use clear_marks here only for illustration purposes, to make the diff clearly show the changes between the old and new versions of our select command.)

    Combining desc with operations other than crate allows selecting descendants of only specific nodes. For example, we can find expressions matching 2 + 2, but only within the function f:

    select target 'item(f); desc(match_expr(2 + 2));'
    

    1
    fn f() -> i32 {
    1
    fn f() -> i32 {
    2
        2 + 2
    2
        2 + 2
    3
    }
    3
    }
    4
    4
    5
    const FOUR: i32 = 2 + 2;
    5
    const FOUR: i32 = 2 + 2;
    6
    6
    7
    static ARRAY: [u8; 2 + 2] = [1, 2, 3, 4];
    7
    static ARRAY: [u8; 2 + 2] = [1, 2, 3, 4];

    In a more complex example, we can use multiple desc calls to target an expression inside of a specific method (recall that methods can't be selected directly with item(path)). We first select the module containing the impl:

    select target 'item(m);'
    

    1
    fn f() -> i32 {
    1
    fn f() -> i32 {
    2
        2 + 2
    2
        2 + 2
    3
    }
    3
    }
    4
    4
    5
    mod m {
    5
    mod m {
    6
        struct S;
    6
        struct S;
    7
        impl S {
    7
        impl S {
    8
            fn f(&self) -> i32 {
    8
            fn f(&self) -> i32 {
    9
                2 + 2
    9
                2 + 2
    10
            }
    10
            }
    11
        }
    11
        }
    12
    }
    12
    }

    Then we select the method of interest, using the name filter (described below):

    clear_marks ;
    select target 'item(m); desc(name("f"));'
    

    1
    fn f() -> i32 {
    1
    fn f() -> i32 {
    2
        2 + 2
    2
        2 + 2
    3
    }
    3
    }
    4
    4
    5
    mod m {
    5
    mod m {
    6
        struct S;
    6
        struct S;
    7
        impl S {
    7
        impl S {
    8
            fn f(&self) -> i32 {
    8
            fn f(&self) -> i32 {
    9
                2 + 2
    9
                2 + 2
    10
            }
    10
            }
    11
        }
    11
        }
    12
    }
    12
    }

    And finally, we select the expression inside the method:

    clear_marks ;
    select target 'item(m); desc(name("f")); desc(match_expr(2 + 2));'
    

    1
    fn f() -> i32 {
    1
    fn f() -> i32 {
    2
        2 + 2
    2
        2 + 2
    3
    }
    3
    }
    4
    4
    5
    mod m {
    5
    mod m {
    6
        struct S;
    6
        struct S;
    7
        impl S {
    7
        impl S {
    8
            fn f(&self) -> i32 {
    8
            fn f(&self) -> i32 {
    9
                2 + 2
    9
                2 + 2
    10
            }
    10
            }
    11
        }
    11
        }
    12
    }
    12
    }

    Combined with some additional filters described below, this approach is quite effective for marking nodes that can't be named with an ordinary import path, such as impl methods or items nested inside functions.

    Script operations

    A select script can consist of any number of operations, which will be run in order to completion. (There is no control flow in select scripts.) Each operation ends with a semicolon, much like Rust statements.

    The remainder of this section documents each script operation.

    crate

    crate (which takes no arguments) adds the root node of the entire crate to the current selection. All functions, modules, and other declarations are descendants of this single root node.

    Example:

    select target 'crate;'
    

    1
    fn f() -> i32 {
    1
    fn f() -> i32 {
    2
        123
    2
        123
    3
    }
    3
    }
    4
    mod m {
    4
    mod m {
    5
        static S: i32 = 0;
    5
        static S: i32 = 0;
    6
    }
    6
    }

    item

    item(p) adds the item identified by the path p to the current selection. The provided path is handled like in Rust's use declarations (except that only plain paths are supported, not wildcards or curly-braced blocks).

    select target 'item(m::S);'
    

    1
    fn f() -> i32 {
    1
    fn f() -> i32 {
    2
        123
    2
        123
    3
    }
    3
    }
    4
    mod m {
    4
    mod m {
    5
        static S: i32 = 0;
    5
        static S: i32 = 0;
    6
    }
    6
    }

    Because the item operation only adds to the current selection (as opposed to replacing the current selection with a set containing only the identified item), we can run item multiple times to select several different items at once:

    select target 'item(f); item(m::S); item(m);'
    

    1
    fn f() -> i32 {
    1
    fn f() -> i32 {
    2
        123
    2
        123
    3
    }
    3
    }
    4
    mod m {
    4
    mod m {
    5
        static S: i32 = 0;
    5
        static S: i32 = 0;
    6
    }
    6
    }

    child

    child(f) checks each child of each currently selected node against the filter f, and replaces the current selection with the set of matching children.

    This can be used, for example, to select a static's type annotation without selecting type annotations that appear inside its initializer:

    select target 'item(S); child(kind(ty));'
    

    1
    static S: i32 = 123_u8 as i32;
    1
    static S: i32 = 123_u8 as i32;
    2
    const C: u32 = 0;
    2
    const C: u32 = 0;

    To illustrate how this works, here is the AST for the static S item:

    • item static S
      • identifier S (the name of the static)
      • type i32 (the type annotation of the static)
      • expression 123_u8 as i32 (the initializer of the static)
        • expression 123_u8 (the input of the cast expression)
        • type i32 (the target type of the cast expression)

    The static's type annotation is a direct child of the static (and has kind ty, matching the kind(ty) filter), so the type annotation is selected by the example command above. The target type for the cast is not a direct child of the static - rather, it's a child of the initializer expression, which is a child of the static - so it is ignored.

    desc

    desc(f) ("descendant") checks each descendant of each currently selected node against the filter f, and replaces the current selection with the set of matching descendants. This is similar to child, but checks for matching descendants at any depth, not only matching direct children.

    Using the same example as for child, we see that desc selects more nodes:

    select target 'item(S); desc(kind(ty));'
    

    1
    static S: i32 = 123_u8 as i32;
    1
    static S: i32 = 123_u8 as i32;
    2
    const C: u32 = 0;
    2
    const C: u32 = 0;

    Specifically, it selects both the type annotation of the static and the target type of the cast expression, as both are descendants of the static (though at different depths). Of course, it still does not select the type annotation of the const C, which is not a descendant of static S at any depth.

    Note that desc only considers the strict descendants of marked nodes - that is, it does not consider a node to be a "depth-zero" descendant of itself. So, for example, the following command selects nothing:

    select target 'item(S); desc(item_kind(static));'
    

    1
    static S: i32 = 123_u8 as i32;
    1
    static S: i32 = 123_u8 as i32;
    2
    const C: u32 = 0;
    2
    const C: u32 = 0;

    S itself is a static, but contains no additional statics inside of it, and desc does not consider S itself when looking for item_kind(static) descendants.

    filter

    filter(f) checks each currently selected node against the filter f, and replaces the current selection with the set of matching nodes. Equivalently, filter(f) removes from the current selection any nodes that don't match f.

    Most uses of the filter operation can be replaced by passing a more appropriate filter expression to desc or child, so the examples in this section are somewhat contrived. (filter can still be useful in combination with marked, described below, or in more complex select scripts.)

    Here is a slightly roundabout way to select all items named f. First, we select all items:

    select target 'crate; desc(kind(item));'
    

    1
    fn f() {}
    1
    fn f() {}
    2
    fn g() {}
    2
    fn g() {}
    3
    3
    4
    mod m {
    4
    mod m {
    5
        fn f() {}
    5
        fn f() {}
    6
    }
    6
    }

    Then, we use filter to keep only items named f:

    clear_marks ;
    select target 'crate; desc(kind(item)); filter(name("f"));'
    

    1
    fn f() {}
    1
    fn f() {}
    2
    fn g() {}
    2
    fn g() {}
    3
    3
    4
    mod m {
    4
    mod m {
    5
        fn f() {}
    5
        fn f() {}
    6
    }
    6
    }

    With this command, only descendants of crate matching both filters kind(item) and name("f") are selected. (This could be written more simply as crate; desc(kind(item) && name("f"));.)

    first and last

    first replaces the current selection with a set containing only the first selected node. last does the same with the last selected node. "First" and "last" are determined by a postorder traversal of the AST, so sibling nodes are ordered as expected, and a parent node come "after" all of its children.

    The first and last operations are most useful for finding places to insert new nodes (such as with the create_item command) while ignoring details such as the specific names or kinds of the nodes around the insertion point. For example, we can use last to easily select the last item in a module. First, we select all the module's items:

    select target 'item(m); child(kind(item));'
    

    1
    mod m {
    1
    mod m {
    2
        fn f() {}
    2
        fn f() {}
    3
        static S: i32 = 0;
    3
        static S: i32 = 0;
    4
        const C: i32 = 1;
    4
        const C: i32 = 1;
    5
    }
    5
    }

    Then we use last to select only the last such child:

    clear_marks ;
    select target 'item(m); child(kind(item)); last;'
    

    1
    mod m {
    1
    mod m {
    2
        fn f() {}
    2
        fn f() {}
    3
        static S: i32 = 0;
    3
        static S: i32 = 0;
    4
        const C: i32 = 1;
    4
        const C: i32 = 1;
    5
    }
    5
    }

    Now we could use create_item to insert a new item after the last existing one.

    marked

    marked(l) adds all nodes marked with label l to the current selection. This is useful for more complex marking operations, since (together with the delete_marks command) it allows using temporary marks to manipulate multiple sets of nodes simultaneously.

    For example, suppose we wish to select both the first and the last item in a module. Normally, this would require duplicating the select command, since both first and last replace the entire current selection with the single first or last item. This would be undesirable if the operations for setting up the initial set of items were fairly complex. But with marked, we can save the selection before running first and restore it afterward.

    We begin by selecting all items in the module and saving that selection by marking it with the tmp_all_items label:

    select tmp_all_items 'item(m); child(kind(item));'
    

    1
    mod m {
    1
    mod m {
    2
        fn f() {}
    2
        fn f() {}
    3
        static S: i32 = 0;
    3
        static S: i32 = 0;
    4
        const C: i32 = 1;
    4
        const C: i32 = 1;
    5
    }
    5
    }

    Next, we use marked to retrieve the tmp_all_items set and take the first item from it. This reduces the current selection to only a single item, but the tmp_all_items marks remain intact for later use.

    select target 'marked(tmp_all_items); first;'
    

    1
    mod m {
    1
    mod m {
    2
        fn f() {}
    2
        fn f() {}
    3
        static S: i32 = 0;
    3
        static S: i32 = 0;
    4
        const C: i32 = 1;
    4
        const C: i32 = 1;
    5
    }
    5
    }

    We do the same to mark the last item with target:

    select target 'marked(tmp_all_items); last;'
    

    1
    mod m {
    1
    mod m {
    2
        fn f() {}
    2
        fn f() {}
    3
        static S: i32 = 0;
    3
        static S: i32 = 0;
    4
        const C: i32 = 1;
    4
        const C: i32 = 1;
    5
    }
    5
    }

    Finally, we clean up, removing the tmp_all_items marks using the delete_marks command:

    delete_marks tmp_all_items
    

    1
    mod m {
    1
    mod m {
    2
        fn f() {}
    2
        fn f() {}
    3
        static S: i32 = 0;
    3
        static S: i32 = 0;
    4
        const C: i32 = 1;
    4
        const C: i32 = 1;
    5
    }
    5
    }

    Now the only marks remaining are the target marks on the first and last items of the module, as we originally intended.

    reset

    reset clears the set of marked nodes. This is only useful in combination with mark and unmark, as otherwise the operations before a reset have no effect.

    mark and unmark

    These operations allow select scripts to manipulate marks directly, rather than relying solely on the automatic marking of selected nodes at the end of the script. mark(l) marks all nodes in the current selection with label l (immediately, rather than waiting until the select command is finished), and unmark(l) removes label l from all selected nodes.

    mark, unmark, and reset can be used to effectively combine multiple select commands in a single script. Here's the "first and last" example from the marked section, using only a single select command:

    select _dummy '
        item(m); child(kind(item)); mark(tmp_all_items); reset;
        marked(tmp_all_items); first; mark(target); reset;
        marked(tmp_all_items); last; mark(target); reset;
        marked(tmp_all_items); unmark(tmp_all_items); reset;
    '
    

    1
    mod m {
    1
    mod m {
    2
        fn f() {}
    2
        fn f() {}
    3
        static S: i32 = 0;
    3
        static S: i32 = 0;
    4
        const C: i32 = 1;
    4
        const C: i32 = 1;
    5
    }
    5
    }

    Note that we pass _dummy as the LABEL argument of select, since the desired target marks are applied using the mark operation, rather than relying on the implicit marking done by select.

    unmark is also useful in combination with marked to interface with non-select mark manipulation commands. For example, suppose we want to mark all occurrences of 2 + 2 that are passed as arguments to a function f. One option is to do this using the mark_arg_uses command, with additional processing by select before and after. Here we start by marking the function f:

    select target 'item(f);'
    

    1
    fn f(x: i32) {
    1
    fn f(x: i32) {
    2
        // ...
    2
        // ...
    3
    }
    3
    }
    4
    4
    5
    fn g(x: i32) {
    5
    fn g(x: i32) {
    6
        // ...
    6
        // ...
    7
    }
    7
    }
    8
    8
    9
    fn main() {
    9
    fn main() {
    10
        f(1);
    10
        f(1);
    11
        f(2 + 2);
    11
        f(2 + 2);
    12
        g(2 + 2);
    12
        g(2 + 2);
    13
        let x = 2 + 2;
    13
        let x = 2 + 2;
    14
    }
    14
    }

    Next, we run mark_arg_uses to replace the mark on f with a mark on each argument expression passed to f:

    mark_arg_uses 0 target
    

    1
    fn f(x: i32) {
    1
    fn f(x: i32) {
    2
        // ...
    2
        // ...
    3
    }
    3
    }
    4
    4
    5
    fn g(x: i32) {
    5
    fn g(x: i32) {
    6
        // ...
    6
        // ...
    7
    }
    7
    }
    8
    8
    9
    fn main() {
    9
    fn main() {
    10
        f(1);
    10
        f(1);
    11
        f(2 + 2);
    11
        f(2 + 2);
    12
        g(2 + 2);
    12
        g(2 + 2);
    13
        let x = 2 + 2;
    13
        let x = 2 + 2;
    14
    }
    14
    }

    And finally, we use select again to mark only those arguments that match 2 + 2:

    select target 'marked(target); unmark(target); filter(match_expr(2 + 2));'
    

    1
    fn f(x: i32) {
    1
    fn f(x: i32) {
    2
        // ...
    2
        // ...
    3
    }
    3
    }
    4
    4
    5
    fn g(x: i32) {
    5
    fn g(x: i32) {
    6
        // ...
    6
        // ...
    7
    }
    7
    }
    8
    8
    9
    fn main() {
    9
    fn main() {
    10
        f(1);
    10
        f(1);
    11
        f(2 + 2);
    11
        f(2 + 2);
    12
        g(2 + 2);
    12
        g(2 + 2);
    13
        let x = 2 + 2;
    13
        let x = 2 + 2;
    14
    }
    14
    }

    Beginning the script with marked(target); unmark(target); copies the set of target-marked nodes into the current selection, then removes the existing marks. The remainder of the script can then operate as usual, manipulating only the current selection with no need to worry about additional marks being already present.

    Filters

    Boolean operators

    Filter expressions can be combined using the boolean operators &&, ||, and !. A node matches the filter f1 && f2 only if it matches f1 and also matches f2, and so on.

    kind

    kind(k) matches AST nodes whose node kind is k. The supported node kinds are:

    • item - a top-level item, as in struct Foo { ... } or fn foo() { ... }. Includes both items in modules and items defined inside functions or other blocks, but does not include "item-like" nodes inside traits, impls, or extern blocks.
    • trait_item - an item inside a trait definition, such as a method or associated type declaration
    • impl_item - an item inside an impl block, such as a method or associated type definition
    • foreign_item - an item inside an extern block ("foreign module"), such as a C function or static declaration
    • stmt
    • expr
    • pat - a pattern, including single-ident patterns like foo in let foo = ...;
    • ty - a type annotation, such as Foo in let x: Foo = ...;
    • arg - a function or method argument declaration
    • field - a struct, enum variant, or union field declaration
    • itemlike - matches nodes whose kind is any of item, trait_item, impl_item, or foreign_item
    • any - matches any node

    The node kind k can be used alone as shorthand for kind(k). For example, the operation desc(item); is the same as desc(kind(item));.

    item_kind

    item_kind(k) matches itemlike AST nodes whose subkind is k. The itemlike subkinds are:

    • extern_crate
    • use
    • static
    • const
    • fn
    • mod
    • foreign_mod
    • global_asm
    • ty - type alias definition, as in type Foo = Bar;
    • existential - existential type definition, as in existential type Foo: Bar;. Note that existential types are currently an unstable language feature.
    • enum
    • struct
    • union
    • trait - ordinary trait Foo { ... } definition, including unsafe trait
    • trait_alias - trait alias definition, as in trait Foo = Bar; Note that trait aliases are currently an unstable language feature.
    • impl - including both trait and inherent impls
    • mac - macro invocation. Note that select works on the macro-expanded AST, so macro invocations are never present under normal circumstances.
    • macro_def - 2.0/decl_macro-style macro definition, as in macro foo(...) { ... }. Note that 2.0-style macro definitions are currently an unstable language feature.

    Note that a single item_kind filter can match multiple distinct node kinds, as long as the subkind is correct. for example, item_kind(fn) will match fn items, method trait_items and impl_items, and fn declarations inside extern blocks (foreign_items). similarly, item_kind(ty) matches ordinary type alias definitions, associated type declarations (in traits) and definitions (in impls), and foreign type declarations inside extern blocks.

    item_kind filters match only those nodes that also match kind(itemlike), as other node kinds have no itemlike subkind.

    The itemlike subkind k can be used alone as shorthand for item_kind(k). For example, the operation desc(fn); is the same as desc(item_kind(fn));.

    pub and mut

    pub matches any item, impl item, or foreign item whose visibility is pub. It currently does not support struct fields, even though they can also be declared pub.

    mut matches static mut items, static mut foreign item declarations, and mutable binding patterns such as the mut foo in let mut foo = ...;.

    name

    name(re) matches itemlikes, arguments, and fields whose name matches the regular expression re. For example, name("[fF].*") matches fn f() { ... } and struct Foo { ... }, but not trait Bar { ... }. It currently does not support general binding patterns, aside from those in function arguments.

    path and path_prefix

    path(p) matches itemlikes and enum variants whose absolute path is p.

    path_prefix(n, p) is similar to path(p), but drops the last n segments of the node's path before comparing to p.

    has_attr

    has_attr(a) matches itemlikes, exprs, and field declarations that have an attribute named a.

    match_*

    match_expr(e) uses rewrite_expr-style AST matching to compare exprs to e, and matches any node where AST matching succeeds. For example, match_expr(__e + 1) matches the expressions 1 + 1, x + 1, and f() + 1, but not 2 + 2.

    match_pat, match_ty, and match_stmt are similar, but operate on pat, ty, and stmt nodes respectively.

    marked

    marked(l) matches nodes that are marked with the label l.

    any_child, all_child, any_desc, and all_desc

    any_child(f) matches nodes that have a child that matches f. all_child(f) matches nodes where all children of the node match f.

    any_desc and all_desc are similar, but consider all descendants instead of only direct children.

    Other commands

    In addition to select, c2rust refactor contains a number of other mark-manipulation commands. A few of these can be replicated with appropriate select scripts (though using the command is typically easier), but some are more complex.

    copy_marks

    copy_marks OLD NEW adds a mark with label NEW to every node currently marked with OLD.

    delete_marks

    delete_marks OLD removes the label OLD from every node that is currently marked with it.

    rename_marks

    rename_marks OLD NEW behaves like copy_marks OLD NEW followed by delete_marks OLD: it adds a mark with label NEW to every node marked with OLD, then removes OLD from each such node.

    mark_uses

    mark_uses LABEL transfers LABEL marks from definitions to uses. That is, it finds each definition marked with LABEL, marks each use of such a definition with LABEL, then removes LABEL from the definitions. For example, if a static FOO: ... = ... is marked with target, then mark_uses target will add a target mark to every expression FOO that references the marked definition and then remove target from FOO itself.

    For the purposes of this command, a "use" of a definition is a path or identifier that resolves to that definition. This includes expressions (both paths and struct literals), patterns (paths to constants, structs, and enum variants), and type annotations. When a function definition is marked, only the function path itself (the foo::bar in foo::bar(x)) is considered a use, not the entire call expression. Method calls (whether using dotted or UFCS syntax) normally can't be handled at all, as their resolution is "type-dependent" (however, the mark_callers command can sometimes work when mark_uses does not).

    mark_callers

    mark_callers LABEL transfers LABEL marks from function or method definitions to uses. That is, it works like mark_uses, but is specialized to functions and methods. mark_callers uses more a more sophisticated means of name resolution that allows it to detect uses via type-dependent method paths, which mark_uses cannot handle.

    For purposes of mark_callers, a "use" is a function call (foo::bar()) or method call (x.foo()) expression where the function or method being called is one of the marked definitons.

    mark_arg_uses

    mark_arg_uses INDEX LABEL transfers LABEL marks from function or method definitions to the argument in position INDEX at each use. That is, it works like mark_callers, but marks the expression passed as argument INDEX instead of the entire call site.

    INDEX is zero-based. However, the self/receiver argument of a method call counts as the first argument (index 0), with the first argument in parentheses having index 1 (arg0.f(arg1, arg2)). For ordinary function calls (including UFCS method calls), the first argument has index 0 (f(arg0, arg1, arg2))

    The analysis::ownership module implements a pointer analysis for inferring ownership information in code using raw pointers. The goal is to take code that has been automatically translated from C, and thus uses only raw pointers, and infer which of those raw pointers should be changed to safe &, &mut, or Box pointers. Pointers can appear in a number of places in the input program, but this analysis focuses mainly on function signatures and struct field types.

    Design

    The goal of the analysis is to assign to each raw pointer type constructor a permission value, one of READ, WRITE, and MOVE, corresponding to the Rust pointer types &, &mut, and Box. These permissions form a trivial lattice, where READ < WRITE < MOVE. The READ permission indicates that the pointed-to data may be read, the WRITE permission indicates that the pointed-to data may be modified, and the MOVE permission indicates that the pointed-to data may be "moved", or consumed in a linear-typed fashion. The MOVE permission also includes the ability to free the pointed-to data, which amouns to "moving to nowhere".

    Here is a simple example to illustrate the major features of the analysis:

    struct Array {
        data: *mut i32,
    }
    
    unsafe fn new_array(len: usize) -> *mut Array {
        let data = malloc(size_of::<i32>() * len);
        let arr = malloc(size_of::<Array>());
        (*arr).data = data;
        array
    }
    
    unsafe fn delete_array(arr: *mut Array) {
        free((*arr).data);
        free(arr);
    }
    
    unsafe fn element_ptr(arr: *mut Array, idx: usize) -> *mut i32 {
        (*arr).data.offset(idx)
    }
    
    unsafe fn get(arr: *mut Array, idx: usize) -> i32 {
        let elt: *mut i32 = element_ptr(arr, idx);
        *elt
    }
    
    unsafe fn set(arr: *mut Array, idx: usize, val: i32) {
        let elt: *mut i32 = element_ptr(arr, idx);
        *elt = val;
    }
    

    The analysis infers pointer permissions by observing how pointers are used, and applying the rules of the Rust reference model. For instance, the set function's elt pointer must have permission WRITE (or higher), because there is a write to the pointed-to data. Similarly, delete_array's first call to free requires that the pointer in the Array::data field must have permission MOVE. Furthermore, the first free also requires arr to have permission MOVE, because consuming the pointer (*arr).data constitutes a move out of *arr. (In general, the pointer permission sets an upper bound on the permissions of all pointers within the pointed-to data. For example, if arr has permission READ, then *(*arr).data can only be read, not written or moved.)

    The element_ptr function presents an interesting case for analysis, because it is used polymorphically: in get, we would like element_ptr to take a READ *mut Array and return a READ *mut i32, whereas in set we would like the same function to take and return WRITE pointers. In strictly const-correct C code, get and set would respectively call separate const and non-const variants of element_ptr, but a great deal of C code is not const-correct.

    This analysis handles functions like element_ptr by allowing inferred function signatures to be permission polymorphic. Signatures may include permission parameters, which can be instantiated separately at each call site, subject to a set of constraints. For example, here is the inferred polymorphic signature of element_ptr, with permission annotations written in comments (since there is no Rust syntax for them):

    fn element_ptr /* <s0, s1> */ (arr: /* s0 */ *mut Array,
                                   idx: usize)
                                   -> /* s1 */ *mut i32
        /* where s1 <= s0 */;
    

    The function has two permission parameters, s0 and s1, which are the permissions of the argument and return pointers respectively. The signature includes the constraint s1 <= s0, indicating that the output pointer's permission is no higher than that of the input pointer. The function is called in get with permission arguments s0 = s1 = READ and in set with s0 = s1 = WRITE.

    Rust does not support any analogue of the permission polymorphism used in this analysis. To make the results useful in actual Rust code, the analysis includes a monomorphization step, which chooses a set of concrete instantiations for each polymorphic function, and selects an instantiation to use for each call site. In the example above, element_ptr would have both READ, READ and WRITE, WRITE instantiations, with the first being used for the callsite in get and the second at the callsite in set.

    Implementation

    The analysis first computes a polymorphic signature for each function, then monomorphizes to produce functions that can be handled by Rust's type system.

    Both parts of the analysis operate on constraint sets, which contain constraints of the form p1 <= p2. The permissions p1, p2 can be concrete permissions (READ, WRITE, MOVE), permission variables, or expressions of the form min(p1, p2) denoting the less-permissive of two permission values.

    Permission variables appear on pointer type constructors in the types of static variables and struct fields ("static" variables), in the types within function signatures ("sig"), in the types of temporaries and local variables ("local"), and at callsites for instantiating a permission polymorphic function ("inst"). Variables are marked with their origin, as variable from different locations are handled in different phases of the analysis.

    The overall goal of the analysis is to produce assignments to static and sig variables that satisfy all the relevant constraints (or multiple assignments, when monomorphizing polymorphic functions).

    Polymorphic signatures

    The permission variables of each function's polymorphic signature are easily determined: for simplicity, the analysis introduces one variable for each occurrence of a pointer type constructor in the function signature. Cases that might otherwise involve a single variable appearing at multiple locations in the signature are instead handled by adding constraints between the variables. The main task of the first part of the analysis is to compute the constraints over the signature variables of each function. This part of the analysis must also build an assignment of permission values to all static vars, which are not involved in any sort of polymorphism.

    Constraints arise mainly at assignments and function call expressions.

    At assignments, the main constraint is that, if the assigned value has a pointer type, the permission on the LHS pointer type must be no greater than the permission on the RHS pointer type (lhs <= rhs). In other words, an assignment of a pointer may downgrade the permission value of that pointer, but may never upgrade it. In non-pointer types, and in the pointed-to type of an outermost pointer type, all permission values occurring in the two types must be equal (lhs <= rhs and rhs <= lhs).

    Assignments also introduce two additional constraints, both relating to path permissions. The path permission for an expression is the minimum of the permission values on all pointers dereferenced in the expression. For example, in *(*x).f, the path permission is the minimum of the permission on the local variable x and the permission on the struct field f. The calculation of path permissions reflects the transitive nature of access restrictions in Rust: for example, if a struct field x.f has type &mut T, but x is an immutable reference (&S), then only immutable access is allowed to *x.f.

    The two additional constraints introduced by assigments are (1) the path permission of the LHS must be no lower than WRITE, and (2) the path permission of the RHS must be no lower than the permission of the LHS pointer type. Constraint (1) prevents writing through a READ pointer, or through any path containing a READ pointer. Constraint (2) prevents assigning a WRITE pointer accessed through a READ path (or a MOVE pointer accessed through a WRITE or READ path) to a WRITE pointer variable, which would allow bypassing the READ restriction.

    Function calls require additional work. At each call site, the analysis copies in the callee's constraints, substituting a fresh "instantiation" ("inst") variable for each variable in the callee's signature. It then links the new inst variables to the surrounding locals by processing a "pseudo-assignment" from each argument expression to the corresponding formal parameter type in the substituted signature, and from the return type to the lvalue expression where the result is to be stored. The effect is to allow the analysis to "reason through" the function call, relating the (local) return value to the caller's argument expressions. Copying the constraints instead of relying on a concrete instantiation permits precise reasoning about polymorphic functions that call other polymorphic functions.

    The final step for each function is to simplify the constraint set by eliminating "local", "inst", and "static" permission variables. Local variables have no connection to types outside the current function, and can be simplified away without consequence. Eliminating static and instantiation variables requires fixed-point iteration, which is described below. The result of the simplification is a set of constraints over only the function's sig variables, which is suitable for use as the constraint portion of the function signature.

    Since each function's signature depends on the signatures of its callees, and functions may be recursive, a fixed-point iteration step is required to compute the final constraint set for each function. To simplify the implementation, the polymorphic signature construction part of the analysis is split into two phases. The intraprocedural phase visits every function once and generates constraints for that function, but doesn't copy in constraints from callees, which may not have been processed yet. This phase records details of each call site for later use. The intraprocedural phase eliminates local variables at the end of each function, but it does not have enough information to safely eliminate static and inst variables. The interprocedural phase updates each function in turn, substituting in callees' sig constraints and simplifying away static and inst variables to produce a new, more accurate set of sig constraints for the current function, and iterates until it reaches a fixed point. The interprocedural phase also computes an assignment of concrete permission values to static variables, during the process of removing static variables from functions' constraint sets.

    Monomorphization

    The first part of the analysis infers a permission polymorphic signature for each function, but Rust does not support this form of polymorphism. To make the analysis results applicable to actual Rust code, the analysis must provide enough information to allow monomorphizing functions - that is, producing multiple copies of each function with different concrete instantiations of the permission variables.

    Monomorphization begins by collecting all "useful" monomorphic signatures for each function. The analysis identifies all signature variables that appear in output positions (in the return type, or behind a pointer whose permission value is always at least WRITE), then enumerates all assignments to those output variables that are allowed by the function's constraints. For each combination of outputs, it finds the least-restrictive valid assignment of permissions to the remaining (input) variables. For example, given this function:

    fn element_ptr /* <s0, s1> */ (arr: /* s0 */ *mut Array,
                                   idx: usize)
                                   -> /* s1 */ *mut i32
        /* where s1 <= s0 */;
    

    The only output variable is s1, which appears in the return type. The monomorphization step will try each assignment to s1 that is allowed by the constraints. Since the only constraint is s1 <= s0, READ, WRITE, and MOVE are all valid. For each of these, it finds the least restrictive assignment to s0 that is compatible with the assignment to s0. For example, when s1 = MOVE, only s0 = MOVE is valid, so the analysis records MOVE, MOVE as a monomorphization for the element_ptr function. When s1 = WRITE, both s0 = MOVE and s0 = WRITE satisfy the constraints, but s0 = WRITE is less restrictive - it allows calling the function with both MOVE and WRITE pointers, while setting s0 = MOVE allows only MOVE pointers. So the analysis records arguments WRITE, WRITE as another monomorphization, and by similar logic records READ, READ as the final one.

    The next step of monomorphization is to select a monomorphic variant to call at each callsite of each monomorphized function. Given a pair of functions:

    fn f /* <s0, s1> */ (arr: /* s0 */ *mut Array) -> /* s1 */ *mut i32
            /* where s1 <= s0 */ {
        g(arr)
    }
    
    fn g /* <s0, s1> */ (arr: /* s0 */ *mut Array) -> /* s1 */ *mut i32
            /* where s1 <= s0 */ {
        ...
    }
    

    For pointer permissions to line up properly, a monomorphic variant of f specialized to READ, READ will need to call a variant of g also specialized to READ, READ, and a variant of f specialized to WRITE, WRITE will need to call a WRITE, WRITE variant of g.

    To infer this information, the analysis separately considers each monomorphic signature of each function. It performs a backtracking search to select, for each callsite in the function, a monomorphic signature of the callee, such that all of the calling function's constraints are satisfied, including constraints setting the caller's sig variables equal to the concrete permissions in the monomorphic signature. The table of callee monomorphization selections is included in the analysis results so that callsites can be updated appropriately when splitting functions for monomorphization.

    Annotations

    The ownership analysis supports annotations to specify the permission types of functions and struct fields. These annotations serve two purposes. First, the user can annotate functions to provide custom signatures for functions on which the analysis produces inaccurate results. Signatures provided this way will be propagated throughout the analysis, so manually correcting a single wrongly-inferred function can fix the inference results for its callers as well. Second, the ownership system provides an ownership_annotate command that adds annotations to functions reflecting their inferred signatures. The user can then read the generated annotations to check the analysis results, and optionally edit them to improve precision, before proceeding with further code transformations.

    There are four annotation types currently supported by the ownership system.

    • #[ownership_static(<perms>)] provides concrete permission values for all pointer types in a static declaration or struct field. The perms argument is a comma-separated sequence of concrete permission tokens (READ, WRITE, MOVE). The given permission values will be applied to the pointers in the static or field type, following a preorder traversal of the type. For example:

      struct S {
          #[ownership_static(READ, WRITE, MOVE)]
          f: *mut (*mut u8, *mut u16)
      }
      

      Here the outermost pointer will be given permission READ, the pointer to u8 will be given permission WRITE, and the pointer to u16 will be given permission MOVE.

    • #[ownership_constraints(<constraints>) provides the signature constraints for the annotated function, overriding polymorphic signature inference. The argument constraints is a comma-separated sequence of constraints of the form le(<perm1>, <perm2>), each representing a single constraint perm1 <= perm2. The permissions used in each constraint may be any combination of concrete permissions (READ, WRITE, MOVE), permission variables (_0, _1, ...), or expressions of the form min(p1, p2, ...). (The permission syntax is limited by the requirement for compatibility with Rust's attribute syntax.)

      The permission variables used in constraints always refer to signature variables of the annotated function. A signature variable is introduced for each pointer type constructor in the function's signature, and they are numbered according to a preorder traversal of each node in the argument and return types of the function. This example shows location of each variable in a simple signature:

      fn get_err(arr: /* _0 */ *mut Array,
                 element_out: /* _1 */ *mut /* _2 */ *mut i32)
                 -> /* _3 */ *const c_char;
      
    • #[ownership_mono(<suffix>, <perms>)] supplies a monomorphic signature to be used for the annotated function. The suffix argument is a quoted string, which (if non-empty) will be used when splitting polymorphic functions into monomorphic variants to construct a name for the monomorphized copy of the function. The perms argument is a comma-separated list of concrete permission tokens, giving the permissions to be used in the function signature in this monomorphization.

      The ownership_mono annotation can appear multiple times on a single function to provide multiple monomorphic signatures. However, if it appears at all, monomorphization inference will be completely overriden for the annotated function, and only the provided signatures will be used in callee argument inference and later transformations.

      Example:

      #[ownership_mono("mut", WRITE, WRITE)]
      #[ownership_mono("", READ, READ)]
      fn first(arr: *mut Array) -> *mut i32;
      

      This function will have two monomorphic variants, one where both pointers' permission values are WRITE and one where both are READ. When the ownership_split_variants command splits the function into its monomorphic variants, the WRITE variant will be named first_mut and the READ variant will keep the original name first.

    • #[ownership_variant_of(<name>)] is used to combine source-level functions into variant groups. See the section on variant groups for details.

    Variant Groups

    The "variant group" mechanism allows combining several source-level functions into a single logical function for purposes of the analysis. This is useful for combining a function that was previously split into monomorphic variants back into a single logical function. This allows for a sort of "modular refactoring", in which the user focuses on one module at a time, analyzing, annotating, and splitting variants in only that module before moving on to another.

    As a concrete example of the purpose of this feature, consider the following code:

    fn f(arr: *mut Array) -> *mut i32 { ... g(arr) ... }
    
    fn g(arr: *mut Array) -> *mut i32 { ... }
    

    The user works first on (the module containing) g, resulting in splitting g into two variants:

    fn f(arr: *mut Array) -> *mut i32 { ... g_mut(arr) ... }
    
    fn g(arr: *mut Array) -> *mut i32 { ... }
    fn g_mut(arr: *mut Array) -> *mut i32 { ... }
    

    Note that, because there is still only one variant of f, the transformation must choose a single g variant for f to call. In this case, it chose the g_mut variant.

    Later, the user works on f. If g and g_mut are treated as separate functions, then there are two possibilities. First, if the constraints on g_mut are set up (or inferred) to require WRITE permission for arr, then only a WRITE variant of f will be generated. Or second, if the constraints are relaxed, then f may get both READ and WRITE variants, but both will (wrongly) call g_mut.

    Treating g and g_mut as two variants of a single function allows the analysis to switch between g variants in the different variants of f, resulting in correct code like the following:

    fn f(arr: *mut Array) -> *mut i32 { ... g(arr) ... }
    fn f_mut(arr: *mut Array) -> *mut i32 { ... g_mut(arr) ... }
    
    fn g(arr: *mut Array) -> *mut i32 { ... }
    fn g_mut(arr: *mut Array) -> *mut i32 { ... }
    

    The ownership_split_variants automatically annotates the split functions so they will be combined into a variant group during further analysis. Variant groups can also be constructed manually using the #[ownership_variant_of(<name>)] annotation, where name is an arbitrary quoted string. All source-level functions bearing an ownership_variant_of annotation with the same name will form a single variant group, which will be treated as a single function throughout the analysis. However, signature inference for the variants themselves is not well supported. Thus, each variant must have an ownership_mono annotation, and exactly one function in each variant group must also have an ownership_constraints annotation. Together, these provide enough information that inference is not required. Note that unlike non-variant functions, variants may not have multiple ownership_mono annotations, as each variant is expected to correspond to a single monomorphization of the original function.

    The "Collection Hack"

    The analysis as described so far tries to mimic the Rust ownership model as implemented in the Rust compiler. However, collection data structures in Rust often use unsafe code to bypass parts of the ownership model. A particularly common case is in removal methods, such as Vec::pop:

    impl<T> Vec<T> {
        fn pop(&mut self) -> Option<T> { ... }
    }
    

    This method moves a T out of self's internal storage, but only takes self by mutable reference. Under the "normal" rules, this is impossible, and the analysis described above will infer a stricter signature for the raw pointer equivalent:

    fn pop(this: /* MOVE */ *mut Vec) -> /* MOVE */ *mut c_void { ... }
    

    The analysis as implemented includes a small adjustment (the "collection hack") to let it infer the correct signature for such methods.

    The collection hack is this: when handling a pointer assignment, instead of constraining the path permission of the RHS to be at least the permission of the LHS, we constraint it to be at least min(lhs_perm, WRITE). The result is that it becomes possible to move a MOVE pointer out of a struct when only WRITE permission is available for the pointer to that struct. Then the analysis will infer the correct type for pop:

    fn pop(this: /* WRITE */ *mut Vec) -> /* MOVE */ *mut c_void { ... }
    

    This is the top-level directory for all cross-checking components, and contains the following:

    • A clang plugin that automatically inserts cross-check instrumentation into C code.

    • An equivalent rustc compiler plugin for Rust.

    • The libfakechecks cross-checking backend library that prints out all cross-checks to standard output. This library is supported by both the C and Rust compiler plugins.

    • Our experimental fork of the ReMon MVEE modified for C/Rust side-by-side checking, along with the mvee-configs directory that contains some MVEE configuration examples.

    Cross-checking Tutorial

    Introduction

    The C2Rust transpiler aims to convert C code to semantically equivalent unsafe Rust code, and later incremental refactoring passes gradually transform this code to Rust code. However, the initial Rust translation might not be a perfect semantic match to the original C code, and the refactoring passes may also change the code in ways that break semantics. Cross-checking is an automated way to verify that the translated program behaves the same as the original C code.

    The way cross-checking achieves this goal is by comparing the execution traces of all versions (henceforth called "variants") of the program (original C, unsafe and refactored Rust) and checking for any differences. Our cross-checking implementation modifies the source code of the program at compile-time (separately during C and Rust compiler invocation) so that the variants output the traces at run-time, and then checks the traces against each other either online during execution (using the ReMon MVEE), or offline by comparing log files. The C2Rust cross-checkers currently instrument function entry and exit points, function return values, and function call arguments (currently experimental and disabled by default, but can be enabled per argument, function or file).

    Example

    To illustrate how cross-checking works, let us take the following code snippet:

    int foo() {
        return 1;
    }
    

    Calling the foo function will cause the following cross-check events to be emitted:

    XCHECK(Ent):193491849/0x0b887389
    XCHECK(Exi):193491849/0x0b887389
    XCHECK(Ret):8680820740569200759/0x7878787878787877
    

    Building code with cross-checks

    C2Rust contains one cross-checking implementation per language, in the form of a compiler plugin in both cases. We provide a clang plugin for C code, and a rustc plugin for Rust code.

    Building C code

    To build C variants with cross-checks enabled, first build the cross-checking plugin using $C2RUST/scripts/build_cross_checks.py, then run clang (or pass it to the build system) with the following options:

    • -Xclang -load -Xclang $C2RUST/build/clang-xcheck-plugin.$(uname -n)/plugin/CrossChecks.so to load the plugin
    • -Xclang -add-plugin -Xclang crosschecks to activate the plugin
    • -Xclang -plugin-arg-crosschecks -Xclang <...> for every additional option to pass to the plugin
    • -ffunction-sections may be required to correctly deduplicate some linkonce functions inserted by the plugin

    Note that every option passed to clang requires a -Xclang prefix before the actual option, so that the compiler driver passes it to the clang backend correctly. We provide a cc_wrapper.sh script in the plugin source code directory that inserts these automatically, as well as several project-specific scripts in directories under examples/.

    Additionally, the following arguments should be passed to the linker:

    • The cross-checking runtime library from $C2RUST/build/clang-xcheck-plugin.$(uname -n)/runtime/libruntime.a
    • A cross-checking backend library that provides the rb_xcheck function, e.g., libfakechecks for offline logging or libclevrbuf for online MVEE-based checks

    Building Rust code

    Building Rust code with cross-checks is simpler that C code, and only requires a few additions to Cargo.toml and the main Rust source file. Add the following to your Cargo.toml file (replacing $C2RUST to the actual path to this repository):

    [dependencies.c2rust-xcheck-plugin]
    path = "$C2RUST/cross-checks/rust-checks/rustc-plugin"
    
    [dependencies.c2rust-xcheck-derive]
    path = "$C2RUST/cross-checks/rust-checks/derive-macros"
    
    [dependencies.c2rust-xcheck-runtime]
    path = "$C2RUST/cross-checks/rust-checks/runtime"
    features = ["libc-hash", "fixed-length-array-hash"]
    

    and this preamble to your lib.rs or main.rs:

    #![feature(plugin, custom_attribute)]
    #![cross_check(yes)]
    
    #[macro_use] extern crate c2rust_xcheck_derive;
    #[macro_use] extern crate c2rust_xcheck_runtime;
    

    You may also add #![plugin(c2rust_xcheck_plugin(...))] to pass additional arguments to the cross-checking plugin.

    Cross-check configuration

    Cross-checks can be customized at a fine granularity using cross-check configuration files or inline attributes.

    Running cross-checked programs

    Offline mode

    When cross-checking in offline mode, all variants are executed independentely on the same inputs, and their cross-checks are written to either standard output or log files. After running all the variants, divergence can be detected by manually comparing the logs for mismatches. There are several backend libraries that support different types of logging outputs:

    • libfakechecks outputs a list of the cross-checks linearly to either standard output or a file (specified using the FAKECHECKS_OUTPUT_FILE environment variable)
    • zstd-logging library from cross-checks/rust-checks/backends (can also be used with the clang plugin) outputs a binary encoding of the cross-checks that is compressed using zstd, and is much more space-efficient than the text output of libfakechecks. The compressed output files can be converted to text using the xcheck-printer tool.

    Before running the C and Rust variants, you may need to load in one of these libraries using LD_PRELOAD if you haven't linked against it and passed in its path using -rpath (this is fairly easy to do for a C build, but more complicated when using Cargo for Rust code), like this:

    $ env LD_PRELOAD=$C2RUST/cross-checks/libfakechecks/libfakechecks.so ./a.out
    

    Running each variant with cross-checks enabled will print a list of cross-check results to the specified output. A simple diff or cmp command will show differences in cross-checks, if any.

    Online (MVEE) mode

    The other execution mode for cross-checks is the online mode, where a monitor program (the MVEE) runs all variants in parallel with exactly the same inputs (by intercepting input system calls like read and replicating their return values) and cross-checks all the output system calls and instrumentation points inserted by our plugins. This approach has several advantages over offline mode:

    • Input operations are fully replicated, including those from stateful resources like sockets; only the master variant performs each actual operation, and each other variant only gets a copy of the data.
    • Outputs are cross-checked but not duplicated, so each output operation is only executed by the master variant; the others are only cross-checked for matching outputs. For example, only the master variant opens and writes to output files.
    • The lock-step MVEE automatically eliminates most sources of non-determinism, like threading and non-deterministic syscalls, e.g., reading from /dev/urandom (see the Troubleshooting section below for more details)

    However, the main disadvantage of this approach is that some applications may not run correctly under the MVEE, due to either incomplete support from the MVEE or fundamental MVEE limitations. In such cases, we recommend using offline mode instead.

    To run your application inside our MVEE, first build it following the instructions in its README. After building it successfully, write an MVEE configuration file for your application (there is a sample file in the MVEE directory, and a few others in our examples directory), then run the MVEE:

    $ ./MVEE/bin/Release/MVEE -f <path/to/MVEE_config.ini> -N<number of variants> -- <variant arguments>
    

    The MVEE.ini configuration file is fairly self-explanatory, but there are a few notable settings that are important:

    • xchecks_initially_enabled disables system call replication and cross-checks up to the first function cross-check (usually for the main function), and should be false by default for cross-language checks. This is because the Rust runtime performs a few additional system calls that C code does not, and the MVEE would terminate with divergence if cross-checks were enabled.
    • relaxed_mman_checks and unsynced_brk disable MVEE cross-checks on the mmap family of calls and brk, respectively, and should both be set to true if the Rust code performs significantly different memory allocations.
    • path specifies the path to the variant's executable, and should be specified separately per variant (ReMon also supports running multiple variants for the same binary, but with different command line arguments; this is not used by C2Rust cross-checks). All variants should be files in the same directory, otherwise the MVEE will abort with a divergence inside the ELF loader.
    • argv specifies the arguments to pass to each variant (can be configured per-variant or globally for all variants).
    • env specifies the environment variables to pass to the variants, and should at least contain a LD_LIBRARY_PATH entry for the libclevrbuf.so library, and a LD_PRELOAD entry for the zeroing allocator libzero_malloc.so, like this:
    {
      "variant": {
        "global": {
          "exec": {
            "env": [
              "LD_LIBRARY_PATH=../../../cross-checks/ReMon/libclevrbuf",
              "LD_PRELOAD=../../../cross-checks/zero-malloc/target/release/libzero_malloc.so"
            ]
          }
        }
      }
    }
    

    Troubleshooting

    In case you run into any issues while building or running the variants, please refer to this section for possible fixes.

    Build failures

    Builds may occasionally fail because of partially or completely unsupported features in our plugins:

    • Bitfields in C structures: these do not currently have a Rust equivalent, and the transpiler converts them to regular integer types. The clang plugin will exit with an error when trying to cross-check a bitfield.
    • Variadic functions: Rust does not support these yet, and our clang plugin cannot handle them either.
    • Fixed-sized arrays of large or unusual sizes: as of the writing of this document, Rust does not have const generics yet, and we need them to support arbitrary-sized arrays on the Rust side. Until then, the rustc plugin runtime only supports cross-checking on fixed-sized arrays from a limited set of sizes (all integers up to 32, all powers of 2 up to 1024).
    • Function pointers with more than 12 arguments: these require variadic generics in Rust.

    In all these cases, we recommend that you either disable cross-checks for values of these types, or manually provide a custom cross-check function (see the cross-check configuration for more details).

    Inline functions in C headers

    Cross-checker output for C variants may sometimes contain additional cross-checks for inline functions from system headers which are missing from the corresponding Rust translation. In such cases, we recommend manually disabling cross-checks for the C inline functions using an external cross-check configuration file.

    Non-determinism

    Ideally, divergence (cross-checks differences between variants) is only caused by actual semantic differences between the variants. However, there is another cause of unintended divergence: nondeterminism in program execution. In most cases, non-determinism will simply cause each variant to produce different cross-checks, but it may also occasionally cause crashes. There are many causes of program non-determinism that interfere with cross-checking:

    • Calls to time, RNG, PID (getpid() and friends) and other non-deterministic OS functions and operations, e.g., reads from /dev/urandom
    • File descriptors. We have hard-coded a fixed cross-check value for all FILE* objects for this exact reason.
    • Threading, i.e., non-deterministic thread scheduling
    • Pointer-to-integer casts under ASLR.

    Generally, running the variants in online mode inside the MVEE fixes these issues by replicating all system calls between the variants, which ensures that they all receive the same values from the OS. In case the MVEE does not support a specific application and you need to run it in offline mode (or for any other reason), the recommended fix is to remove all such non-determinism from your code manually, e.g., replace all reads from /dev/urandom with constant values.

    To verify that non-determinism truly is the cause of divergence, we recommend running each separate variant multiple times and cross-checking it against itself. If non-determinism really is the problem, each run will produce different cross-checks.

    A note on ASLR and pointers: our cross-checkers currently check pointers by dereference instead of by address, thereby making the checks insensitive to ASLR. However, manually casting pointers to integers poses problems because integers cannot be dereferenced. Integers are cross-checked by value regardless of their source, and their values will differ across runs when they originate from pointers with ASLR enabled.

    Uninitialized memory

    Uninitialized memory is one common source of non-determinism, since an uninitialized value may have different actual values across different runs of a program. Since our plugins cross-check pointers by dereferencing them, invalid pointers can also crash our cross-checking runtime.

    To eliminate this problem, we force zero-initialization for all C and Rust values. The plugins enforce this for stack (function-local values), and all global values are already zero-initialized (as required by the C standard), which only leaves heap-allocated values, i.e., those allocated using malloc. We provide a zeroing malloc wrapper under cross-checks/zero-malloc which can be preloaded into an application using LD_PRELOAD. This wrapper library intercepts all memory allocation calls, and zeroes the allocated buffer after each call. To use this in Rust executables in place of the default jemalloc, add the following lines to your code to use the system allocator, which our library intercepts:

    #[global_allocator]
    static A: ::std::alloc::System = ::std::alloc::System;
    

    We recommend that you use our zeroing memory allocator for all cross-checks.

    Pointer aliasing

    Some C code will use pointers of some type T to refer to values of another type U, tricking our runtime into cross-checking the values incorrectly. This may not only cause divergence, but also potential crashes when our runtime attempts to cross-check actual integers as invalid pointers (see example below). If the value of the integer incidentally represents a valid memory address, the runtime will try to cross-check that memory as a T; otherwise, the runtime will most likely crash.

    struct T {
      int n;
    };
    struct U {
      char *s;
    };
    void foo(struct U *x) {
      // Cross-check problem here: will try to cross-check `x` as a `struct U*`,
      // when it's a `struct T*`, so the check will most likely crash
      // when attempting to dereference x->s
    }
    int main() {
      T x = { 0x1234 };
      foo((struct U*)&x);
      return 0;
    }
    

    Our cross-checking runtimes can recover from attempts to dereference invalid pointers, but rely on the pointer-tracer tool that uses ptrace to check and restart all invalid pointer dereferences. To use this recovery feature, you must pointer-tracer to start the variants:

    $ $C2RUST/cross-checks/pointer-tracer/target/release/pointer-tracer ./a.out
    

    Alternatively, some issues caused by pointer aliasing can be fixed by disabling cross-checks altogether for certain types and values, or by providing custom cross-check functions for certain types. For example, one common pattern is the tagged union, where multiple structures have an overlapping prefix with a tag, followed by a type-specific part:

    enum Tag {
      TAG_A,
      TAG_B
    };
    struct TypeA {
      // Common part
      Tag tag;
      char *foo;
      // Specific part
      int valA;
    };
    struct TypeB {
      // Common part
      Tag tag;
      char *foo;
      // Specific part
      void *valB;
    };
    

    For this example, you can either disable cross-checks manually for all the type-specific types, e.g., valA and valB above, or provide a custom cross-check function that cross-checks each value based on its tag.

    End-of-buffer pointers

    Another common C pattern related to memory allocations is the pointer to end of buffer pattern:

    struct Buf {
      char *start;
      char *end;
    };
    void alloc(struct Buf *buf, unsigned size) {
        buf->start = malloc(size);
        buf->end = buf->start + size;
    }
    

    In this example, cross-checks will diverge on the end pointer, since it points to the first byte after the allocation returned by malloc. Since we only require the allocation itself to be zero-initialized, the value of that byte is undefined, and could change at any time during the execution of the variant.

    For any pointers outside allocated memory, we recommend disabling cross-checks altogether.

    Benign dangling pointers

    The cross-checking runtimes may attempt to cross-check pointers to values that have been deallocated, e.g., by calling free, but still linger in memory without being used by the program. This means that the runtimes may dereference these pointers, even if the program never does, which may lead to divergence since the allocator is free to reuse that memory.

    struct Data {
      int allocated;
      char *buf;
    };
    struct Data *free_data(struct Data *data) {
        data->allocated = 0;
        free(data->buf);
        // Potential non-determinism: data->buf has been freed,
        // but our runtime will try to dereference it
        return data;
    }
    

    As of the writing of this document, we have no automatic way to detect when the runtimes attempt to dereference deallocated memory, so we recommend manually disabling cross-checks when this occurs.

    Compiler builtins and optimizations

    In some cases, clang and rustc may optimize the generated code differently in ways that produce divergence. For example, clang (more specifically, a LLVM optimization pass enabled by clang) converts single-argument printf calls to direct calls to puts. For example, clang converts printf("Hello world!\n"); to puts("Hello world!");. The two functions have different internal implementations and make different syscalls (mainly the write syscall), so this optimization causes divergence. We recommend compiling all C code with the -fno-builtin argument to prevent this.

    Cross-checking Configuration

    In many cases, we can add identical cross-checks to the original C and the transpiled Rust code, e.g., when the C code is naively translated to the perfectly equivalent Rust code, and everything just works. However, this might not always be the case, and we need to handle mismatches such as:

    • Type mismatches between C and Rust, e.g., a C const char* (with or without an attached length parameter) being translated to a str. Additionally, if a string+length value pair (with the types const char* and size_t) gets translated to a single str, we may want to omit the cross-check on the length parameter.
    • Whole functions added or removed by the transpiler or refactoring tool, e.g., helpers.

    Note that this list is not exhaustive, so there may be many more cases of mismatches.

    To handle all these cases, we need a language that lets us add new cross-checks, or modify or delete existing ones.

    The cross-check language

    The cross-check metadata is stored as a YAML encoding of an array of configuration entries. Each configuration entry describes the configuration for that specific check.

    An example configuration file for a function foo with 3 arguments a, alen and b looks something like:

    main.c:
      - item: defaults
        disable_xchecks: true
    
      - item: function
        name: foo
        disable_xchecks: false
        args:
          a: default
          alen: none
          b: default
        return: no
    
    main.rs:
      - item: function
        name: foo
        args:
          a: default
          b: default
        return: no
    

    Inline vs external configuration

    We can store the cross-check configuration entries in a few places:

    • Externally in separate configuration files.
    • Inline in the source code, attached to the checked functions and structures.

    Each approach has advantages and drawbacks. Inline configuration entries are simpler to maintain, but do not scale as well to larger codebases or more complex cross-check configuration entries. Conversely, external configuration entries are more flexible and can potentially express complex configurations in a cleaner and more elegant way, but can easily get out of sync with their corresponding source code. We currently support both approaches, with external configuration settings taking priority over inline attributes where both are present.

    In the current implementation of the Rust cross-checker, inline configuration settings are passed to the enclosing scope's #[cross_check] attribute, e.g.:

    
    # #![allow(unused_variables)]
    #fn main() {
    #[cross_check(yes, entry(djb2="foo"))]
    fn bar() { }
    
    #[cross_check(yes, entry(fixed=0x1234))]
    fn baz() { }
    #}

    Configuration file format

    At the top level, each configuration file is a YAML associative array mapping file names to their configuration entries. Each array element maps a file name (represented as a string) to a list of individual items, each item representing a Rust/C scope entity, i.e., function or structure. Each item is encoded in YAML as an associative array. All items have a few common array members:

    • item specifies the type of the current item, e.g., function, struct or others.
    • name specifies the name of the item, i.e., the name of the function or structure.

    Function cross-check configuration

    Function cross-checks are configured using entries with item: function. Function entries support the following fields:

    Field Role
    disable_xchecks Disables all cross-checks for this function and everything in it if set to true.
    entry Configures the function entry cross-check (see below for information on accepted values).
    exit Configures the function exit cross-check.
    all_args Specifies a cross-check override for all of this function's arguments. For example, setting all_args: none disables cross-checks for all arguments.
    args An associative array that maps argument names to their corresponding cross-checks. This can be used to customize the cross-checks for some of the function arguments individually. This setting overrides both the global default and the one specified in all_args for the current function.
    return Configures the function return value cross-check.
    ahasher and shasher Override the default values for the aggregate and simple hasher for this function (see the hashing documentation for the meaning of these fields).
    nested Recursively configures the items nested inside the current items. Since Rust allows arbitrarily deep function and structure nesting, we use this to recursively configure nested functions.
    entry_extra Specifies a list of additional custom cross-checks to perform after the argument. Each cross-check accepts an optional tag parameter that overrides the default UNKNOWN tag.
    exit_extra Specifies a list of additional custom cross-checks to perform on function return.

    Structure cross-check configuration

    Structure entries configure cross-checks for Rust structure, tuple and enumeration types, and are tagged with item: struct. For a general overview of cross-checking for structures (aggregate types), see the hashing documentation. Structure entries support the following fields:

    Field Role
    disable_xchecks Disable automatic cross-check emission for this structure (this is generally best left out, unless the default is true and needs to be reset to false).
    field_hasher Configures the replacement hasher for this structure. The hasher is a Rust object that implements the cross_check_runtime::hash::CrossCheckHasher trait.
    custom_hash Specifies a function to call to hash objects of this type, instead of the default implementation. This function should have the signature fn foo<XCHA, XCHS>(arg: &T, depth: usize) -> u64 where T is the name of the current type. XCHA and XCHS are template parameters passed by the caller that specify the aggregate and simple hasher to use for this computation (and can be overridden using ahasher and shasher below).
    fields An associative array that specifies custom hash computations for some or all of the structure's fields. Accepts values in the format of cross-check types.
    ahasher and shasher Override the aggregate and simple hasher for the default hash implementation for the current type (mainly useful if field_hasher is left out). These are recursively passed to the hash function call for each structure field.

    The field_hasher and custom_hash provide two alternative methods of customizing the hashing algorithm for a given structure: users may either provide a custom implementation of CrossCheckHasher and pass that to field_hasher, or implement a hashing function and pass it to custom_hash. The two alternatives are mostly equivalent, and users may use whichever is more convenient. Additionally, users can choose to completely disable the automatic derivation of CrossCheckHash, and manually implement CrossCheckHasher for some of the types instead.

    Cross-check types

    There are several types of cross-check implemented in the compiler:

    Check Value Type Behavior
    default Lets the compiler perform the default cross-check.
    none or disabled Disables cross-checking or hashing for the current value.
    fixed u64 Sets the cross-checked value to the given 64-bit integer.
    djb2 String Sets the cross-checked value to the djb2 hash of the given string. This is mainly useful for overriding function entry cross-checks, in case the function names don't match between languages.
    as_type String Perform the default value cross-check, but after casting the value to the given type, e.g., cast it to a u32 then cross-check it as a u32.
    custom String Parses the given string as a C or Rust expression and uses it to compute the cross-checked value. In most cases, the string is inserted verbatim into the cross-check code, e.g., for function argument cross-checks.

    Each cross-check is encoded in YAML as either a single word with the type, e.g., default, or a single-element associative array mapping the type to its argument, e.g., { fixed: 0x1234 }.

    More cross-check types may be added as needed.

    Custom hash functions for structures

    If custom_hash: { custom: "hash_foo" } is a configuration entry for structure Foo, then the compiler will insert a call to hash_foo to perform the cross checks. This function should have the following signature:

    
    # #![allow(unused_variables)]
    #fn main() {
    fn hash_foo<XCHA, XCHS>(foo: &Foo, depth: usize) -> u64 { ... }
    #}

    The hash function receives a reference to a Foo object and a maximum depth, and should return the 64-bit hash value for the given object.

    Custom hash functions for structure fields

    If bar: { custom: "hash_bar" } is a configuration entry for field bar, then the compiler will insert a call to hash_bar to compute the hash for bar. This function should have the following signature:

    
    # #![allow(unused_variables)]
    #fn main() {
    fn hash_bar<XCHA, XCHS, S, F>(h: &mut XCHA, foo: &S, bar: &F, depth: usize)
           where XCHA: cross_check_runtime::hash::CrossCheckHasher { ... }
    #}

    The function receives the following arguments:

    • The current aggregate hasher for this structure. The function can call the hasher's write_u64 function as many times as needed.
    • The structure containing this field. This argument has generic type S, so the same function can be reused for different structures.
    • The field itself, with generic type F. The function may require additional type bounds for F to make it compatible with its callers.
    • The maximum hashing depth (explained in the hashing documentation).
    • The type parameters XCHA and XCHS bound to the current aggregate and simple value hasher for the current invocation.

    This function should not return the hash value of the field. Instead, the function should call the hasher's write_u64 method directly.

    Per-file default settings

    The special defaults item type specifies the default cross-check settings for all items in a file. We currently support the following entries:

    Field Role
    disable_xchecks Disables all cross-checks for this file. Can be individually overridden per function or structure.
    entry Configures the default entry cross-check for all functions in this file.
    exit Similarly configures the function exit cross-check.
    all_args Specifies a cross-check override for all arguments to all functions in this file. For example, setting all_args: default enables cross-checks for all arguments.
    return Configures the function return value cross-check.

    More examples

    Function example

    Example configuration for a function baz1(a, b):

    main.rs:
      - item: function
        name: baz1
        entry: { djb2: "baz" }    // Cross-check the function as "baz"
        args:
          a: { custom: "foo(a)" } // Cross-check a as foo(a)
          b: none                 // Do not cross-check b
        entry_extra:              // Cross-check foo(b) with a FUNCTION_ARG tag
          - { custom: "foo(b)", tag: FUNCTION_ARG }
          - { custom: "a" }       // Cross-check the value "a" with UNKNOWN_TAG
    

    Structure example

    Example configuration for a structure Foo (illustrated on an object foo of type Foo):

    main.rs:
      - item: struct
        name: Foo
        field_hasher: "FooHasher"  // Use FooHasher as the aggregate hasher
        fields:
          a: { fixed: 0x12345678 } // Use 0x12345678 as the hash of foo.a
          b: { custom: "hash_b" }  // Hash foo.b using hash_b(foo.b)
          c: none                  // Ignore foo.c when hashing foo
    

    Inline cross-check configuration

    In addition to the external configuration format, a subset of cross-checks can also be configured inline in the program source code. The compiler plugin provides a custom #[cross_check] attribute used to annotate functions, structures and fields with custom cross-check metadata.

    Inline function configuration

    The #[cross_check] function attribute currently supports the following arguments:

    Argument Type Role
    none or disabled Disable cross-checks for this function and all its sub-items (this attribute is inherited). Each sub-item can individually override this with yes or enabled.
    yes or enabled Enable cross-checks for this function and its sub-items. Each nested item can also override this setting with none or disabled.
    entry XCheckType Cross-check to use on function entry, same as for external configuration.
    exit XCheckType Cross-check to use on function entry, same as for external configuration.
    all_args XCheckType Enable cross-checks for this function's arguments (disabled by default). Takes the cross-check type as its argument.
    args(...) Per-argument cross-check overrides (same as for external configuration).
    return XCheckType Cross-check to perform on the function return value, same as for external configuration.
    ahasher and shasher String Same as for external configuration.
    entry_extra and exit_extra Same as for external configuration.

    Function example

    
    # #![allow(unused_variables)]
    #fn main() {
    #[cross_check(yes, entry(djb2="foo"))] // Cross-check this function as "foo"
    fn foo1() {
      #[cross_check(none)]
      fn bar() { ... }
      bar();
    
      #[cross_check(yes, all_args(default), args(a(fixed=0x123)))]
      fn baz(a: u8, b: u16, c: u32) { ... }
      baz(1, 2, 3);
    }
    #}

    Inline structure configuration

    The compiler plugin also supports a subset of the full external configuration settings as #[cross_check] arguments:

    Argument Type Role
    field_hasher String Same as for external configuration.
    custom_hash String Same as for external configuration.
    ahasher and shasher String Same as for external configuration.

    The #[cross_check] attribute can also be attached to structure fields to configure hashing:

    Argument Type Role
    none or disabled This field is skipped during hashing.
    fixed u64 Fixed 64-bit integer to use as the hash value for this field. Identical to the fixed external cross-check type.
    custom_hash String Same as for external configuration.

    Structure example

    
    # #![allow(unused_variables)]
    #fn main() {
    #[cross_check(field_hasher="MyHasher")]
    struct Foo {
      #[cross_check(none)]
      foo: u64,
    
      #[cross_check(fixed=0x1234)]
      bar: String,
    
      #[cross_check(custom_hash="hash_baz")]
      baz: String,
    }
    #}

    Caveats

    Duplicate items

    At any level or scope, there may be duplicate items, i.e., multiple items with the same names. It is not clear at this point how to best handle this case, since we have several conflicting requirements. On the one hand, we may wish to allow the configuration for one source file to be spread across multiple configuration files, and entries from later configuration files to be appended or replace entries from earlier files. On the other hand, we may have identically-named structures or functions in nested scopes that we want to configure separately. For an example, consider the following code:

    fn foo(x: u32) -> u32 {
        if x > 22 {
            fn bar(x: u32) -> u32 {
                x - 22
            };
            bar(x)
        } else {
            fn bar(x: u32) -> u32 {
                x + 34
            }
            bar(x)
        }
    }
    

    In this example, there are two distinct foo::bar functions, and we wish to configure them separately. However, at the top level of a file, there may only be one foo function, so we can merge all entries for foo together. Alternatively, we could check for multiple top-level items with the same name and exit with an error if we encounter any duplicates.

    Configuration priority

    Currently, if a certain cross-check is configured using both an external entry and an inline #[cross_check(...)] attribute, the external entry takes priority. Alternatively, we may reverse this priority, or exit with an error if both are present.

    Scope configuration inheritance

    The configuration settings described above apply to the scope of an item. While most settings apply exclusively to the scope itself (for example, args and all_args settings only apply to the current function, e.g., foo above and not any of the bar functions) and not any of its nested sub-items, there are a few that apply to everything inside the scope. These attributes are internally "inherited" from each scope by its child scopes. Currently, the only inherited attributes are disable_xchecks (so that disabling cross-checks for a module or function disables them for everything inside that function), ahasher and shasher.

    Custom cross-check parameters

    Custom cross-check definitions have a different format for each language. The rustc plugin accepts any Rust expression that is valid on function entry as a custom cross-check.

    The clang plugin, on the other hand, only accepts a limited subset of C expressions: each cross-check specification contains the name of the function to call, optionally followed by a list of parameters to pass to the function, e.g., function or function(arg1, arg2, ...). Each parameter is the name of a global variable or function argument, and is optionally preceded by & (to pass the parameter by address instead of value) or by * (to dereference the value if it is a pointer).

    Anonymous structures

    C allows developers to define anonymous structures that define the type for a single value, e.g.:

    struct {
      int x;
    } y;
    

    For a variety of reasons, we need to assign names to these structures ourselves. The most important reason is that we need to identify these structures in the external configuration files. We assign the names using one of the following formats, depending on the context where the anonymous structure is defined:

    Assigned name Meaning
    Foo$field$x This structure defines the type for the field x of the outer structure Foo. Note that Foo itself may also be an anonymous structure that follows the same naming policy.
    foo$arg$x This structure defines the type for the argument x of function foo (as illustrated below).
    foo$result This structure defines the return type for function foo.

    Examples

    struct Foo {
      struct {                  // This gets named `Foo$field$x`
        int x;
      }
    };
    
    struct { int a; }           // This gets the `foo$result` name
    foo(struct { int b; } x) { // The `x` argument type gets the `foo$arg$x` name
    }
    

    Cross-checking hashing algorithm

    For a given value x of a type T, our cross-checking implementation needs to hash x to a hash value H(x) of fixed size (64 bits in the current implementation), regardless of the size and layout of T. This document describes the design and implementation of the type-aware hashing algorithms used by the cross-checker.

    Using an established hash functions over the raw bytes of x has a few disadvantages:

    • C/Rust structures contain padding bytes between consecutive fields (due to alignment requirements), and we must not include this padding in the hash.
    • Pointer addresses are non-deterministic due to ASLR and other factors, so we must hash them by dereference instead of address.

    For these reasons, we have chosen to design our own type-aware hashing algorithms. The algorithms hash each value differently depending on its type, and are implemented by functions with the following signature:

    uint64_t __c2rust_hash_T(T x, size_t depth);
    

    We use recursive hashing algorithms for complex types. To prevent infinite recursion and long hashing times, we limit the recursion depth to a fixed value. When recursion reaches this limit, the hash function returns a constant hash instead of going deeper.

    We distinguish between the following kinds of types:

    • Simple types, e.g., integers, booleans, characters, floats, are trivial types which can be hashed directly by value. In the current implementation, we hash these values by XORing them with a constant that depends on the type (see the C and Rust implementations for details). Since simple types cannot recurse, we perform no depth checks for this case.

    • Aggregate (or non-trivial) types:

      • Structures. We hash the contents of each structure by recursively hashing each field (with depth increased by one), then aggregating all the hashes into one. We currently use the JodyHash function for the latter.

      • Fixed-size arrays are hashed in fundamentally the same way as structures, by recursively hashing each array element then aggregating the resulting hashes.

      • Pointers. We avoid hashing pointers by address for the reasons listed above. Instead, we hash each pointer by recursively hashing its dereferenced value (with depth increased by one). We have two special cases here that we need to handle:

        • Null pointers, which our hash functions check and return a special hard-coded hash value for.
        • Non-null invalid pointers. Our cross-checking implementation will crash when dereferencing these pointers. However, running the crashing program either using pointer-tracer tool or under the MVEE will fix the crashes and safely hash these pointers by returning another special hard-coded value.

    Other data types, e.g., unions and structures containing bitfields, are difficult to hash programatically and require the user to specify a manual hash function.

    The cross-checking configuration settings can be used to specify different hashing algorithm separately for simple and aggregate types.

    Rustc cross-checker compiler plugin

    This is a simple cross-check inserter for Rust code that is implemented as a Rust compiler plugin.

    Usage

    To use the compiler plugin, you need to take several steps. First, add the plugin as a Cargo dependency to your Cargo.toml file:

    [dependencies]
    c2rust-xcheck-plugin = { path = ".../C2Rust/cross-checks/rust-checks/rustc-plugin" }
    c2rust-xcheck-derive = { path = ".../C2Rust/cross-checks/rust-checks/derive-macros" }
    c2rust-xcheck-runtime = { path = ".../C2Rust/cross-checks/rust-checks/runtime" }
    

    with ... as the full path to the C2Rust repository. Next, add the following preamble to your main.rs or lib.rs file:

    
    # #![allow(unused_variables)]
    #![feature(plugin)]
    #![plugin(c2rust_xcheck_plugin)]
    
    #fn main() {
    #[macro_use]
    extern crate c2rust_xcheck_derive;
    #[macro_use]
    extern crate c2rust_xcheck_runtime;
    #}

    Cross-checker options

    Cross-checking is enabled and configured using the #[cross_check] directive, which can either be enabled globally (using #![cross_check] at the beginning of main.rs or lib.rs) or individually per function (the per-function settings override the global ones).

    The directive optionally takes the following options:

    • yes and enabled enable cross-checking for the current scope (crate or function).
    • none and disabled disable cross-checking for the current scope.
    • entry(djb2="foo") sets the cross-checking name for the current function entry point to the DJB2 hash of foo.
    • entry(fixed=NNN) sets the cross-checking ID for the current function entry point to NNN.

    Example:

    
    # #![allow(unused_variables)]
    #fn main() {
    #[cross_check(yes, entry(djb2="foo"))]
    fn bar() { }
    
    #[cross_check(yes, entry(fixed=0x1234))]
    fn baz() { }
    
    #[cross_check(no)]
    fn foo() { }
    #}

    Clang plugin for crosschecking on C programs

    This is a cross-check inserter for C programs implemented as a clang compiler plugin.

    Building and running the plugin

    1. Build libfakechecks (optional, useful for testing):

       $ cd ../../libfakechecks
       $ make all
      
    2. Build the clang plugin using the build script:

       $ ../../../scripts/build_cross_checks.py
      
    3. To compile code using the plugin, either wrap the compilation command with the cc_wrapper.sh script from this directory:

      $ cc_wrapper.sh <path/to/clang> .../CrossChecks.so <rest of command line...>
    

    or add the following arguments manually to the clang command line, e.g., using CFLAGS:

    -Xclang -load -Xclang .../CrossChecks.so -Xclang -add-plugin -Xclang crosschecks
    

    and link against libruntime.a. In both cases, the target binary must then be linked against one of the rb_xcheck implementation libraries: libfakechecks.so or libclevrbuf.so.

    Testing

    This plugin can be tested in this directory by running make test.

    Example translations

    The following example translations illustrate how to run C2Rust on real codebases. Each example has been modified if necessary to prepare it for translation with C2Rust and each has accompanying documentation on how to translate the example.

    The robotfindskitten example is accompanied by a demonstration of the refactoring tool rewriting the unsafe translated Rust into idiomatic, safe Rust.

    json-c library

    Translating json-c

    # in examples/json-c/repo:
    ../configure    # use the custom c2rust configure script
    intercept-build make
    make check
    python3 ../translate.py
    ninja -C rust
    

    This will produce rust/libjson-c.so.4.0.0.

    Running tests

    # in examples/json-c/repo:
    
    # Replace the C libjson-c.so with a symlink to the Rust one.
    # You only need to do this the first time.
    rm .libs/libjson-c.so.4.0.0
    ln -s ../rust/libjson-c.so.4.0.0 .libs/libjson-c.so.4.0.0
    
    # Run tests
    make check
    

    If you modify the C files, make check will try to rebuild some stuff and then will break because of the object files that translate.py deleted. If this happens, run make clean && make, then repeat the "running tests" steps from the top.

    url parser

    Getting Started

    If the repo submodule appears to be empty or out of date, you may need to run git submodule update --init path/to/repo.

    Transpiling

    $ intercept-build make
    $ c2rust transpile compile_commands.json
    $ rustc test.rs
    

    qsort

    This tiny project provides an example of how to use CMake to build a C project and to generate the clang "compile_commands.json" file which is used by tools like the c2rust-ast-exporter.

    Build with the following commands:

    $ mkdir ../build
    $ cd ../build
    $ cmake ../qsort -DCMAKE_EXPORT_COMPILE_COMMANDS=1
    $ cmake --build .
    $ c2rust transpile compile_commands.json
    

    tmux

    Checking out the tmux sources

    Only linux is supported at the moment, but OSX might work with some tweaks.

    In path/to/examples/tmux, initialize the git submodule:

    git submodule update --init repo

    Create a Makefile

    in tmux/repo:

    ./autogen.sh && ./configure

    Create a compile_commands.json

    in tmux/repo:

    intercept-build make check

    If your compile_commands.json enables optimizations(-O2, -O3, etc) you will need to remove them so that unsupported compiler_builtins are less likely to be generated and leave you in an uncompilable state.

    Run rm *.o compat/*.o here to get rid of gcc generated staticlibs or else you may see CRITICAL:root:error: some ELF objects were not compiled with clang: in the next step

    Generate Rust Code

    in tmux:

    ./translate.py to translate all required c files into the tmux/repo/rust/src and tmux/repo/rust/src/compat directories.

    Run Tmux

    Run cargo run to build and execute tmux.

    grabc

    Getting Started

    If the repo submodule appears to be empty or out of date, you may need to run git submodule update --init path/to/repo.

    Transpiling

    The steps to get the transpiled code are as follows:

    $ intercept-build make
    $ c2rust transpile compile_commands.json
    $ rustc grabc.rs -L/usr/X11R6/lib -lX11
    

    If you want to have the transpiler create a crate:

    $ intercept-build make
    $ c2rust transpile compile_commands.json --emit-build-files -m grabc --output-dir rust
    $ cd rust
    $ RUSTFLAGS="-L/usr/X11R6/lib -lX11" cargo build
    

    libxml2

    Checking out the libxml2 sources

    In path/to/examples/libxml2, initialize the git submodule:

    git submodule update --init repo

    Create a Makefile

    in libxml2/repo:

    ./autogen.sh

    and optionally ./configure (autogen.sh currently runs this automatically, so you're not required to).

    Create a compile_commands.json

    in libxml2/repo:

    intercept-build make check

    If your compile_commands.json enables optimizations(-O2) you will need to remove them so that unsupported compiler_builtins are less likely to be generated and leave you in an uncompilable state.

    Run rm .libs/*.o here to get rid of gcc generated staticlibs or else you may see CRITICAL:root:error: some ELF objects were not compiled with clang: in the next step

    Generate Rust Code

    in libxml2:

    ./translate.py to translate all required c files (including tests) into the libxml2/repo/rust/src and libxml2/repo/rust/examples directories.

    Fix Known Translation Issues

    in libxml2:

    ./patch_translated_code.py to apply patches to some known issues in the generated code.

    Run Libxml2 C Tests

    Since each of these tests have their own main file, we decided to move them to the rust examples directory instead of trying to wrap them in the test framework.

    You can run a test like so: cargo run --example EXAMPLE where EXAMPLE is one of the files in libxml2/repo/rust/examples, not including the file extension.

    Outstanding Test Issues

    Runnable

    • testReader seems to be mostly working identically but with some slight differences. Try testReader --valid test/japancrlf.xml. It produces an extra "Ns verBoom: Validation failed: no DTD found !, (null), (null)"

    Working

    • runtest seems to be consistently successful now
    • testRelax seems to work equivalently with files as in C
    • testXPath seems to work equivalently with files as in C
    • xmllint seems to work equivalently with files as in C
    • testSAX prints out nothing on success, just like C version
    • testModule prints "Success!"
    • testHTML works with input files from test/HTML and produces same output as C version
    • testRegexp works with files from test/regexp and produces same output as C version
    • testrecurse prints "Total 9 tests, no errors"
    • testlimits prints "Total 514 tests, no errors"
      • Note: text output seems noticeably slower than the C version
    • testThreads prints nothing (but no longer prints parsing errors)
    • testapi runs successfully and prints "Total: 1172 functions, 280928 tests, 0 errors"
    • testC14N prints parsed output when given a file to read from test/c14n
    • testSchemas no longer crashes when provided a file from test/schemas/*.xsd
    • testchar prints tests completed
    • testdict prints "dictionary tests succeeded 20000 strings"
    • testAutomata takes a file from test/automata and produces equivalent output to C run
    • testURI waits on input from stdin, needs example input from test/URI. See Makefile.am and result/URI/uri.data for examples

    Working cross-checks

    • testchar all cross-checks match
    • testdict all cross-checks match
    • testapi all cross-checks match (345 million)
    • runtest all cross-checks match
    • testlimits all cross-checks match, but requires -fno-builtin as a compiler argument
    • testSAX works
    • testHTML works
    • testRegexp works
    • testModule requires testdso.so, doesn't work yet
    • testAutomata works
    • testSchemas works on all files from test/schemas
    • testRelax works on all files from test/relaxng
    • testURI works
    • testC14N works
    • testXPath works on files under test/XPath/expr and test/xmlid
    • testThreads deadlocks, still investigating
    • xmlllint does not compile

    Snudown

    To build snudown with the C2Rust translator and/or cross-checks, initialize the git submodule by running git submodule update --init path/to/repo.

    Make sure to build the derive-macros, runtime and rustc-plugin projects in the cross-checks folder beforehand. The runtime project must be built with the libc-hash feature (e.g. cargo build --features libc-hash).

    Next, cd into the repo directory and run python setup.py build with one of the following arguments:

    • --translate to translate the C code to Rust without any checks
    • --clang-crosschecks to build the C version of snudown with full cross-checking
    • --rust-crosschecks to translate to cross-checked Rust code
    • --use-fakechecks may be appended to use the fakechecks library to print out the cross-checks, instead of libclevrbuf from the MVEE
    • running with no flags will build the C version of the code
    • Note that -f may need to be appended to the end of the command to force a rebuild, if building multiple times consecutively

    After building any of the 3 versions, run python setup.py test to test it.

    genann (Neural Network Library)

    Getting Started

    If the repo submodule appears to be empty or out of date, you may need to run git submodule update --init path/to/repo.

    Transpiling

    # generate compile_commands.json
    $ intercept-build make
    $ c2rust transpile compile_commands.json --emit-build-files
    

    Testing

    Instead of translating with --emit-build-files to generate a library crate, you can build with --main exampleN where N is one of 1, 3, or 4 (example2.c seems to never halt in both C and Rust but translates and executes just fine). This will create a binary crate that will run the specified example.

    lil (Little Interpreted Language)

    Getting Started

    If the repo submodule appears to be empty or out of date, you may need to run git submodule update --init path/to/repo.

    Transpiling

    $ intercept-build make
    $ c2rust transpile compile_commands.json --emit-build-files -m main --output-dir rust
    $ cd rust
    $ cargo build
    

    xzoom

    Getting Started

    If the repo submodule appears to be empty or out of date, you may need to run git submodule update --init path/to/repo.

    Required Manual Changes

    You may need to add #include <unistd.h> to xzoom.c for it to properly generate (otherwise main_0 goes missing). This include is normally only added with the TIMER macro enabled, but seems to be required for standard functionality. (We could fork the repo if we want to make this change explicit for the purposes of automated testing.)

    Required Dependencies

    • clang >= 5.0
    • sed

    Transpiling

    $ clang -MJ compile_commands.o.json xzoom.c -L/usr/X11R6/lib -lX11
    $ sed -e '1s/^/[\n/' -e '$s/,$/\n]/' *.o.json > compile_commands.json
    $ c2rust transpile compile_commands.json
    $ rustc xzoom.rs  -L/usr/X11R6/lib -lX11
    

    Refactoring robotfindskitten

    This section details the refactoring script used to transform the initial Rust translation of robotfindskitten, as generated by c2rust transpile, into a safe Rust program. We divide the refactoring process into several major steps:

    • ncurses macro cleanup: The ncurses library implements parts of its API using C preprocessor macros, and a few of those macros expand to relatively complex code. We replace these expanded macro bodies with calls to equivalent functions, which are easier to recognize and refactor.

    • String formatting: robotfindskitten calls several printf-style string-formatting functions. We replace these unsafe variable-argument function calls with safe wrappers using Rust's format family of macros. Aside from improving memory safety, this also allows the Rust compiler to more accurately typecheck the format arguments, which is helpful for later type-directed refactoring passes.

    • Static string constants: robotfindskitten has two global variables containing string constants, which are translated to Rust as static mut definitions containing C-style *const c_char pointers. We refactor to remove both sources of unsafety, replacing raw pointers with checked &'static str references and converting the mutable statics to immutable ones.

    • Heap allocations: robotfindskitten uses a heap allocated array to track the objects in the game world. This array is represented as a raw pointer, and the underlying storage is managed explicitly with malloc and free. We replace the array with a memory-safe collection type, avoiding unsafe FFI calls and preventing out-of-bounds memory accesses.

    • Using the pancurses library: Calling ncurses library functions directly through the Rust FFI requires unsafe code at every call site. We replace unsafe ncurses function calls with calls to the safe wrappers provided by the pancurses crate.

    • Moving global state to the stack: robotfindskitten uses mutable global variables to store the game state, which turn into unsafe static mut definitions in Rust. We collect all such variables into a single stack-allocated struct, which can be mutated without unsafety.

    • libc calls: We replace calls to miscellaneous libc functions, such as sleep and rand, with calls to safe Rust equivalents.

    • Function argument types: Two remaining functions in robotfindskitten take raw pointers as arguments. We change each function's signature to use only safe Rust types, and update their callers to match.

    • String conversion cleanup: Several of the previous refactoring passes insert conversions between Rust and C string types. In several places, these conversions form cycles, such as &str -> *const c_char -> &str, which are both redundant and a source of unsafe code. We remove such conversion cycles to avoid unnecessary raw pointer manipulation.

    • Final cleanup: At this point, we have removed all the unsafe code we can. Only a few cleanup steps remain, such as removing unused unsafe qualifiers and deleting unused extern "C" definitions. In the end, we are left with a correct Rust translation of robotfindskitten that contains only a single line of unsafe code.

    ncurses macro cleanup

    robotfindskitten uses a variety of macros provided by the ncurses library. Since c2rust transpile runs the C preprocessor before translating to Rust, the expansions of those macros effectively get inlined in the Rust code at each call site. In many cases, this is harmless: for example, move(y, x) expands to wmove(stdscr, y, x), which is not much harder to refactor than the original. However, the attr_get and attrset macros are more complex: they expand to multiple lines of code involving several conditionals and complex expressions. In this step, we convert the expanded code into simple function calls, which are easier to manipulate in later refactoring passes.

    Fortunately, the ncurses library provides functions implementing the same operations as the troublesome macros, and we can call those functions through Rust's FFI. We begin by providing Rust declarations for these foreign functions. For ease of reading, we put the new declarations just after the existing extern "C" block:

    select target 'crate; child(foreign_mod); last;' ;
    create_item
        '
            extern "C" {
                fn wattr_get(win: *mut WINDOW, attrs: *mut attr_t,
                    pair: *mut libc::c_short, opts: *mut libc::c_void) -> libc::c_int;
                fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
            }
        '
        after ;
    

    Diff #1

    src/robotfindskitten.rs
    8
    )]
    8
    )]
    9
    #![feature(const_raw_ptr_to_usize_cast, extern_types, libc)]
    9
    #![feature(const_raw_ptr_to_usize_cast, extern_types, libc)]
    10
    extern crate libc;
    10
    extern crate libc;
    11
    extern "C" {
    11
    extern "C" {
    12
        pub type ldat;
    12
        pub type ldat;
    13
        #[no_mangle]
    13
        #[no_mangle]
    14
        fn printf(_: *const libc::c_char, ...) -> libc::c_int;
    14
        fn printf(_: *const libc::c_char, ...) -> libc::c_int;
    15
        #[no_mangle]
    15
        #[no_mangle]
    16
        fn cbreak() -> libc::c_int;
    16
        fn cbreak() -> libc::c_int;

    77
        #[no_mangle]
    77
       #[no_mangle]
    78
        fn time(__timer: *mut time_t) -> time_t;
    78
        fn time(__timer: *mut time_t) -> time_t;
    79
        #[no_mangle]
    79
        #[no_mangle]
    80
        fn sleep(__seconds: libc::c_uint) -> libc::c_uint;
    80
        fn sleep(__seconds: libc::c_uint) -> libc::c_uint;
    81
    }
    81
    }
    82
    extern "C" {
    83
        fn wattr_get(
    84
            win: *mut WINDOW,
    85
            attrs: *mut attr_t,
    86
            pair: *mut libc::c_short,
    87
            opts: *mut libc::c_void,
    88
        ) -> libc::c_int;
    89
        fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
    90
    }
    82
    pub type __time_t = libc::c_long;
    91
    pub type __time_t = libc::c_long;
    83
    pub type chtype = libc::c_ulong;
    92
    pub type chtype = libc::c_ulong;
    93
    94
    #[repr(C)]
    84
    #[derive(Copy, Clone)]
    95
    #[derive(Copy, Clone)]
    85
    #[repr(C)]
    86
    pub struct _win_st {
    96
    pub struct _win_st {
    87
        pub _cury: libc::c_short,
    97
        pub _cury: libc::c_short,
    88
        pub _curx: libc::c_short,
    98
        pub _curx: libc::c_short,
    89
        pub _maxy: libc::c_short,
    99
        pub _maxy: libc::c_short,
    90
        pub _maxx: libc::c_short,
    100
        pub _maxx: libc::c_short,

    110
        pub _pary: libc::c_int,
    120
        pub _pary: libc::c_int,
    111
        pub _parent: *mut WINDOW,
    121
        pub _parent: *mut WINDOW,
    112
        pub _pad: pdat,
    122
        pub _pad: pdat,
    113
        pub _yoffset: libc::c_short,
    123
        pub _yoffset: libc::c_short,
    114
    }
    124
    }
    125
    126
    #[repr(C)]
    115
    #[derive(Copy, Clone)]
    127
    #[derive(Copy, Clone)]
    116
    #[repr(C)]
    117
    pub struct pdat {
    128
    pub struct pdat {
    118
        pub _pad_y: libc::c_short,
    129
        pub _pad_y: libc::c_short,
    119
        pub _pad_x: libc::c_short,
    130
        pub _pad_x: libc::c_short,
    120
        pub _pad_top: libc::c_short,
    131
        pub _pad_top: libc::c_short,
    121
        pub _pad_left: libc::c_short,
    132
        pub _pad_left: libc::c_short,

    156
    /*Screen dimensions.*/
    167
    /*Screen dimensions.*/
    157
    /*Macros for generating numbers in different ranges*/
    168
    /*Macros for generating numbers in different ranges*/
    158
    /*Row constants for the animation*/
    169
    /*Row constants for the animation*/
    159
    /*This struct contains all the information we need to display an object
    170
    /*This struct contains all the information we need to display an object
    160
    on the screen*/
    171
    on the screen*/
    172
    173
    #[repr(C)]
    161
    #[derive(Copy, Clone)]
    174
    #[derive(Copy, Clone)]
    162
    #[repr(C)]
    163
    pub struct screen_object {
    175
    pub struct screen_object {
    164
        pub x: libc::c_int,
    176
        pub x: libc::c_int,
    165
        pub y: libc::c_int,
    177
        pub y: libc::c_int,
    166
        pub color: libc::c_int,
    178
        pub color: libc::c_int,
    167
        pub bold: bool,
    179
        pub bold: bool,

    Now we can use rewrite_expr to find Rust code that comes from the expansions of the wattrset macro and replace it with calls to the wattrset function:

    rewrite_expr
        '
            if !(__win as *const libc::c_void).is_null() {
                (*__win)._attrs = __attrs
            } else {
            }
        '
        'wattrset(__win, __attrs as libc::c_int)' ;
    

    Diff #2

    src/robotfindskitten.rs
    1202
            new |= 1u64 << 14i32 + 8i32
    1202
            new |= 1u64 << 14i32 + 8i32
    1203
        }
    1203
        }
    1204
        if o.bold {
    1204
        if o.bold {
    1205
            new |= 1u64 << 13i32 + 8i32
    1205
            new |= 1u64 << 13i32 + 8i32
    1206
        }
    1206
        }
    1207
        if !(stdscr as *const libc::c_void).is_null() {
    1207
        wattrset(stdscr, new as libc::c_int);
    1208
            (*stdscr)._attrs = new
    1209
        } else {
    1210
        };
    1211
        if in_place {
    1208
        if in_place {
    1212
            printw(
    1209
            printw(
    1213
                b"%c\x00" as *const u8 as *const libc::c_char,
    1210
                b"%c\x00" as *const u8 as *const libc::c_char,
    1214
                o.character as libc::c_int,
    1211
                o.character as libc::c_int,
    1215
            );
    1212
            );

    1220
                b"%c\x00" as *const u8 as *const libc::c_char,
    1217
                b"%c\x00" as *const u8 as *const libc::c_char,
    1221
                o.character as libc::c_int,
    1218
                o.character as libc::c_int,
    1222
            );
    1219
            );
    1223
            wmove(stdscr, o.y, o.x);
    1220
            wmove(stdscr, o.y, o.x);
    1224
        }
    1221
        }
    1225
        if !(stdscr as *const libc::c_void).is_null() {
    1222
        wattrset(stdscr, old as libc::c_int);
    1226
            (*stdscr)._attrs = old
    1227
        } else {
    1228
        };
    1229
    }
    1223
    }
    1230
    #[no_mangle]
    1224
    #[no_mangle]
    1231
    pub unsafe extern "C" fn instructions() {
    1225
    pub unsafe extern "C" fn instructions() {
    1232
        let mut dummy: libc::c_char = 0;
    1226
        let mut dummy: libc::c_char = 0;
    1233
        mvprintw(
    1227
        mvprintw(

    The __win and __attrs metavariables in the pattern correspond to the arguments of the original C macro, and are used in the replacement to construct the equivalent Rust function call.

    Next, we do the same thing for the more complicated wattr_get macro:

    rewrite_expr
        '
            if !(__win as *const libc::c_void).is_null() {
                if !(&mut __attrs as *mut attr_t as *const libc::c_void).is_null() {
                    __attrs = (*__win)._attrs
                } else {
                };
                if !(&mut __pair as *mut libc::c_short as *const libc::c_void).is_null() {
                    __pair = (((*__win)._attrs as libc::c_ulong
                        & ((1u32 << 8i32).wrapping_sub(1u32) << 0i32 + 8i32) as libc::c_ulong)
                        >> 8i32) as libc::c_int as libc::c_short
                } else {
                };
            } else {
            }
        '
        'wattr_get(__win, &mut __attrs, &mut __pair, ::std::ptr::null_mut())' ;
    

    Finally, we are done with this bit of cleanup, so we write the changes to disk before continuing on:

    commit ;
    

    Diff #4

    src/robotfindskitten.rs
    8
    )]
    8
    )]
    9
    #![feature(const_raw_ptr_to_usize_cast, extern_types, libc)]
    9
    #![feature(const_raw_ptr_to_usize_cast, extern_types, libc)]
    10
    extern crate libc;
    10
    extern crate libc;
    11
    extern "C" {
    11
    extern "C" {
    12
        pub type ldat;
    12
        pub type ldat;
    13
        #[no_mangle]
    13
        #[no_mangle]
    14
        fn printf(_: *const libc::c_char, ...) -> libc::c_int;
    14
        fn printf(_: *const libc::c_char, ...) -> libc::c_int;
    15
        #[no_mangle]
    15
        #[no_mangle]
    16
        fn cbreak() -> libc::c_int;
    16
        fn cbreak() -> libc::c_int;

    79
       #[no_mangle]
    79
        #[no_mangle]
    80
        fn sleep(__seconds: libc::c_uint) -> libc::c_uint;
    80
        fn sleep(__seconds: libc::c_uint) -> libc::c_uint;
    81
    }
    81
    }
    82
    extern "C" {
    82
    extern "C" {
    83
        fn wattr_get(
    83
        fn wattr_get(
    84
            win: *mut WINDOW,
    84
            win: *mut WINDOW,
    85
            attrs: *mut attr_t,
    85
            attrs: *mut attr_t,
    86
            pair: *mut libc::c_short,
    86
            pair: *mut libc::c_short,
    87
            opts: *mut libc::c_void,
    87
            opts: *mut libc::c_void,

    String formatting

    robotfindskitten calls several printf-style variable-argument functions to perform string formatting. Since variable-argument function calls are considered unsafe in Rust, we must replace these with Rust-style string formatting using format! and related macros. Specifically, for each string formatting function such as printf, we will create a safe wrapper fmt_printf that takes a Rust fmt::Arguments object, and replace printf(...) calls with fmt_printf(format_args!(...)). This approach isolates all the unsafety into the fmt_printf wrapper, where it can be eliminated by later passes.

    The replacement itself happens in two steps. First, we convert printf calls from printf(<C format args...>) to printf(format_args!(<Rust format args...>)). Note that the code does not typecheck in this intermediate state: C's printf function cannot accept the std::fmt::Arguments produced by the format_args! macro. The second step then replaces the printf call with a call to the fmt_printf wrapper, which does accept std::fmt::Arguments.

    printf format argument conversion

    We run a few commands to mark the nodes involved in string formatting, before finally running the convert_format_args command to perform the actual transformation.

    First, we use select and mark_arg_uses to mark the first argument of every printf call as targets:

    select target 'item(printf);' ;
    mark_arg_uses 0 target ;
    

    Diff #5

    src/robotfindskitten.rs
    977
    unsafe extern "C" fn finish(mut sig: libc::c_int) {
    977
    unsafe extern "C" fn finish(mut sig: libc::c_int) {
    978
        endwin();
    978
        endwin();
    979
        printf(
    979
        printf(
    980
            b"%c%c%c\x00" as *const u8 as *const libc::c_char,
    980
            b"%c%c%c\x00" as *const u8 as *const libc::c_char,
    981
            27i32,
    981
            27i32,
    982
            '(' as i32,
    982
            '(' as i32,
    983
            'B' as i32,
    983
            'B' as i32,
    984
        );
    984
        );
    985
        exit(0i32);
    985
        exit(0i32);

    1419
                        .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
    1419
                        .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
    1420
            {
    1420
            {
    1421
                printf(
    1421
                printf(
    1422
                    b"Run-time parameter must be between 0 and %d.\n\x00" as *const u8
    1422
                    b"Run-time parameter must be between 0 and %d.\n\x00" as *const u8
    1423
                        as *const libc::c_char,
    1423
                        as *const libc::c_char,
    1424
                    (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    1424
                    (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    1425
                        .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong),
    1425
                        .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong),
    1426
                );
    1426
                );
    1427
                exit(0i32);
    1427
                exit(0i32);

    1429
        }
    1429
        }
    1430
        srand(time(0 as *mut time_t) as libc::c_uint);
    1430
        srand(time(0 as *mut time_t) as libc::c_uint);
    1431
        printf(
    1431
        printf(
    1432
            b"%c%c%c\x00" as *const u8 as *const libc::c_char,
    1432
            b"%c%c%c\x00" as *const u8 as *const libc::c_char,
    1433
            27i32,
    1433
            27i32,
    1434
            '(' as i32,
    1434
            '(' as i32,
    1435
            'U' as i32,
    1435
            'U' as i32,
    1436
        );
    1436
        );
    1437
        initialize_ncurses();
    1437
        initialize_ncurses();

    convert_format_args will treat the target argument at each call site as a printf-style format string, and will treat all later arguments as format args.

    Next, we mark the format string literal with fmt_str, which tells convert_format_args the exact string literal it should use as the format string. This usually is not the same as the target argument, since c2rust-transpile inserts several casts to turn a Rust string literal into a *const libc::c_char.

    select fmt_str 'marked(target); desc(expr && !match_expr(__e as __t));' ;
    

    Diff #6

    src/robotfindskitten.rs
    977
    unsafe extern "C" fn finish(mut sig: libc::c_int) {
    977
    unsafe extern "C" fn finish(mut sig: libc::c_int) {
    978
        endwin();
    978
        endwin();
    979
        printf(
    979
        printf(
    980
            b"%c%c%c\x00" as *const u8 as *const libc::c_char,
    980
            b"%c%c%c\x00" as *const u8 as *const libc::c_char,
    981
            27i32,
    981
            27i32,
    982
            '(' as i32,
    982
            '(' as i32,
    983
            'B' as i32,
    983
            'B' as i32,
    984
        );
    984
        );
    985
        exit(0i32);
    985
        exit(0i32);

    1419
                        .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
    1419
                        .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
    1420
            {
    1420
            {
    1421
                printf(
    1421
                printf(
    1422
                    b"Run-time parameter must be between 0 and %d.\n\x00" as *const u8
    1422
                    b"Run-time parameter must be between 0 and %d.\n\x00" as *const u8
    1423
                        as *const libc::c_char,
    1423
                        as *const libc::c_char,
    1424
                    (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    1424
                    (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    1425
                        .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong),
    1425
                        .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong),
    1426
                );
    1426
                );
    1427
                exit(0i32);
    1427
                exit(0i32);

    1429
        }
    1429
        }
    1430
        srand(time(0 as *mut time_t) as libc::c_uint);
    1430
        srand(time(0 as *mut time_t) as libc::c_uint);
    1431
        printf(
    1431
        printf(
    1432
            b"%c%c%c\x00" as *const u8 as *const libc::c_char,
    1432
            b"%c%c%c\x00" as *const u8 as *const libc::c_char,
    1433
            27i32,
    1433
            27i32,
    1434
            '(' as i32,
    1434
            '(' as i32,
    1435
            'U' as i32,
    1435
            'U' as i32,
    1436
        );
    1436
        );
    1437
        initialize_ncurses();
    1437
        initialize_ncurses();

    With both target and fmt_str marks in place, we can apply the actual transformation:

    convert_format_args ;
    

    Diff #7

    src/robotfindskitten.rs
    974
            );
    974
            );
    975
        };
    975
        };
    976
    }
    976
    }
    977
    unsafe extern "C" fn finish(mut sig: libc::c_int) {
    977
    unsafe extern "C" fn finish(mut sig: libc::c_int) {
    978
        endwin();
    978
        endwin();
    979
        printf(
    979
        printf(format_args!(
    980
            b"%c%c%c\x00" as *const u8 as *const libc::c_char,
    980
            "{:}{:}{:}",
    981
            27i32,
    981
            27i32 as u8 as char, '(' as i32 as u8 as char, 'B' as i32 as u8 as char
    982
            '(' as i32,
    983
            'B' as i32,
    984
        );
    982
        ));
    985
        exit(0i32);
    983
        exit(0i32);
    986
    }
    984
    }
    987
    #[no_mangle]
    985
    #[no_mangle]
    988
    pub unsafe extern "C" fn initialize_arrays() {
    986
    pub unsafe extern "C" fn initialize_arrays() {
    989
        let mut counter: libc::c_int = 0;
    987
        let mut counter: libc::c_int = 0;

    1416
            if num_bogus < 0i32
    1414
            if num_bogus < 0i32
    1417
                || num_bogus as libc::c_ulong
    1415
                || num_bogus as libc::c_ulong
    1418
                    > (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    1416
                    > (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    1419
                        .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
    1417
                        .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
    1420
            {
    1418
            {
    1421
                printf(
    1419
                printf(format_args!(
    1422
                    b"Run-time parameter must be between 0 and %d.\n\x00" as *const u8
    1420
                    "Run-time parameter must be between 0 and {:}.\n",
    1423
                        as *const libc::c_char,
    1424
                    (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    1421
                    (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    1425
                        .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong),
    1422
                        .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
    1423
                        as libc::c_int
    1426
                );
    1424
                ));
    1427
                exit(0i32);
    1425
                exit(0i32);
    1428
            }
    1426
            }
    1429
        }
    1427
        }
    1430
        srand(time(0 as *mut time_t) as libc::c_uint);
    1428
        srand(time(0 as *mut time_t) as libc::c_uint);
    1431
        printf(
    1429
        printf(format_args!(
    1432
            b"%c%c%c\x00" as *const u8 as *const libc::c_char,
    1430
            "{:}{:}{:}",
    1433
            27i32,
    1431
            27i32 as u8 as char, '(' as i32 as u8 as char, 'U' as i32 as u8 as char
    1434
            '(' as i32,
    1435
            'U' as i32,
    1436
        );
    1432
        ));
    1437
        initialize_ncurses();
    1433
        initialize_ncurses();
    1438
        initialize_arrays();
    1434
        initialize_arrays();
    1439
        initialize_robot();
    1435
        initialize_robot();
    1440
        initialize_kitten();
    1436
        initialize_kitten();
    1441
        initialize_bogus();
    1437
        initialize_bogus();

    Finally, we clean up from this step by clearing all the marks.

    clear_marks ;
    

    commit would also clear the marks, but we don't want to commit these changes until we've fixed the type errors introduced in this step.

    Creating a printf wrapper

    As a reminder, we currently have code that looks like this:

    printf(format_args!("Hello, {}!\n", "world"))
    

    printf itself can't accept the std::fmt::Arguments returned by format_args!, so we will define a wrapper that does accept std::fmt::Arguments and then rewrite these printf calls to call the wrapper instead.

    First, we insert the wrapper:

    select target 'crate; child(foreign_mod); last;' ;
    create_item
        '
            fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
                print!("{}", args);
                0
            }
        '
        after ;
    

    Diff #9

    src/robotfindskitten.rs
    79
        #[no_mangle]
    79
        #[no_mangle]
    80
        fn sleep(__seconds: libc::c_uint) -> libc::c_uint;
    80
        fn sleep(__seconds: libc::c_uint) -> libc::c_uint;
    81
    }
    81
    }
    82
    extern "C" {
    82
    extern "C" {
    83
        fn wattr_get(
    83
        fn wattr_get(
    84
            win: *mut WINDOW,
    84
            win: *mut WINDOW,
    85
            attrs: *mut attr_t,
    85
            attrs: *mut attr_t,
    86
            pair: *mut libc::c_short,
    86
            pair: *mut libc::c_short,
    87
            opts: *mut libc::c_void,
    87
            opts: *mut libc::c_void,
    88
        ) -> libc::c_int;
    88
        ) -> libc::c_int;
    89
        fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
    89
        fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
    90
    }
    90
    }
    91
    fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
    92
        print!("{}", args);
    93
        0
    94
    }
    91
    pub type __time_t = libc::c_long;
    95
    pub type __time_t = libc::c_long;
    92
    pub type chtype = libc::c_ulong;
    96
    pub type chtype = libc::c_ulong;
    93
    97
    94
    #[repr(C)]
    98
    #[repr(C)]
    95
    #[derive(Copy, Clone)]
    99
    #[derive(Copy, Clone)]

    Since Rust provides a print! macro with similar functionality to printf, our "wrapper" actually just calls print! directly, avoiding the string conversions necessary to call the actual C printf. (See the next subsection for an example of a "real" wrapper function.)

    With the wrapper in place, we can now update the call sites:

    rewrite_expr 'printf' 'fmt_printf' ;
    

    Diff #10

    src/robotfindskitten.rs
    978
            );
    978
            );
    979
        };
    979
        };
    980
    }
    980
    }
    981
    unsafe extern "C" fn finish(mut sig: libc::c_int) {
    981
    unsafe extern "C" fn finish(mut sig: libc::c_int) {
    982
        endwin();
    982
        endwin();
    983
        printf(format_args!(
    983
        fmt_printf(format_args!(
    984
            "{:}{:}{:}",
    984
            "{:}{:}{:}",
    985
            27i32 as u8 as char, '(' as i32 as u8 as char, 'B' as i32 as u8 as char
    985
            27i32 as u8 as char, '(' as i32 as u8 as char, 'B' as i32 as u8 as char
    986
        ));
    986
        ));
    987
        exit(0i32);
    987
        exit(0i32);
    988
    }
    988
    }

    1418
            if num_bogus < 0i32
    1418
            if num_bogus < 0i32
    1419
                || num_bogus as libc::c_ulong
    1419
                || num_bogus as libc::c_ulong
    1420
                    > (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    1420
                    > (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    1421
                        .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
    1421
                        .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
    1422
            {
    1422
            {
    1423
                printf(format_args!(
    1423
                fmt_printf(format_args!(
    1424
                    "Run-time parameter must be between 0 and {:}.\n",
    1424
                    "Run-time parameter must be between 0 and {:}.\n",
    1425
                    (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    1425
                    (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    1426
                        .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
    1426
                        .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
    1427
                        as libc::c_int
    1427
                        as libc::c_int
    1428
                ));
    1428
                ));
    1429
                exit(0i32);
    1429
                exit(0i32);
    1430
            }
    1430
            }
    1431
        }
    1431
        }
    1432
        srand(time(0 as *mut time_t) as libc::c_uint);
    1432
        srand(time(0 as *mut time_t) as libc::c_uint);
    1433
        printf(format_args!(
    1433
        fmt_printf(format_args!(
    1434
            "{:}{:}{:}",
    1434
            "{:}{:}{:}",
    1435
            27i32 as u8 as char, '(' as i32 as u8 as char, 'U' as i32 as u8 as char
    1435
            27i32 as u8 as char, '(' as i32 as u8 as char, 'U' as i32 as u8 as char
    1436
        ));
    1436
        ));
    1437
        initialize_ncurses();
    1437
        initialize_ncurses();
    1438
        initialize_arrays();
    1438
        initialize_arrays();

    Now that we've finished this step and the crate typechecks again, we can safely commit the changes:

    commit ;
    

    Diff #11

    src/robotfindskitten.rs
    79
        #[no_mangle]
    79
        #[no_mangle]
    80
        fn sleep(__seconds: libc::c_uint) -> libc::c_uint;
    80
        fn sleep(__seconds: libc::c_uint) -> libc::c_uint;
    81
    }
    81
    }
    82
    extern "C" {
    82
    extern "C" {
    83
        fn wattr_get(
    83
        fn wattr_get(
    84
            win: *mut WINDOW,
    84
            win: *mut WINDOW,
    85
            attrs: *mut attr_t,
    85
            attrs: *mut attr_t,
    86
            pair: *mut libc::c_short,
    86
            pair: *mut libc::c_short,
    87
            opts: *mut libc::c_void,
    87
            opts: *mut libc::c_void,
    88
        ) -> libc::c_int;
    88
        ) -> libc::c_int;
    89
        fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
    89
        fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
    90
    }
    90
    }
    91
    fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
    91
    fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
    92
        print!("{}", args);
    92
        print!("{}", args);
    93
        0
    93
        0
    94
    }
    94
    }
    95
    pub type __time_t = libc::c_long;
    95
    pub type __time_t = libc::c_long;
    96
    pub type chtype = libc::c_ulong;
    96
    pub type chtype = libc::c_ulong;

    Other string formatting functions

    Aside from printf, robotfindskitten also uses the ncurses printw and mvprintw string-formatting functions. The refactoring script for printw is similar to the previous two steps combined:

    select target 'item(printw);' ;
    mark_arg_uses 0 target ;
    select fmt_str 'marked(target); desc(expr && !match_expr(__e as __t));' ;
    
    convert_format_args ;
    
    clear_marks ;
    
    select target 'crate; child(foreign_mod); last;' ;
    create_item
        '
            fn fmt_printw(args: ::std::fmt::Arguments) -> libc::c_int {
                unsafe {
                    ::printw(b"%s\0" as *const u8 as *const libc::c_char,
                             ::std::ffi::CString::new(format!("{}", args))
                                 .unwrap().as_ptr())
                }
            }
        '
        after ;
    rewrite_expr 'printw' 'fmt_printw' ;
    commit ;
    

    Diff #12

    src/robotfindskitten.rs
    86
            pair: *mut libc::c_short,
    86
            pair: *mut libc::c_short,
    87
            opts: *mut libc::c_void,
    87
            opts: *mut libc::c_void,
    88
        ) -> libc::c_int;
    88
        ) -> libc::c_int;
    89
        fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
    89
        fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
    90
    }
    90
    }
    91
    fn fmt_printw(args: ::std::fmt::Arguments) -> libc::c_int {
    92
        unsafe {
    93
            ::printw(
    94
                b"%s\0" as *const u8 as *const libc::c_char,
    95
                ::std::ffi::CString::new(format!("{}", args))
    96
                    .unwrap()
    97
                    .as_ptr(),
    98
            )
    99
        }
    100
    }
    91
    fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
    101
    fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
    92
        print!("{}", args);
    102
        print!("{}", args);
    93
        0
    103
        0
    94
    }
    104
    }
    95
    pub type __time_t = libc::c_long;
    105
    pub type __time_t = libc::c_long;

    1161
            b"robotfindskitten v%s\n\n\x00" as *const u8 as *const libc::c_char,
    1171
            b"robotfindskitten v%s\n\n\x00" as *const u8 as *const libc::c_char,
    1162
            ver,
    1172
            ver,
    1163
        );
    1173
        );
    1164
        counter = 0i32;
    1174
        counter = 0i32;
    1165
        while counter <= COLS - 1i32 {
    1175
        while counter <= COLS - 1i32 {
    1166
            printw(b"%c\x00" as *const u8 as *const libc::c_char, 95i32);
    1176
            fmt_printw(format_args!("{:}", 95i32 as u8 as char));
    1167
            counter += 1
    1177
            counter += 1
    1168
        }
    1178
        }
    1169
        counter = 0i32;
    1179
        counter = 0i32;
    1170
        while counter < num_bogus {
    1180
        while counter < num_bogus {
    1171
            draw(bogus[counter as usize]);
    1181
            draw(bogus[counter as usize]);

    1206
        if o.bold {
    1216
        if o.bold {
    1207
            new |= 1u64 << 13i32 + 8i32
    1217
            new |= 1u64 << 13i32 + 8i32
    1208
        }
    1218
        }
    1209
        wattrset(stdscr, new as libc::c_int);
    1219
        wattrset(stdscr, new as libc::c_int);
    1210
        if in_place {
    1220
        if in_place {
    1211
            printw(
    1221
            fmt_printw(format_args!(
    1212
                b"%c\x00" as *const u8 as *const libc::c_char,
    1222
                "{:}",
    1213
                o.character as libc::c_int,
    1223
                o.character as libc::c_int as u8 as char
    1214
            );
    1224
            ));
    1215
        } else {
    1225
        } else {
    1216
            mvprintw(
    1226
            mvprintw(
    1217
                o.y,
    1227
                o.y,
    1218
                o.x,
    1228
                o.x,
    1219
                b"%c\x00" as *const u8 as *const libc::c_char,
    1229
                b"%c\x00" as *const u8 as *const libc::c_char,

    1230
            0i32,
    1240
            0i32,
    1231
            0i32,
    1241
            0i32,
    1232
            b"robotfindskitten v%s\n\x00" as *const u8 as *const libc::c_char,
    1242
            b"robotfindskitten v%s\n\x00" as *const u8 as *const libc::c_char,
    1233
            ver,
    1243
            ver,
    1234
        );
    1244
        );
    1235
        printw(
    1245
        fmt_printw(format_args!(
    1236
            b"By the illustrious Leonard Richardson (C) 1997, 2000\n\x00" as *const u8
    1246
            "By the illustrious Leonard Richardson (C) 1997, 2000\n"
    1237
                as *const libc::c_char,
    1238
        );
    1247
        ));
    1239
        printw(
    1248
        fmt_printw(format_args!(
    1240
            b"Written originally for the Nerth Pork robotfindskitten contest\n\n\x00" as *const u8
    1249
            "Written originally for the Nerth Pork robotfindskitten contest\n\n"
    1241
                as *const libc::c_char,
    1242
        );
    1250
        ));
    1243
        printw(b"In this game, you are robot (\x00" as *const u8 as *const libc::c_char);
    1251
        fmt_printw(format_args!("In this game, you are robot ("));
    1244
        draw_in_place(robot);
    1252
        draw_in_place(robot);
    1245
        printw(b"). Your job is to find kitten. This task\n\x00" as *const u8 as *const libc::c_char);
    1253
        fmt_printw(format_args!("). Your job is to find kitten. This task\n"));
    1246
        printw(
    1254
        fmt_printw(format_args!(
    1247
            b"is complicated by the existence of various things which are not kitten.\n\x00"
    1255
            "is complicated by the existence of various things which are not kitten.\n"
    1248
                as *const u8 as *const libc::c_char,
    1249
        );
    1256
        ));
    1250
        printw(
    1257
        fmt_printw(format_args!(
    1251
            b"Robot must touch items to determine if they are kitten or not. The game\n\x00"
    1258
            "Robot must touch items to determine if they are kitten or not. The game\n"
    1252
                as *const u8 as *const libc::c_char,
    1253
        );
    1259
        ));
    1254
        printw(
    1260
        fmt_printw(format_args!(
    1255
            b"ends when robotfindskitten. Alternatively, you may end the game by hitting\n\x00"
    1261
            "ends when robotfindskitten. Alternatively, you may end the game by hitting\n"
    1256
                as *const u8 as *const libc::c_char,
    1257
        );
    1262
        ));
    1258
        printw(
    1263
        fmt_printw(format_args!(
    1259
            b"the Esc key. See the documentation for more information.\n\n\x00" as *const u8
    1264
            "the Esc key. See the documentation for more information.\n\n"
    1260
                as *const libc::c_char,
    1261
        );
    1265
        ));
    1262
        printw(b"Press any key to start.\n\x00" as *const u8 as *const libc::c_char);
    1266
        fmt_printw(format_args!("Press any key to start.\n"));
    1263
        wrefresh(stdscr);
    1267
        wrefresh(stdscr);
    1264
        dummy = wgetch(stdscr) as libc::c_char;
    1268
        dummy = wgetch(stdscr) as libc::c_char;
    1265
        wclear(stdscr);
    1269
        wclear(stdscr);
    1266
    }
    1270
    }
    1267
    #[no_mangle]
    1271
    #[no_mangle]

    Aside from replacing the name printf with printw, the other notable difference from the printf script is the body of fmt_printw. There is no convenient replacement for printw in the Rust standard library, so instead we call the original printw function, passing in the result of Rust string formatting (converted to a C string) as an argument.

    The mvprintw replacement is also similar, just with a few extra arguments:

    select target 'item(mvprintw);' ;
    mark_arg_uses 2 target ;
    select fmt_str 'marked(target); desc(expr && !match_expr(__e as __t));' ;
    
    convert_format_args ;
    
    clear_marks ;
    
    select target 'crate; child(foreign_mod); last;' ;
    create_item
        '
            fn fmt_mvprintw(y: libc::c_int, x: libc::c_int,
                            args: ::std::fmt::Arguments) -> libc::c_int {
                unsafe {
                    ::mvprintw(y, x, b"%s\0" as *const u8 as *const libc::c_char,
                             ::std::ffi::CString::new(format!("{}", args))
                                 .unwrap().as_ptr())
                }
            }
        '
        after ;
    rewrite_expr 'mvprintw' 'fmt_mvprintw' ;
    commit ;
    

    Diff #13

    src/robotfindskitten.rs
    86
            pair: *mut libc::c_short,
    86
            pair: *mut libc::c_short,
    87
            opts: *mut libc::c_void,
    87
            opts: *mut libc::c_void,
    88
        ) -> libc::c_int;
    88
        ) -> libc::c_int;
    89
        fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
    89
        fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
    90
    }
    90
    }
    91
    fn fmt_mvprintw(y: libc::c_int, x: libc::c_int, args: ::std::fmt::Arguments) -> libc::c_int {
    92
        unsafe {
    93
            ::mvprintw(
    94
                y,
    95
                x,
    96
                b"%s\0" as *const u8 as *const libc::c_char,
    97
                ::std::ffi::CString::new(format!("{}", args))
    98
                    .unwrap()
    99
                    .as_ptr(),
    100
            )
    101
        }
    102
    }
    91
    fn fmt_printw(args: ::std::fmt::Arguments) -> libc::c_int {
    103
    fn fmt_printw(args: ::std::fmt::Arguments) -> libc::c_int {
    92
        unsafe {
    104
        unsafe {
    93
            ::printw(
    105
            ::printw(
    94
                b"%s\0" as *const u8 as *const libc::c_char,
    106
                b"%s\0" as *const u8 as *const libc::c_char,
    95
                ::std::ffi::CString::new(format!("{}", args))
    107
                ::std::ffi::CString::new(format!("{}", args))

    1163
    #[no_mangle]
    1175
    #[no_mangle]
    1164
    pub static mut num_bogus: libc::c_int = 0;
    1176
    pub static mut num_bogus: libc::c_int = 0;
    1165
    #[no_mangle]
    1177
    #[no_mangle]
    1166
    pub unsafe extern "C" fn initialize_screen() {
    1178
    pub unsafe extern "C" fn initialize_screen() {
    1167
        let mut counter: libc::c_int = 0;
    1179
        let mut counter: libc::c_int = 0;
    1168
        mvprintw(
    1180
        fmt_mvprintw(
    1169
            0i32,
    1181
            0i32,
    1170
            0i32,
    1182
            0i32,
    1171
            b"robotfindskitten v%s\n\n\x00" as *const u8 as *const libc::c_char,
    1183
            format_args!("robotfindskitten v{:}\n\n", unsafe {
    1184
                ::std::ffi::CStr::from_ptr(ver as *const libc::c_char)
    1185
                    .to_str()
    1186
                    .unwrap()
    1172
            ver,
    1187
            }),
    1173
        );
    1188
        );
    1174
        counter = 0i32;
    1189
        counter = 0i32;
    1175
        while counter <= COLS - 1i32 {
    1190
        while counter <= COLS - 1i32 {
    1176
            fmt_printw(format_args!("{:}", 95i32 as u8 as char));
    1191
            fmt_printw(format_args!("{:}", 95i32 as u8 as char));
    1177
            counter += 1
    1192
            counter += 1

    1221
            fmt_printw(format_args!(
    1236
            fmt_printw(format_args!(
    1222
                "{:}",
    1237
                "{:}",
    1223
                o.character as libc::c_int as u8 as char
    1238
                o.character as libc::c_int as u8 as char
    1224
            ));
    1239
            ));
    1225
        } else {
    1240
        } else {
    1226
            mvprintw(
    1241
            fmt_mvprintw(
    1227
                o.y,
    1242
                o.y,
    1228
                o.x,
    1243
                o.x,
    1229
                b"%c\x00" as *const u8 as *const libc::c_char,
    1244
                format_args!("{:}", o.character as libc::c_int as u8 as char),
    1230
                o.character as libc::c_int,
    1231
            );
    1245
            );
    1232
            wmove(stdscr, o.y, o.x);
    1246
            wmove(stdscr, o.y, o.x);
    1233
        }
    1247
        }
    1234
        wattrset(stdscr, old as libc::c_int);
    1248
        wattrset(stdscr, old as libc::c_int);
    1235
    }
    1249
    }
    1236
    #[no_mangle]
    1250
    #[no_mangle]
    1237
    pub unsafe extern "C" fn instructions() {
    1251
    pub unsafe extern "C" fn instructions() {
    1238
        let mut dummy: libc::c_char = 0;
    1252
        let mut dummy: libc::c_char = 0;
    1239
        mvprintw(
    1253
        fmt_mvprintw(
    1240
            0i32,
    1254
            0i32,
    1241
            0i32,
    1255
            0i32,
    1242
            b"robotfindskitten v%s\n\x00" as *const u8 as *const libc::c_char,
    1256
            format_args!("robotfindskitten v{:}\n", unsafe {
    1257
                ::std::ffi::CStr::from_ptr(ver as *const libc::c_char)
    1258
                    .to_str()
    1259
                    .unwrap()
    1243
            ver,
    1260
            }),
    1244
        );
    1261
        );
    1245
        fmt_printw(format_args!(
    1262
        fmt_printw(format_args!(
    1246
            "By the illustrious Leonard Richardson (C) 1997, 2000\n"
    1263
            "By the illustrious Leonard Richardson (C) 1997, 2000\n"
    1247
        ));
    1264
        ));
    1248
        fmt_printw(format_args!(
    1265
        fmt_printw(format_args!(

    1301
    }
    1318
    }
    1302
    #[no_mangle]
    1319
    #[no_mangle]
    1303
    pub unsafe extern "C" fn message(mut message_0: *mut libc::c_char) {
    1320
    pub unsafe extern "C" fn message(mut message_0: *mut libc::c_char) {
    1304
        wmove(stdscr, 1i32, 0i32);
    1321
        wmove(stdscr, 1i32, 0i32);
    1305
        wclrtoeol(stdscr);
    1322
        wclrtoeol(stdscr);
    1306
        mvprintw(
    1323
        fmt_mvprintw(
    1307
            1i32,
    1324
            1i32,
    1308
            0i32,
    1325
            0i32,
    1309
            b"%.*s\x00" as *const u8 as *const libc::c_char,
    1326
            format_args!("{:.*}", COLS as usize, unsafe {
    1327
                ::std::ffi::CStr::from_ptr(message_0 as *const libc::c_char)
    1328
                    .to_str()
    1329
                    .unwrap()
    1310
            COLS,
    1330
            }),
    1311
            message_0,
    1312
        );
    1331
        );
    1313
        wmove(stdscr, robot.y, robot.x);
    1332
        wmove(stdscr, robot.y, robot.x);
    1314
        wrefresh(stdscr);
    1333
        wrefresh(stdscr);
    1315
    }
    1334
    }
    1316
    #[no_mangle]
    1335
    #[no_mangle]

    Static string constant - ver

    robotfindskitten defines a static string constant, ver, to store the game's version. Using ver is currently unsafe, first because its Rust type is a raw pointer (*mut c_char), and second because it's mutable. To make ver usage safe, we first change its type to &'static str (and fix up the resulting type errors), and then we change it from a static mut to an ordinary immutable static. Note that we must change the type first because Rust does not allow raw pointers to be stored in safe (non-mut) statics.

    We change the type using rewrite_ty:

    select target 'item(ver); child(ty);' ;
    rewrite_ty 'marked!(*mut libc::c_char)' "&'static str" ;
    delete_marks target ;
    

    Diff #14

    src/robotfindskitten.rs
    203
        pub y: libc::c_int,
    203
        pub y: libc::c_int,
    204
        pub color: libc::c_int,
    204
        pub color: libc::c_int,
    205
        pub bold: bool,
    205
        pub bold: bool,
    206
        pub character: libc::c_char,
    206
        pub character: libc::c_char,
    207
    }
    207
    }
    208
    static mut ver: *mut libc::c_char =
    208
    static mut ver: &'static str =
    209
        b"1.7320508.406\x00" as *const u8 as *const libc::c_char as *mut libc::c_char;
    209
        b"1.7320508.406\x00" as *const u8 as *const libc::c_char as *mut libc::c_char;
    210
    #[inline]
    210
    #[inline]
    211
    unsafe extern "C" fn atoi(mut __nptr: *const libc::c_char) -> libc::c_int {
    211
    unsafe extern "C" fn atoi(mut __nptr: *const libc::c_char) -> libc::c_int {
    212
        return strtol(
    212
        return strtol(
    213
            __nptr,
    213
            __nptr,

    The combination of select and the marked! matching form ensures that only ver's type annotation is modified. We delete the mark afterward, since it's no longer needed.

    Simply replacing *mut c_char with &str introduces type errors throughout the crate. The initializer for ver still has type *mut c_char, and all uses of ver are still expecting a *mut c_char.

    Fixing ver's initializer

    Fixing the ver initializer is straightforward: we simply remove all the casts, then convert the binary string (&[u8]) literal to an ordinary string literal. For the casts, we mark all cast expressions in ver's definition, then replace each one with its subexpression:

    select target 'item(ver); desc(match_expr(__e as __t));' ;
    rewrite_expr 'marked!(__e as __t)' '__e' ;
    delete_marks target ;
    

    Diff #15

    src/robotfindskitten.rs
    203
        pub y: libc::c_int,
    203
        pub y: libc::c_int,
    204
        pub color: libc::c_int,
    204
        pub color: libc::c_int,
    205
        pub bold: bool,
    205
        pub bold: bool,
    206
        pub character: libc::c_char,
    206
        pub character: libc::c_char,
    207
    }
    207
    }
    208
    static mut ver: &'static str =
    208
    static mut ver: &'static str = b"1.7320508.406\x00";
    209
        b"1.7320508.406\x00" as *const u8 as *const libc::c_char as *mut libc::c_char;
    210
    #[inline]
    209
    #[inline]
    211
    unsafe extern "C" fn atoi(mut __nptr: *const libc::c_char) -> libc::c_int {
    210
    unsafe extern "C" fn atoi(mut __nptr: *const libc::c_char) -> libc::c_int {
    212
        return strtol(
    211
        return strtol(
    213
            __nptr,
    212
            __nptr,
    214
            0 as *mut libc::c_void as *mut *mut libc::c_char,
    213
            0 as *mut libc::c_void as *mut *mut libc::c_char,

    Only the binary string literal remains, so we mark it and change it to an ordinary str:

    select target 'item(ver); child(expr);' ;
    bytestr_to_str ;
    delete_marks target ;
    

    Diff #16

    src/robotfindskitten.rs
    203
        pub y: libc::c_int,
    203
        pub y: libc::c_int,
    204
        pub color: libc::c_int,
    204
        pub color: libc::c_int,
    205
        pub bold: bool,
    205
        pub bold: bool,
    206
        pub character: libc::c_char,
    206
        pub character: libc::c_char,
    207
    }
    207
    }
    208
    static mut ver: &'static str = b"1.7320508.406\x00";
    208
    static mut ver: &'static str = "1.7320508.406\u{0}";
    209
    #[inline]
    209
    #[inline]
    210
    unsafe extern "C" fn atoi(mut __nptr: *const libc::c_char) -> libc::c_int {
    210
    unsafe extern "C" fn atoi(mut __nptr: *const libc::c_char) -> libc::c_int {
    211
        return strtol(
    211
        return strtol(
    212
            __nptr,
    212
            __nptr,
    213
            0 as *mut libc::c_void as *mut *mut libc::c_char,
    213
            0 as *mut libc::c_void as *mut *mut libc::c_char,

    Fixing ver's uses

    ver's initializer is now well-typed, but its uses are still expecting a *mut c_char instead of a &str. To fix these up, we use the type_fix_rules command, which rewrites expressions anywhere a type error occurs:

    type_fix_rules '*, &str, *const __t => __old.as_ptr()' ;
    

    Diff #17

    src/robotfindskitten.rs
    1178
        let mut counter: libc::c_int = 0;
    1178
        let mut counter: libc::c_int = 0;
    1179
        fmt_mvprintw(
    1179
        fmt_mvprintw(
    1180
            0i32,
    1180
            0i32,
    1181
            0i32,
    1181
            0i32,
    1182
            format_args!("robotfindskitten v{:}\n\n", unsafe {
    1182
            format_args!("robotfindskitten v{:}\n\n", unsafe {
    1183
                ::std::ffi::CStr::from_ptr(ver as *const libc::c_char)
    1183
                ::std::ffi::CStr::from_ptr(ver.as_ptr() as *const libc::c_char)
    1184
                    .to_str()
    1184
                    .to_str()
    1185
                    .unwrap()
    1185
                    .unwrap()
    1186
            }),
    1186
            }),
    1187
        );
    1187
        );
    1188
        counter = 0i32;
    1188
        counter = 0i32;

    1251
        let mut dummy: libc::c_char = 0;
    1251
        let mut dummy: libc::c_char = 0;
    1252
        fmt_mvprintw(
    1252
        fmt_mvprintw(
    1253
            0i32,
    1253
            0i32,
    1254
            0i32,
    1254
            0i32,
    1255
            format_args!("robotfindskitten v{:}\n", unsafe {
    1255
            format_args!("robotfindskitten v{:}\n", unsafe {
    1256
                ::std::ffi::CStr::from_ptr(ver as *const libc::c_char)
    1256
                ::std::ffi::CStr::from_ptr(ver.as_ptr() as *const libc::c_char)
    1257
                    .to_str()
    1257
                    .to_str()
    1258
                    .unwrap()
    1258
                    .unwrap()
    1259
            }),
    1259
            }),
    1260
        );
    1260
        );
    1261
        fmt_printw(format_args!(
    1261
        fmt_printw(format_args!(

    Here we run type_fix_rules with only one rule: in any position (*), if an expression has type &str but is expected to have a raw pointer type (*const __t), then wrap the original expression in a call to .as_ptr(). This turns out to be enough to fix all the errors at uses of ver.

    Making ver immutable

    Now that all type errors have been corrected, we can finish our refactoring of ver. We make it immutable, then commit the changes.

    select target 'item(ver);' ;
    set_mutability imm ;
    
    commit ;
    

    Diff #18

    src/robotfindskitten.rs
    203
        pub y: libc::c_int,
    203
        pub y: libc::c_int,
    204
        pub color: libc::c_int,
    204
        pub color: libc::c_int,
    205
        pub bold: bool,
    205
        pub bold: bool,
    206
        pub character: libc::c_char,
    206
        pub character: libc::c_char,
    207
    }
    207
    }
    208
    static mut ver: &'static str = "1.7320508.406\u{0}";
    208
    static ver: &'static str = "1.7320508.406\u{0}";
    209
    #[inline]
    209
    #[inline]
    210
    unsafe extern "C" fn atoi(mut __nptr: *const libc::c_char) -> libc::c_int {
    210
    unsafe extern "C" fn atoi(mut __nptr: *const libc::c_char) -> libc::c_int {
    211
        return strtol(
    211
        return strtol(
    212
            __nptr,
    212
            __nptr,
    213
            0 as *mut libc::c_void as *mut *mut libc::c_char,
    213
            0 as *mut libc::c_void as *mut *mut libc::c_char,

    Static string array - messages

    Aside from ver, robotfindskitten contains a static array of strings, called messages. Like ver, accessing messages is unsafe because each element is a raw *mut c_char pointer and because messages itself is a static mut.

    We rewrite the type and initializer of messages using the same strategy as for ver:

    select target 'item(messages); child(ty); desc(ty);' ;
    rewrite_ty 'marked!(*mut libc::c_char)' "&'static str" ;
    delete_marks target ;
    select target 'item(messages); child(expr); desc(expr);' ;
    rewrite_expr 'marked!(__e as __t)' '__e' ;
    bytestr_to_str ;
    delete_marks target ;
    

    Diff #19

    src/robotfindskitten.rs
    218
    will happen.*/
    218
    will happen.*/
    219
    /*Also, take note that robotfindskitten.c and configure.in
    219
    /*Also, take note that robotfindskitten.c and configure.in
    220
    currently have the version number hardcoded into them, and they
    220
    currently have the version number hardcoded into them, and they
    221
    should reflect MESSAGES. */
    221
    should reflect MESSAGES. */
    222
    /* Watch out for fenceposts.*/
    222
    /* Watch out for fenceposts.*/
    223
    static mut messages: [*mut libc::c_char; 406] = [
    223
    static mut messages: [&'static str; 406] = [
    224
        b"\"I pity the fool who mistakes me for kitten!\", sez Mr. T.\x00" as *const u8
    224
        "\"I pity the fool who mistakes me for kitten!\", sez Mr. T.\u{0}",
    225
            as *const libc::c_char as *mut libc::c_char,
    225
        "That\'s just an old tin can.\u{0}",
    226
        b"That\'s just an old tin can.\x00" as *const u8 as *const libc::c_char as *mut libc::c_char,
    226
        "It\'s an altar to the horse god.\u{0}",
    227
        b"It\'s an altar to the horse god.\x00" as *const u8 as *const libc::c_char
    228
            as *mut libc::c_char,
    229
        b"A box of dancing mechanical pencils. They dance! They sing!\x00" as *const u8
    227
        "A box of dancing mechanical pencils. They dance! They sing!\u{0}",
    230
            as *const libc::c_char as *mut libc::c_char,
    228
        "It\'s an old Duke Ellington record.\u{0}",
    231
        b"It\'s an old Duke Ellington record.\x00" as *const u8 as *const libc::c_char
    232
            as *mut libc::c_char,

    933
            as *mut libc::c_char,
    625
        "It\'s your favorite game -- robotfindscatan!\u{0}",
    934
        b"It\'s your favorite game -- robotfindscatan!\x00" as *const u8 as *const libc::c_char
    626
        "Just a man selling an albatross.\u{0}",
    935
            as *mut libc::c_char,
    627
        "The intermission from a 1930s silent movie.\u{0}",
    936
        b"Just a man selling an albatross.\x00" as *const u8 as *const libc::c_char
    628
        "It\'s an inverted billiard ball!\u{0}",
    937
            as *mut libc::c_char,
    629
        "The spectre of Sherlock Holmes wills you onwards.\u{0}",
    938
        b"The intermission from a 1930s silent movie.\x00" as *const u8 as *const libc::c_char
    939
            as *mut libc::c_char,
    940
        b"It\'s an inverted billiard ball!\x00" as *const u8 as *const libc::c_char
    941
            as *mut libc::c_char,
    942
        b"The spectre of Sherlock Holmes wills you onwards.\x00" as *const u8 as *const libc::c_char
    943
            as *mut libc::c_char,
    944
    ];
    630
    ];
    945
    /*
    631
    /*
    946
     *Function definitions
    632
     *Function definitions
    947
     */
    633
     */
    948
    /*Initialization and setup functions*/
    634
    /*Initialization and setup functions*/

    We use type_fix_rules to fix up the uses of messages, as we did for ver:

    type_fix_rules
        '*, &str, *const __t => __old.as_ptr()'
        '*, &str, *mut __t => __old.as_ptr() as *mut __t' ;
    

    Diff #20

    src/robotfindskitten.rs
    1069
                }
    1069
                }
    1070
                _ => {
    1070
                _ => {
    1071
                    message(
    1071
                    message(
    1072
                        messages[bogus_messages[(*(*screen.offset(check_x as isize))
    1072
                        messages[bogus_messages[(*(*screen.offset(check_x as isize))
    1073
                            .offset(check_y as isize)
    1073
                            .offset(check_y as isize)
    1074
                            - 2i32) as usize] as usize],
    1074
                            - 2i32) as usize] as usize]
    1075
                            .as_ptr() as *mut i8,
    1075
                    );
    1076
                    );
    1076
                }
    1077
                }
    1077
            }
    1078
            }
    1078
            return;
    1079
            return;
    1079
        }
    1080
        }

    Here we needed a second rule for *mut pointers, similar to the one for *const, because robotfindskitten mistakenly declares messages as an array of char* instead of const char*.

    With all type errors fixed, we can make messages immutable and commit the changes:

    select target 'item(messages);' ;
    set_mutability imm ;
    
    commit ;
    

    Diff #21

    src/robotfindskitten.rs
    218
    will happen.*/
    218
    will happen.*/
    219
    /*Also, take note that robotfindskitten.c and configure.in
    219
    /*Also, take note that robotfindskitten.c and configure.in
    220
    currently have the version number hardcoded into them, and they
    220
    currently have the version number hardcoded into them, and they
    221
    should reflect MESSAGES. */
    221
    should reflect MESSAGES. */
    222
    /* Watch out for fenceposts.*/
    222
    /* Watch out for fenceposts.*/
    223
    static mut messages: [&'static str; 406] = [
    223
    static messages: [&'static str; 406] = [
    224
        "\"I pity the fool who mistakes me for kitten!\", sez Mr. T.\u{0}",
    224
        "\"I pity the fool who mistakes me for kitten!\", sez Mr. T.\u{0}",
    225
        "That\'s just an old tin can.\u{0}",
    225
        "That\'s just an old tin can.\u{0}",
    226
        "It\'s an altar to the horse god.\u{0}",
    226
        "It\'s an altar to the horse god.\u{0}",
    227
        "A box of dancing mechanical pencils. They dance! They sing!\u{0}",
    227
        "A box of dancing mechanical pencils. They dance! They sing!\u{0}",
    228
        "It\'s an old Duke Ellington record.\u{0}",
    228
        "It\'s an old Duke Ellington record.\u{0}",

    Heap allocations

    The screen variable stores a heap-allocated two-dimensional array, represented in C as an int**. In Rust, this becomes *mut *mut c_int, which is unsafe to access. We replace it with CArray<CArray<c_int>>, where CArray is a memory-safe collection type provided by the c2rust_runtime library. CArray is convenient for this purpose because it supports C-style initialization and access patterns (including pointer arithmetic) while still guaranteeing memory safety.

    We actually perform the conversion from *mut to CArray in two steps. First, we replace *mut with the simpler CBlockPtr type, also defined in c2rust_runtime. CBlockPtr provides some limited bounds checking, but otherwise functions much like a raw pointer. It serves as a useful intermediate step, letting us fix up the differences between the raw-pointer and CArray APIs in two stages instead of attempting to do it all at once. Once screen has been fully converted to CBlockPtr<CBlockPtr<c_int>>, we finish the conversion to CArray in the second step.

    As a preliminary, we need to add an import of the c2rust_runtime library:

    select target 'crate;' ;
    create_item 'extern crate c2rust_runtime;' inside ;
    

    Diff #22

    src/robotfindskitten.rs
    5
        non_snake_case,
    5
        non_snake_case,
    6
        non_upper_case_globals,
    6
        non_upper_case_globals,
    7
        unused_mut
    7
        unused_mut
    8
    )]
    8
    )]
    9
    #![feature(const_raw_ptr_to_usize_cast, extern_types, libc)]
    9
    #![feature(const_raw_ptr_to_usize_cast, extern_types, libc)]
    10
    extern crate c2rust_runtime;
    10
    extern crate libc;
    11
    extern crate libc;
    11
    extern "C" {
    12
    extern "C" {
    12
        pub type ldat;
    13
        pub type ldat;
    13
        #[no_mangle]
    14
        #[no_mangle]
    14
        fn printf(_: *const libc::c_char, ...) -> libc::c_int;
    15
        fn printf(_: *const libc::c_char, ...) -> libc::c_int;

    Now we can proceed with the actual refactoring.

    Converting to CBlockPtr

    We further break down the transition from *mut *mut c_int to CBlockPtr<CBlockPtr<c_int>> into two steps, first converting the inner pointer (leaving the overall type as *mut CBlockPtr<c_int>) and then the outer. We change the type annotation first, as we did for var and messages:

    select target 'item(screen); child(ty);' ;
    rewrite_ty 'marked!(*mut *mut __t)'
        '*mut ::c2rust_runtime::CBlockPtr<__t>' ;
    

    Diff #23

    src/robotfindskitten.rs
    759
    /* This array contains our internal representation of the screen. The
    759
    /* This array contains our internal representation of the screen. The
    760
    array is bigger than it needs to be, as we don't need to keep track
    760
    array is bigger than it needs to be, as we don't need to keep track
    761
    of the first few rows of the screen. But that requires making an
    761
    of the first few rows of the screen. But that requires making an
    762
    offset function and using that everywhere. So not right now. */
    762
    offset function and using that everywhere. So not right now. */
    763
    #[no_mangle]
    763
    #[no_mangle]
    764
    pub static mut screen: *mut *mut libc::c_int =
    764
    pub static mut screen: *mut ::c2rust_runtime::CBlockPtr =
    765
        0 as *const *mut libc::c_int as *mut *mut libc::c_int;
    765
        0 as *const *mut libc::c_int as *mut *mut libc::c_int;
    766
    #[no_mangle]
    766
    #[no_mangle]
    767
    pub unsafe extern "C" fn initialize_robot() {
    767
    pub unsafe extern "C" fn initialize_robot() {
    768
        robot.x = rand() % (COLS - 1i32) + 1i32;
    768
        robot.x = rand() % (COLS - 1i32) + 1i32;
    769
        robot.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
    769
        robot.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;

    This introduces type errors, letting us easily find (and fix) related expressions using type_fix_rules:

    type_fix_rules
        'rval, *mut __t, ::c2rust_runtime::CBlockPtr<__u> =>
            unsafe { ::c2rust_runtime::CBlockPtr::from_ptr(__old) }'
        'rval, *mut __t, *mut __u => __old as *mut __u'
        ;
    

    Diff #24

    src/robotfindskitten.rs
    707
       };
    707
       };
    708
        let mut i: libc::c_int = 0i32;
    708
        let mut i: libc::c_int = 0i32;
    709
        screen = malloc(
    709
        screen = malloc(
    710
            (::std::mem::size_of::<*mut libc::c_int>() as libc::c_ulong)
    710
            (::std::mem::size_of::<*mut libc::c_int>() as libc::c_ulong)
    711
                .wrapping_mul((COLS - 1i32 + 1i32) as libc::c_ulong),
    711
                .wrapping_mul((COLS - 1i32 + 1i32) as libc::c_ulong),
    712
        ) as *mut *mut libc::c_int;
    712
        ) as *mut *mut libc::c_int as *mut ::c2rust_runtime::CBlockPtr<i32>;
    713
        i = 0i32;
    713
        i = 0i32;
    714
        while i < COLS - 1i32 + 1i32 {
    714
        while i < COLS - 1i32 + 1i32 {
    715
            let ref mut fresh0 = *screen.offset(i as isize);
    715
            let ref mut fresh0 = *screen.offset(i as isize);
    716
            *fresh0 = malloc(
    716
            *fresh0 = unsafe {
    717
                ::c2rust_runtime::CBlockPtr::from_ptr(malloc(
    717
                (::std::mem::size_of::() as libc::c_ulong)
    718
                    (::std::mem::size_of::() as libc::c_ulong)
    718
                    .wrapping_mul((LINES - 1i32 + 1i32) as libc::c_ulong),
    719
                        .wrapping_mul((LINES - 1i32 + 1i32) as libc::c_ulong),
    719
            ) as *mut libc::c_int;
    720
                ) as *mut libc::c_int)
    721
            };
    720
            i += 1
    722
            i += 1
    721
        }
    723
        }
    722
        empty.x = -1i32;
    724
        empty.x = -1i32;
    723
        empty.y = -1i32;
    725
        empty.y = -1i32;
    724
        empty.color = 0i32;
    726
        empty.color = 0i32;

    760
    array is bigger than it needs to be, as we don't need to keep track
    762
    array is bigger than it needs to be, as we don't need to keep track
    761
    of the first few rows of the screen. But that requires making an
    763
    of the first few rows of the screen. But that requires making an
    762
    offset function and using that everywhere. So not right now. */
    764
    offset function and using that everywhere. So not right now. */
    763
    #[no_mangle]
    765
    #[no_mangle]
    764
    pub static mut screen: *mut ::c2rust_runtime::CBlockPtr =
    766
    pub static mut screen: *mut ::c2rust_runtime::CBlockPtr =
    765
        0 as *const *mut libc::c_int as *mut *mut libc::c_int;
    767
        0 as *const *mut libc::c_int as *mut *mut libc::c_int as *mut ::c2rust_runtime::CBlockPtr<i32>;
    766
    #[no_mangle]
    768
    #[no_mangle]
    767
    pub unsafe extern "C" fn initialize_robot() {
    769
    pub unsafe extern "C" fn initialize_robot() {
    768
        robot.x = rand() % (COLS - 1i32) + 1i32;
    770
        robot.x = rand() % (COLS - 1i32) + 1i32;
    769
        robot.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
    771
        robot.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
    770
        robot.character = '#' as i32 as libc::c_char;
    772
        robot.character = '#' as i32 as libc::c_char;

    The first rule provided here handles the later part of screen's initialization, where the program allocates a *mut c_int array (now CBlockPtr<c_int>) for each row of the screen. The second rule handles the earlier part, where it allocates the top-level *mut *mut c_int (now *mut CBlockPtr<c_int>). Both allocations now need a cast, since the type of the rows has changed.

    One category of type errors remains: the initialization code tries to dereference the result of offsetting the array pointer, which is not possible directly with the CBlockPtr API. We add the necessary method call using rewrite_expr:

    rewrite_expr
        '*typed!(__e, ::c2rust_runtime::block_ptr::CBlockOffset<__t>)'
        '*__e.as_mut()' ;
    

    Diff #25

    src/robotfindskitten.rs
    728
       empty.character = ' ' as i32 as libc::c_char;
    728
       empty.character = ' ' as i32 as libc::c_char;
    729
        counter = 0i32;
    729
        counter = 0i32;
    730
        while counter <= COLS - 1i32 {
    730
        while counter <= COLS - 1i32 {
    731
            counter2 = 0i32;
    731
            counter2 = 0i32;
    732
            while counter2 <= LINES - 1i32 {
    732
            while counter2 <= LINES - 1i32 {
    733
                *(*screen.offset(counter as isize)).offset(counter2 as isize) = -1i32;
    733
                *(*screen.offset(counter as isize))
    734
                    .offset(counter2 as isize)
    735
                    .as_mut() = -1i32;
    734
                counter2 += 1
    736
                counter2 += 1
    735
            }
    737
            }
    736
            counter += 1
    738
            counter += 1
    737
        }
    739
        }
    738
        counter = 0i32;
    740
        counter = 0i32;

    770
       robot.x = rand() % (COLS - 1i32) + 1i32;
    772
       robot.x = rand() % (COLS - 1i32) + 1i32;
    771
        robot.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
    773
        robot.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
    772
        robot.character = '#' as i32 as libc::c_char;
    774
        robot.character = '#' as i32 as libc::c_char;
    773
        robot.color = 0i32;
    775
        robot.color = 0i32;
    774
        robot.bold = 0 != 0i32;
    776
        robot.bold = 0 != 0i32;
    775
        *(*screen.offset(robot.x as isize)).offset(robot.y as isize) = 0i32;
    777
        *(*screen.offset(robot.x as isize))
    778
            .offset(robot.y as isize)
    779
            .as_mut() = 0i32;
    776
    }
    780
    }
    777
    /*Global variables. Bite me, it's fun.*/
    781
    /*Global variables. Bite me, it's fun.*/
    778
    #[no_mangle]
    782
    #[no_mangle]
    779
    pub static mut robot: screen_object = screen_object {
    783
    pub static mut robot: screen_object = screen_object {
    780
        x: 0,
    784
        x: 0,

    786
    #[no_mangle]
    790
    #[no_mangle]
    787
    pub unsafe extern "C" fn initialize_kitten() {
    791
    pub unsafe extern "C" fn initialize_kitten() {
    788
        loop {
    792
        loop {
    789
            kitten.x = rand() % (COLS - 1i32) + 1i32;
    793
            kitten.x = rand() % (COLS - 1i32) + 1i32;
    790
            kitten.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
    794
            kitten.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
    791
            if !(*(*screen.offset(kitten.x as isize)).offset(kitten.y as isize) != -1i32) {
    795
            if !(*(*screen.offset(kitten.x as isize))
    796
                .offset(kitten.y as isize)
    797
                .as_mut()
    798
                != -1i32)
    799
            {
    792
                break;
    800
                break;
    793
            }
    801
            }
    794
        }
    802
        }
    795
        loop {
    803
        loop {
    796
            kitten.character = (rand() % (126i32 - '!' as i32 + 1i32) + '!' as i32) as libc::c_char;
    804
            kitten.character = (rand() % (126i32 - '!' as i32 + 1i32) + '!' as i32) as libc::c_char;
    797
            if !(0 == validchar(kitten.character)) {
    805
            if !(0 == validchar(kitten.character)) {
    798
                break;
    806
                break;
    799
            }
    807
            }
    800
        }
    808
        }
    801
        *(*screen.offset(kitten.x as isize)).offset(kitten.y as isize) = 1i32;
    809
        *(*screen.offset(kitten.x as isize))
    810
            .offset(kitten.y as isize)
    811
            .as_mut() = 1i32;
    802
        kitten.color = rand() % 6i32 + 1i32;
    812
        kitten.color = rand() % 6i32 + 1i32;
    803
        kitten.bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
    813
        kitten.bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
    804
    }
    814
    }
    805
    #[no_mangle]
    815
    #[no_mangle]
    806
    pub static mut kitten: screen_object = screen_object {
    816
    pub static mut kitten: screen_object = screen_object {

    837
           loop {
    847
           loop {
    838
                bogus[counter as usize].x = rand() % (COLS - 1i32) + 1i32;
    848
                bogus[counter as usize].x = rand() % (COLS - 1i32) + 1i32;
    839
                bogus[counter as usize].y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
    849
                bogus[counter as usize].y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
    840
                if !(*(*screen.offset(bogus[counter as usize].x as isize))
    850
                if !(*(*screen.offset(bogus[counter as usize].x as isize))
    841
                    .offset(bogus[counter as usize].y as isize)
    851
                    .offset(bogus[counter as usize].y as isize)
    852
                    .as_mut()
    842
                    != -1i32)
    853
                    != -1i32)
    843
                {
    854
                {
    844
                    break;
    855
                    break;
    845
                }
    856
                }
    846
            }
    857
            }
    847
            *(*screen.offset(bogus[counter as usize].x as isize))
    858
            *(*screen.offset(bogus[counter as usize].x as isize))
    848
                .offset(bogus[counter as usize].y as isize) = counter + 2i32;
    859
                .offset(bogus[counter as usize].y as isize)
    860
                .as_mut() = counter + 2i32;
    849
            loop {
    861
            loop {
    850
                index = (rand() as libc::c_ulong).wrapping_rem(
    862
                index = (rand() as libc::c_ulong).wrapping_rem(
    851
                    (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    863
                    (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    852
                        .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong),
    864
                        .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong),
    853
                ) as libc::c_int;
    865
                ) as libc::c_int;

    989
           if !(old_x == robot.x && old_y == robot.y) {
    1001
           if !(old_x == robot.x && old_y == robot.y) {
    990
                if wmove(stdscr, old_y, old_x) == -1i32 {
    1002
                if wmove(stdscr, old_y, old_x) == -1i32 {
    991
                } else {
    1003
                } else {
    992
                    waddch(stdscr, ' ' as i32 as chtype);
    1004
                    waddch(stdscr, ' ' as i32 as chtype);
    993
                };
    1005
                };
    994
                *(*screen.offset(old_x as isize)).offset(old_y as isize) = -1i32;
    1006
                *(*screen.offset(old_x as isize))
    1007
                    .offset(old_y as isize)
    1008
                    .as_mut() = -1i32;
    995
                draw(robot);
    1009
                draw(robot);
    996
                wrefresh(stdscr);
    1010
                wrefresh(stdscr);
    997
                *(*screen.offset(robot.x as isize)).offset(robot.y as isize) = 0i32;
    1011
                *(*screen.offset(robot.x as isize))
    1012
                    .offset(robot.y as isize)
    1013
                    .as_mut() = 0i32;
    998
                old_x = robot.x;
    1014
                old_x = robot.x;
    999
                old_y = robot.y
    1015
                old_y = robot.y
    1000
            }
    1016
            }
    1001
            input = wgetch(stdscr)
    1017
            input = wgetch(stdscr)
    1002
        }
    1018
        }

    1058
           }
    1074
           }
    1059
        }
    1075
        }
    1060
        if check_y < 3i32 || check_y > LINES - 1i32 || check_x < 0i32 || check_x > COLS - 1i32 {
    1076
        if check_y < 3i32 || check_y > LINES - 1i32 || check_x < 0i32 || check_x > COLS - 1i32 {
    1061
            return;
    1077
            return;
    1062
        }
    1078
        }
    1063
        if *(*screen.offset(check_x as isize)).offset(check_y as isize) != -1i32 {
    1079
        if *(*screen.offset(check_x as isize))
    1080
            .offset(check_y as isize)
    1081
            .as_mut()
    1082
            != -1i32
    1083
        {
    1064
            match *(*screen.offset(check_x as isize)).offset(check_y as isize) {
    1084
            match *(*screen.offset(check_x as isize))
    1085
                .offset(check_y as isize)
    1086
                .as_mut()
    1087
            {
    1065
                0 => {}
    1088
                0 => {}
    1066
                1 => {
    1089
                1 => {
    1067
                    /*We didn't move, or we're stuck in a
    1090
                    /*We didn't move, or we're stuck in a
    1068
                    time warp or something.*/
    1091
                    time warp or something.*/
    1069
                    wmove(stdscr, 1i32, 0i32);
    1092
                    wmove(stdscr, 1i32, 0i32);

    1072
               }
    1095
               }
    1073
                _ => {
    1096
                _ => {
    1074
                    message(
    1097
                    message(
    1075
                        messages[bogus_messages[(*(*screen.offset(check_x as isize))
    1098
                        messages[bogus_messages[(*(*screen.offset(check_x as isize))
    1076
                            .offset(check_y as isize)
    1099
                            .offset(check_y as isize)
    1100
                            .as_mut()
    1077
                            - 2i32) as usize] as usize]
    1101
                            - 2i32) as usize] as usize]
    1078
                            .as_ptr() as *mut i8,
    1102
                            .as_ptr() as *mut i8,
    1079
                    );
    1103
                    );
    1080
                }
    1104
                }
    1081
            }
    1105
            }

    Here, the pattern filters for dereferences of CBlockOffset expressions, which result from calling offset on a CBlockPtr, and adds a call to as_mut() before the dereference.

    The conversion of screen to *mut CBlockPtr<c_int> is now complete. The conversion to CBlockPtr<CBlockPtr<c_int>> uses a similar refactoring script:

    select target 'crate; item(screen); child(ty);' ;
    rewrite_ty 'marked!(*mut __t)'
        '::c2rust_runtime::CBlockPtr<__t>' ;
    type_fix_rules
        'rval, *mut __t, ::c2rust_runtime::CBlockPtr<__u> =>
            unsafe { ::c2rust_runtime::CBlockPtr::from_ptr(__old) }'
        'rval, *mut __t, *mut __u => __old as *mut __u'
        ;
    rewrite_expr
        '*typed!(__e, ::c2rust_runtime::block_ptr::CBlockOffset<__t>)'
        '*__e.as_mut()' ;
    

    Diff #26

    src/robotfindskitten.rs
    704
           color: 0,
    704
           color: 0,
    705
            bold: false,
    705
            bold: false,
    706
            character: 0,
    706
            character: 0,
    707
        };
    707
        };
    708
        let mut i: libc::c_int = 0i32;
    708
        let mut i: libc::c_int = 0i32;
    709
        screen = malloc(
    709
        screen = unsafe {
    710
            ::c2rust_runtime::CBlockPtr::from_ptr(malloc(
    710
            (::std::mem::size_of::<*mut libc::c_int>() as libc::c_ulong)
    711
                (::std::mem::size_of::<*mut libc::c_int>() as libc::c_ulong)
    711
                .wrapping_mul((COLS - 1i32 + 1i32) as libc::c_ulong),
    712
                    .wrapping_mul((COLS - 1i32 + 1i32) as libc::c_ulong),
    712
        ) as *mut *mut libc::c_int as *mut ::c2rust_runtime::CBlockPtr<i32>;
    713
            ) as *mut *mut libc::c_int
    714
                as *mut ::c2rust_runtime::CBlockPtr<i32>)
    715
        };
    713
        i = 0i32;
    716
        i = 0i32;
    714
        while i < COLS - 1i32 + 1i32 {
    717
        while i < COLS - 1i32 + 1i32 {
    715
            let ref mut fresh0 = *screen.offset(i as isize);
    718
            let ref mut fresh0 = *screen.offset(i as isize).as_mut();
    716
            *fresh0 = unsafe {
    719
            *fresh0 = unsafe {
    717
                ::c2rust_runtime::CBlockPtr::from_ptr(malloc(
    720
                ::c2rust_runtime::CBlockPtr::from_ptr(malloc(
    718
                    (::std::mem::size_of::() as libc::c_ulong)
    721
                    (::std::mem::size_of::() as libc::c_ulong)
    719
                        .wrapping_mul((LINES - 1i32 + 1i32) as libc::c_ulong),
    722
                        .wrapping_mul((LINES - 1i32 + 1i32) as libc::c_ulong),
    720
                ) as *mut libc::c_int)
    723
                ) as *mut libc::c_int)

    728
       empty.character = ' ' as i32 as libc::c_char;
    731
       empty.character = ' ' as i32 as libc::c_char;
    729
        counter = 0i32;
    732
        counter = 0i32;
    730
        while counter <= COLS - 1i32 {
    733
        while counter <= COLS - 1i32 {
    731
            counter2 = 0i32;
    734
            counter2 = 0i32;
    732
            while counter2 <= LINES - 1i32 {
    735
            while counter2 <= LINES - 1i32 {
    733
                *(*screen.offset(counter as isize))
    736
                *(*screen.offset(counter as isize).as_mut())
    734
                    .offset(counter2 as isize)
    737
                    .offset(counter2 as isize)
    735
                    .as_mut() = -1i32;
    738
                    .as_mut() = -1i32;
    736
                counter2 += 1
    739
                counter2 += 1
    737
            }
    740
            }
    738
            counter += 1
    741
            counter += 1

    763
    /* This array contains our internal representation of the screen. The
    766
    /* This array contains our internal representation of the screen. The
    764
    array is bigger than it needs to be, as we don't need to keep track
    767
    array is bigger than it needs to be, as we don't need to keep track
    765
    of the first few rows of the screen. But that requires making an
    768
    of the first few rows of the screen. But that requires making an
    766
    offset function and using that everywhere. So not right now. */
    769
    offset function and using that everywhere. So not right now. */
    767
    #[no_mangle]
    770
    #[no_mangle]
    768
    pub static mut screen: *mut ::c2rust_runtime::CBlockPtr =
    771
    pub static mut screen: ::c2rust_runtime::CBlockPtr<::c2rust_runtime::CBlockPtr> = unsafe {
    769
        0 as *const *mut libc::c_int as *mut *mut libc::c_int as *mut ::c2rust_runtime::CBlockPtr<i32>;
    772
        ::c2rust_runtime::CBlockPtr::from_ptr(
    773
            0 as *const *mut libc::c_int as *mut *mut libc::c_int
    774
                as *mut ::c2rust_runtime::CBlockPtr<i32>,
    775
        )
    776
    };
    770
    #[no_mangle]
    777
    #[no_mangle]
    771
    pub unsafe extern "C" fn initialize_robot() {
    778
    pub unsafe extern "C" fn initialize_robot() {
    772
        robot.x = rand() % (COLS - 1i32) + 1i32;
    779
        robot.x = rand() % (COLS - 1i32) + 1i32;
    773
        robot.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
    780
        robot.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
    774
        robot.character = '#' as i32 as libc::c_char;
    781
        robot.character = '#' as i32 as libc::c_char;
    775
        robot.color = 0i32;
    782
        robot.color = 0i32;
    776
        robot.bold = 0 != 0i32;
    783
        robot.bold = 0 != 0i32;
    777
        *(*screen.offset(robot.x as isize))
    784
        *(*screen.offset(robot.x as isize).as_mut())
    778
            .offset(robot.y as isize)
    785
            .offset(robot.y as isize)
    779
            .as_mut() = 0i32;
    786
            .as_mut() = 0i32;
    780
    }
    787
    }
    781
    /*Global variables. Bite me, it's fun.*/
    788
    /*Global variables. Bite me, it's fun.*/
    782
    #[no_mangle]
    789
    #[no_mangle]

    790
    #[no_mangle]
    797
    #[no_mangle]
    791
    pub unsafe extern "C" fn initialize_kitten() {
    798
    pub unsafe extern "C" fn initialize_kitten() {
    792
        loop {
    799
        loop {
    793
            kitten.x = rand() % (COLS - 1i32) + 1i32;
    800
            kitten.x = rand() % (COLS - 1i32) + 1i32;
    794
            kitten.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
    801
            kitten.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
    795
            if !(*(*screen.offset(kitten.x as isize))
    802
            if !(*(*screen.offset(kitten.x as isize).as_mut())
    796
                .offset(kitten.y as isize)
    803
                .offset(kitten.y as isize)
    797
                .as_mut()
    804
                .as_mut()
    798
                != -1i32)
    805
                != -1i32)
    799
            {
    806
            {
    800
                break;
    807
                break;

    804
           kitten.character = (rand() % (126i32 - '!' as i32 + 1i32) + '!' as i32) as libc::c_char;
    811
           kitten.character = (rand() % (126i32 - '!' as i32 + 1i32) + '!' as i32) as libc::c_char;
    805
            if !(0 == validchar(kitten.character)) {
    812
            if !(0 == validchar(kitten.character)) {
    806
                break;
    813
                break;
    807
            }
    814
            }
    808
        }
    815
        }
    809
        *(*screen.offset(kitten.x as isize))
    816
        *(*screen.offset(kitten.x as isize).as_mut())
    810
            .offset(kitten.y as isize)
    817
            .offset(kitten.y as isize)
    811
            .as_mut() = 1i32;
    818
            .as_mut() = 1i32;
    812
        kitten.color = rand() % 6i32 + 1i32;
    819
        kitten.color = rand() % 6i32 + 1i32;
    813
        kitten.bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
    820
        kitten.bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
    814
    }
    821
    }

    845
               }
    852
               }
    846
            }
    853
            }
    847
            loop {
    854
            loop {
    848
                bogus[counter as usize].x = rand() % (COLS - 1i32) + 1i32;
    855
                bogus[counter as usize].x = rand() % (COLS - 1i32) + 1i32;
    849
                bogus[counter as usize].y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
    856
                bogus[counter as usize].y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
    850
                if !(*(*screen.offset(bogus[counter as usize].x as isize))
    857
                if !(*(*screen.offset(bogus[counter as usize].x as isize).as_mut())
    851
                    .offset(bogus[counter as usize].y as isize)
    858
                    .offset(bogus[counter as usize].y as isize)
    852
                    .as_mut()
    859
                    .as_mut()
    853
                    != -1i32)
    860
                    != -1i32)
    854
                {
    861
                {
    855
                    break;
    862
                    break;
    856
                }
    863
                }
    857
            }
    864
            }
    858
            *(*screen.offset(bogus[counter as usize].x as isize))
    865
            *(*screen.offset(bogus[counter as usize].x as isize).as_mut())
    859
                .offset(bogus[counter as usize].y as isize)
    866
                .offset(bogus[counter as usize].y as isize)
    860
                .as_mut() = counter + 2i32;
    867
                .as_mut() = counter + 2i32;
    861
            loop {
    868
            loop {
    862
                index = (rand() as libc::c_ulong).wrapping_rem(
    869
                index = (rand() as libc::c_ulong).wrapping_rem(
    863
                    (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    870
                    (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)

    1001
           if !(old_x == robot.x && old_y == robot.y) {
    1008
           if !(old_x == robot.x && old_y == robot.y) {
    1002
                if wmove(stdscr, old_y, old_x) == -1i32 {
    1009
                if wmove(stdscr, old_y, old_x) == -1i32 {
    1003
                } else {
    1010
                } else {
    1004
                    waddch(stdscr, ' ' as i32 as chtype);
    1011
                    waddch(stdscr, ' ' as i32 as chtype);
    1005
                };
    1012
                };
    1006
                *(*screen.offset(old_x as isize))
    1013
                *(*screen.offset(old_x as isize).as_mut())
    1007
                    .offset(old_y as isize)
    1014
                    .offset(old_y as isize)
    1008
                    .as_mut() = -1i32;
    1015
                    .as_mut() = -1i32;
    1009
                draw(robot);
    1016
                draw(robot);
    1010
                wrefresh(stdscr);
    1017
                wrefresh(stdscr);
    1011
                *(*screen.offset(robot.x as isize))
    1018
                *(*screen.offset(robot.x as isize).as_mut())
    1012
                    .offset(robot.y as isize)
    1019
                    .offset(robot.y as isize)
    1013
                    .as_mut() = 0i32;
    1020
                    .as_mut() = 0i32;
    1014
                old_x = robot.x;
    1021
                old_x = robot.x;
    1015
                old_y = robot.y
    1022
                old_y = robot.y
    1016
            }
    1023
            }

    1074
           }
    1081
           }
    1075
        }
    1082
        }
    1076
        if check_y < 3i32 || check_y > LINES - 1i32 || check_x < 0i32 || check_x > COLS - 1i32 {
    1083
        if check_y < 3i32 || check_y > LINES - 1i32 || check_x < 0i32 || check_x > COLS - 1i32 {
    1077
            return;
    1084
            return;
    1078
        }
    1085
        }
    1079
        if *(*screen.offset(check_x as isize))
    1086
        if *(*screen.offset(check_x as isize).as_mut())
    1080
            .offset(check_y as isize)
    1087
            .offset(check_y as isize)
    1081
            .as_mut()
    1088
            .as_mut()
    1082
            != -1i32
    1089
            != -1i32
    1083
        {
    1090
        {
    1084
            match *(*screen.offset(check_x as isize))
    1091
            match *(*screen.offset(check_x as isize).as_mut())
    1085
                .offset(check_y as isize)
    1092
                .offset(check_y as isize)
    1086
                .as_mut()
    1093
                .as_mut()
    1087
            {
    1094
            {
    1088
                0 => {}
    1095
                0 => {}
    1089
                1 => {
    1096
                1 => {

    1093
                   wclrtoeol(stdscr);
    1100
                   wclrtoeol(stdscr);
    1094
                    play_animation(input);
    1101
                    play_animation(input);
    1095
                }
    1102
                }
    1096
                _ => {
    1103
                _ => {
    1097
                    message(
    1104
                    message(
    1098
                        messages[bogus_messages[(*(*screen.offset(check_x as isize))
    1105
                        messages[bogus_messages[(*(*screen.offset(check_x as isize).as_mut())
    1099
                            .offset(check_y as isize)
    1106
                            .offset(check_y as isize)
    1100
                            .as_mut()
    1107
                            .as_mut()
    1101
                            - 2i32) as usize] as usize]
    1108
                            - 2i32) as usize] as usize]
    1102
                            .as_ptr() as *mut i8,
    1109
                            .as_ptr() as *mut i8,
    1103
                    );
    1110
                    );

    The only change is in the rewrite_ty step.

    There's one last bit of cleanup to perform: now that screen has the desired CBlockPtr<CBlockPtr<c_int>> type, we can rewrite the allocations that initialize it. At this point the allocations use the unsafe malloc function followed by the unsafe CBlockPtr::from_ptr, but we can change that to use the safe CBlockPtr::alloc method instead:

    rewrite_expr 'malloc(__e) as *mut __t as *mut __u' 'malloc(__e) as *mut __u' ;
    rewrite_expr
        '::c2rust_runtime::CBlockPtr::from_ptr(malloc(__e) as *mut __t)'
        '::c2rust_runtime::CBlockPtr::alloc(
            __e as usize / ::std::mem::size_of::<__t>())'
        ;
    

    Diff #27

    src/robotfindskitten.rs
    705
           bold: false,
    705
           bold: false,
    706
            character: 0,
    706
            character: 0,
    707
        };
    707
        };
    708
        let mut i: libc::c_int = 0i32;
    708
        let mut i: libc::c_int = 0i32;
    709
        screen = unsafe {
    709
        screen = unsafe {
    710
            ::c2rust_runtime::CBlockPtr::from_ptr(malloc(
    710
            ::c2rust_runtime::CBlockPtr::alloc(
    711
                (::std::mem::size_of::<*mut libc::c_int>() as libc::c_ulong)
    711
                (::std::mem::size_of::<*mut libc::c_int>() as libc::c_ulong)
    712
                    .wrapping_mul((COLS - 1i32 + 1i32) as libc::c_ulong),
    712
                    .wrapping_mul((COLS - 1i32 + 1i32) as libc::c_ulong) as usize
    713
            ) as *mut *mut libc::c_int
    713
                    / ::std::mem::size_of::<::c2rust_runtime::CBlockPtr<i32>>(),
    714
                as *mut ::c2rust_runtime::CBlockPtr<i32>)
    714
            )
    715
        };
    715
        };
    716
        i = 0i32;
    716
        i = 0i32;
    717
        while i < COLS - 1i32 + 1i32 {
    717
        while i < COLS - 1i32 + 1i32 {
    718
            let ref mut fresh0 = *screen.offset(i as isize).as_mut();
    718
            let ref mut fresh0 = *screen.offset(i as isize).as_mut();
    719
            *fresh0 = unsafe {
    719
            *fresh0 = unsafe {
    720
                ::c2rust_runtime::CBlockPtr::from_ptr(malloc(
    720
                ::c2rust_runtime::CBlockPtr::alloc(
    721
                    (::std::mem::size_of::() as libc::c_ulong)
    721
                    (::std::mem::size_of::() as libc::c_ulong)
    722
                        .wrapping_mul((LINES - 1i32 + 1i32) as libc::c_ulong),
    722
                        .wrapping_mul((LINES - 1i32 + 1i32) as libc::c_ulong) as usize
    723
                ) as *mut libc::c_int)
    723
                        / ::std::mem::size_of::(),
    724
                )
    724
            };
    725
            };
    725
            i += 1
    726
            i += 1
    726
        }
    727
        }
    727
        empty.x = -1i32;
    728
        empty.x = -1i32;
    728
        empty.y = -1i32;
    729
        empty.y = -1i32;

    This doesn't remove the unsafe blocks wrapping each allocation - we leave those until the end of our refactoring, when we remove unnecessary unsafe blocks throughout the entire crate at once.

    At this point, the refactoring of screen to is done, and we can commit the changes:

    commit ;
    

    Diff #28

    src/robotfindskitten.rs
    7
        unused_mut
    7
        unused_mut
    8
    )]
    8
    )]
    9
    #![feature(const_raw_ptr_to_usize_cast, extern_types, libc)]
    9
    #![feature(const_raw_ptr_to_usize_cast, extern_types, libc)]
    10
    extern crate c2rust_runtime;
    10
    extern crate c2rust_runtime;
    11
    extern crate libc;
    11
    extern crate libc;
    12
    extern "C" {
    12
    extern "C" {
    13
        pub type ldat;
    13
        pub type ldat;
    14
        #[no_mangle]
    14
        #[no_mangle]
    15
        fn printf(_: *const libc::c_char, ...) -> libc::c_int;
    15
        fn printf(_: *const libc::c_char, ...) -> libc::c_int;

    Converting to CArray

    The CArray and CBlockPtr APIs are deliberately quite similar, which makes this part of the screen refactoring fairly straightforward.

    First, we replace all uses of CBlockPtr with CArray, both in types and in function calls:

    rewrite_ty '::c2rust_runtime::CBlockPtr<__t>' '::c2rust_runtime::CArray<__t>' ;
    rewrite_expr
        '::c2rust_runtime::CBlockPtr::from_ptr'
        '::c2rust_runtime::CArray::from_ptr' ;
    rewrite_expr
        '::c2rust_runtime::CBlockPtr::alloc'
        '::c2rust_runtime::CArray::alloc' ;
    

    Diff #29

    src/robotfindskitten.rs
    705
            bold: false,
    705
            bold: false,
    706
            character: 0,
    706
            character: 0,
    707
        };
    707
        };
    708
        let mut i: libc::c_int = 0i32;
    708
        let mut i: libc::c_int = 0i32;
    709
        screen = unsafe {
    709
        screen = unsafe {
    710
            ::c2rust_runtime::CBlockPtr::alloc(
    710
            ::c2rust_runtime::CArray::alloc(
    711
                (::std::mem::size_of::<*mut libc::c_int>() as libc::c_ulong)
    711
                (::std::mem::size_of::<*mut libc::c_int>() as libc::c_ulong)
    712
                    .wrapping_mul((COLS - 1i32 + 1i32) as libc::c_ulong) as usize
    712
                    .wrapping_mul((COLS - 1i32 + 1i32) as libc::c_ulong) as usize
    713
                    / ::std::mem::size_of::<::c2rust_runtime::CBlockPtr<i32>>(),
    713
                    / ::std::mem::size_of::<::c2rust_runtime::CArray<i32>>(),
    714
            )
    714
            )
    715
        };
    715
        };
    716
        i = 0i32;
    716
        i = 0i32;
    717
        while i < COLS - 1i32 + 1i32 {
    717
        while i < COLS - 1i32 + 1i32 {
    718
            let ref mut fresh0 = *screen.offset(i as isize).as_mut();
    718
            let ref mut fresh0 = *screen.offset(i as isize).as_mut();
    719
            *fresh0 = unsafe {
    719
            *fresh0 = unsafe {
    720
                ::c2rust_runtime::CBlockPtr::alloc(
    720
                ::c2rust_runtime::CArray::alloc(
    721
                    (::std::mem::size_of::() as libc::c_ulong)
    721
                    (::std::mem::size_of::() as libc::c_ulong)
    722
                        .wrapping_mul((LINES - 1i32 + 1i32) as libc::c_ulong) as usize
    722
                        .wrapping_mul((LINES - 1i32 + 1i32) as libc::c_ulong) as usize
    723
                        / ::std::mem::size_of::(),
    723
                        / ::std::mem::size_of::(),
    724
                )
    724
                )
    725
            };
    725
            };

    767
    /* This array contains our internal representation of the screen. The
    767
    /* This array contains our internal representation of the screen. The
    768
    array is bigger than it needs to be, as we don't need to keep track
    768
    array is bigger than it needs to be, as we don't need to keep track
    769
    of the first few rows of the screen. But that requires making an
    769
    of the first few rows of the screen. But that requires making an
    770
    offset function and using that everywhere. So not right now. */
    770
    offset function and using that everywhere. So not right now. */
    771
    #[no_mangle]
    771
    #[no_mangle]
    772
    pub static mut screen: ::c2rust_runtime::CBlockPtr<::c2rust_runtime::CBlockPtr> = unsafe {
    772
    pub static mut screen: ::c2rust_runtime::CArray<::c2rust_runtime::CArray> = unsafe {
    773
        ::c2rust_runtime::CBlockPtr::from_ptr(
    773
        ::c2rust_runtime::CArray::from_ptr(
    774
            0 as *const *mut libc::c_int as *mut *mut libc::c_int
    774
            0 as *const *mut libc::c_int as *mut *mut libc::c_int as *mut ::c2rust_runtime::CArray<i32>,
    775
                as *mut ::c2rust_runtime::CBlockPtr<i32>,
    776
        )
    775
        )
    777
    };
    776
    };
    778
    #[no_mangle]
    777
    #[no_mangle]
    779
    pub unsafe extern "C" fn initialize_robot() {
    778
    pub unsafe extern "C" fn initialize_robot() {
    780
        robot.x = rand() % (COLS - 1i32) + 1i32;
    779
        robot.x = rand() % (COLS - 1i32) + 1i32;

    Next, we fix up calls to offset. Unlike CBlockPtr (and raw pointers in general), CArray distinguishes between mutable and immutable offset pointers. We handle this by simply replacing all offset calls with offset_mut:

    rewrite_expr
        'typed!(__e, ::c2rust_runtime::CArray<__t>).offset(__f)'
        '__e.offset_mut(__f)' ;
    

    Diff #30

    src/robotfindskitten.rs
    713
                    / ::std::mem::size_of::<::c2rust_runtime::CArray<i32>>(),
    713
                    / ::std::mem::size_of::<::c2rust_runtime::CArray<i32>>(),
    714
            )
    714
            )
    715
        };
    715
        };
    716
        i = 0i32;
    716
        i = 0i32;
    717
        while i < COLS - 1i32 + 1i32 {
    717
        while i < COLS - 1i32 + 1i32 {
    718
            let ref mut fresh0 = *screen.offset(i as isize).as_mut();
    718
            let ref mut fresh0 = *screen.offset_mut(i as isize).as_mut();
    719
            *fresh0 = unsafe {
    719
            *fresh0 = unsafe {
    720
                ::c2rust_runtime::CArray::alloc(
    720
                ::c2rust_runtime::CArray::alloc(
    721
                    (::std::mem::size_of::() as libc::c_ulong)
    721
                    (::std::mem::size_of::() as libc::c_ulong)
    722
                        .wrapping_mul((LINES - 1i32 + 1i32) as libc::c_ulong) as usize
    722
                        .wrapping_mul((LINES - 1i32 + 1i32) as libc::c_ulong) as usize
    723
                        / ::std::mem::size_of::(),
    723
                        / ::std::mem::size_of::(),

    732
        empty.character = ' ' as i32 as libc::c_char;
    732
        empty.character = ' ' as i32 as libc::c_char;
    733
        counter = 0i32;
    733
        counter = 0i32;
    734
        while counter <= COLS - 1i32 {
    734
        while counter <= COLS - 1i32 {
    735
            counter2 = 0i32;
    735
            counter2 = 0i32;
    736
            while counter2 <= LINES - 1i32 {
    736
            while counter2 <= LINES - 1i32 {
    737
                *(*screen.offset(counter as isize).as_mut())
    737
                *(*screen.offset_mut(counter as isize).as_mut())
    738
                    .offset(counter2 as isize)
    738
                    .offset_mut(counter2 as isize)
    739
                    .as_mut() = -1i32;
    739
                    .as_mut() = -1i32;
    740
                counter2 += 1
    740
                counter2 += 1
    741
            }
    741
            }
    742
            counter += 1
    742
            counter += 1
    743
        }
    743
        }

    779
        robot.x = rand() % (COLS - 1i32) + 1i32;
    779
        robot.x = rand() % (COLS - 1i32) + 1i32;
    780
        robot.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
    780
        robot.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
    781
        robot.character = '#' as i32 as libc::c_char;
    781
        robot.character = '#' as i32 as libc::c_char;
    782
        robot.color = 0i32;
    782
        robot.color = 0i32;
    783
        robot.bold = 0 != 0i32;
    783
        robot.bold = 0 != 0i32;
    784
        *(*screen.offset(robot.x as isize).as_mut())
    784
        *(*screen.offset_mut(robot.x as isize).as_mut())
    785
            .offset(robot.y as isize)
    785
            .offset_mut(robot.y as isize)
    786
            .as_mut() = 0i32;
    786
            .as_mut() = 0i32;
    787
    }
    787
    }
    788
    /*Global variables. Bite me, it's fun.*/
    788
    /*Global variables. Bite me, it's fun.*/
    789
    #[no_mangle]
    789
    #[no_mangle]
    790
    pub static mut robot: screen_object = screen_object {
    790
    pub static mut robot: screen_object = screen_object {

    797
    #[no_mangle]
    797
    #[no_mangle]
    798
    pub unsafe extern "C" fn initialize_kitten() {
    798
    pub unsafe extern "C" fn initialize_kitten() {
    799
        loop {
    799
        loop {
    800
            kitten.x = rand() % (COLS - 1i32) + 1i32;
    800
            kitten.x = rand() % (COLS - 1i32) + 1i32;
    801
            kitten.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
    801
            kitten.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
    802
            if !(*(*screen.offset(kitten.x as isize).as_mut())
    802
            if !(*(*screen.offset_mut(kitten.x as isize).as_mut())
    803
                .offset(kitten.y as isize)
    803
                .offset_mut(kitten.y as isize)
    804
                .as_mut()
    804
                .as_mut()
    805
                != -1i32)
    805
                != -1i32)
    806
            {
    806
            {
    807
                break;
    807
                break;
    808
            }
    808
            }

    811
            kitten.character = (rand() % (126i32 - '!' as i32 + 1i32) + '!' as i32) as libc::c_char;
    811
            kitten.character = (rand() % (126i32 - '!' as i32 + 1i32) + '!' as i32) as libc::c_char;
    812
            if !(0 == validchar(kitten.character)) {
    812
            if !(0 == validchar(kitten.character)) {
    813
                break;
    813
                break;
    814
            }
    814
            }
    815
        }
    815
        }
    816
        *(*screen.offset(kitten.x as isize).as_mut())
    816
        *(*screen.offset_mut(kitten.x as isize).as_mut())
    817
            .offset(kitten.y as isize)
    817
            .offset_mut(kitten.y as isize)
    818
            .as_mut() = 1i32;
    818
            .as_mut() = 1i32;
    819
        kitten.color = rand() % 6i32 + 1i32;
    819
        kitten.color = rand() % 6i32 + 1i32;
    820
        kitten.bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
    820
        kitten.bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
    821
    }
    821
    }
    822
    #[no_mangle]
    822
    #[no_mangle]

    852
                }
    852
                }
    853
            }
    853
            }
    854
            loop {
    854
            loop {
    855
                bogus[counter as usize].x = rand() % (COLS - 1i32) + 1i32;
    855
                bogus[counter as usize].x = rand() % (COLS - 1i32) + 1i32;
    856
                bogus[counter as usize].y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
    856
                bogus[counter as usize].y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
    857
                if !(*(*screen.offset(bogus[counter as usize].x as isize).as_mut())
    857
                if !(*(*screen
    858
                    .offset(bogus[counter as usize].y as isize)
    858
                    .offset_mut(bogus[counter as usize].x as isize)
    859
                    .as_mut()
    859
                    .as_mut())
    860
                .offset_mut(bogus[counter as usize].y as isize)
    861
                .as_mut()
    860
                    != -1i32)
    862
                    != -1i32)
    861
                {
    863
                {
    862
                    break;
    864
                    break;
    863
                }
    865
                }
    864
            }
    866
            }
    865
            *(*screen.offset(bogus[counter as usize].x as isize).as_mut())
    867
            *(*screen
    866
                .offset(bogus[counter as usize].y as isize)
    868
                .offset_mut(bogus[counter as usize].x as isize)
    869
                .as_mut())
    870
            .offset_mut(bogus[counter as usize].y as isize)
    867
                .as_mut() = counter + 2i32;
    871
            .as_mut() = counter + 2i32;
    868
            loop {
    872
            loop {
    869
                index = (rand() as libc::c_ulong).wrapping_rem(
    873
                index = (rand() as libc::c_ulong).wrapping_rem(
    870
                    (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    874
                    (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    871
                        .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong),
    875
                        .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong),
    872
                ) as libc::c_int;
    876
                ) as libc::c_int;

    1008
            if !(old_x == robot.x && old_y == robot.y) {
    1012
            if !(old_x == robot.x && old_y == robot.y) {
    1009
                if wmove(stdscr, old_y, old_x) == -1i32 {
    1013
                if wmove(stdscr, old_y, old_x) == -1i32 {
    1010
                } else {
    1014
                } else {
    1011
                    waddch(stdscr, ' ' as i32 as chtype);
    1015
                    waddch(stdscr, ' ' as i32 as chtype);
    1012
                };
    1016
                };
    1013
                *(*screen.offset(old_x as isize).as_mut())
    1017
                *(*screen.offset_mut(old_x as isize).as_mut())
    1014
                    .offset(old_y as isize)
    1018
                    .offset_mut(old_y as isize)
    1015
                    .as_mut() = -1i32;
    1019
                    .as_mut() = -1i32;
    1016
                draw(robot);
    1020
                draw(robot);
    1017
                wrefresh(stdscr);
    1021
                wrefresh(stdscr);
    1018
                *(*screen.offset(robot.x as isize).as_mut())
    1022
                *(*screen.offset_mut(robot.x as isize).as_mut())
    1019
                    .offset(robot.y as isize)
    1023
                    .offset_mut(robot.y as isize)
    1020
                    .as_mut() = 0i32;
    1024
                    .as_mut() = 0i32;
    1021
                old_x = robot.x;
    1025
                old_x = robot.x;
    1022
                old_y = robot.y
    1026
                old_y = robot.y
    1023
            }
    1027
            }
    1024
            input = wgetch(stdscr)
    1028
            input = wgetch(stdscr)

    1081
            }
    1085
            }
    1082
        }
    1086
        }
    1083
        if check_y < 3i32 || check_y > LINES - 1i32 || check_x < 0i32 || check_x > COLS - 1i32 {
    1087
        if check_y < 3i32 || check_y > LINES - 1i32 || check_x < 0i32 || check_x > COLS - 1i32 {
    1084
            return;
    1088
            return;
    1085
        }
    1089
        }
    1086
        if *(*screen.offset(check_x as isize).as_mut())
    1090
        if *(*screen.offset_mut(check_x as isize).as_mut())
    1087
            .offset(check_y as isize)
    1091
            .offset_mut(check_y as isize)
    1088
            .as_mut()
    1092
            .as_mut()
    1089
            != -1i32
    1093
            != -1i32
    1090
        {
    1094
        {
    1091
            match *(*screen.offset(check_x as isize).as_mut())
    1095
            match *(*screen.offset_mut(check_x as isize).as_mut())
    1092
                .offset(check_y as isize)
    1096
                .offset_mut(check_y as isize)
    1093
                .as_mut()
    1097
                .as_mut()
    1094
            {
    1098
            {
    1095
                0 => {}
    1099
                0 => {}
    1096
                1 => {
    1100
                1 => {
    1097
                    /*We didn't move, or we're stuck in a
    1101
                    /*We didn't move, or we're stuck in a

    1100
                    wclrtoeol(stdscr);
    1104
                    wclrtoeol(stdscr);
    1101
                    play_animation(input);
    1105
                    play_animation(input);
    1102
                }
    1106
                }
    1103
                _ => {
    1107
                _ => {
    1104
                    message(
    1108
                    message(
    1105
                        messages[bogus_messages[(*(*screen.offset(check_x as isize).as_mut())
    1109
                        messages[bogus_messages[(*(*screen.offset_mut(check_x as isize).as_mut())
    1106
                            .offset(check_y as isize)
    1110
                            .offset_mut(check_y as isize)
    1107
                            .as_mut()
    1111
                            .as_mut()
    1108
                            - 2i32) as usize] as usize]
    1112
                            - 2i32) as usize] as usize]
    1109
                            .as_ptr() as *mut i8,
    1113
                            .as_ptr() as *mut i8,
    1110
                    );
    1114
                    );
    1111
                }
    1115
                }

    This works fine for robotfindskitten, though in other codebases it may be necessary to properly distinguish mutable and immutable uses of offset.

    With this change, the code typechecks with screens new memory-safe type, so we could stop here. However, unlike CBlockPtr, CArray supports array indexing - ptr[i] - in place of the convoluted *arr.offset(i).as_mut() syntax. So we perform a simple rewrite to make the code a little easier to read:

    rewrite_expr
        'typed!(__e, ::c2rust_runtime::CArray<__t>).offset_mut(__f).as_mut()'
        '&mut __e[__f as usize]' ;
    rewrite_expr '*&mut __e' '__e' ;
    

    Diff #31

    src/robotfindskitten.rs
    713
                    / ::std::mem::size_of::<::c2rust_runtime::CArray<i32>>(),
    713
                    / ::std::mem::size_of::<::c2rust_runtime::CArray<i32>>(),
    714
            )
    714
            )
    715
        };
    715
        };
    716
        i = 0i32;
    716
        i = 0i32;
    717
        while i < COLS - 1i32 + 1i32 {
    717
        while i < COLS - 1i32 + 1i32 {
    718
            let ref mut fresh0 = *screen.offset_mut(i as isize).as_mut();
    718
            let ref mut fresh0 = screen[i as isize as usize];
    719
            *fresh0 = unsafe {
    719
            *fresh0 = unsafe {
    720
                ::c2rust_runtime::CArray::alloc(
    720
                ::c2rust_runtime::CArray::alloc(
    721
                    (::std::mem::size_of::() as libc::c_ulong)
    721
                    (::std::mem::size_of::() as libc::c_ulong)
    722
                        .wrapping_mul((LINES - 1i32 + 1i32) as libc::c_ulong) as usize
    722
                        .wrapping_mul((LINES - 1i32 + 1i32) as libc::c_ulong) as usize
    723
                        / ::std::mem::size_of::(),
    723
                        / ::std::mem::size_of::(),

    732
        empty.character = ' ' as i32 as libc::c_char;
    732
        empty.character = ' ' as i32 as libc::c_char;
    733
        counter = 0i32;
    733
        counter = 0i32;
    734
        while counter <= COLS - 1i32 {
    734
        while counter <= COLS - 1i32 {
    735
            counter2 = 0i32;
    735
            counter2 = 0i32;
    736
            while counter2 <= LINES - 1i32 {
    736
            while counter2 <= LINES - 1i32 {
    737
                *(*screen.offset_mut(counter as isize).as_mut())
    737
                screen[counter as isize as usize][counter2 as isize as usize] = -1i32;
    738
                    .offset_mut(counter2 as isize)
    739
                    .as_mut() = -1i32;
    740
                counter2 += 1
    738
                counter2 += 1
    741
            }
    739
            }
    742
            counter += 1
    740
            counter += 1
    743
        }
    741
        }
    744
        counter = 0i32;
    742
        counter = 0i32;

    779
        robot.x = rand() % (COLS - 1i32) + 1i32;
    777
        robot.x = rand() % (COLS - 1i32) + 1i32;
    780
        robot.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
    778
        robot.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
    781
        robot.character = '#' as i32 as libc::c_char;
    779
        robot.character = '#' as i32 as libc::c_char;
    782
        robot.color = 0i32;
    780
        robot.color = 0i32;
    783
        robot.bold = 0 != 0i32;
    781
        robot.bold = 0 != 0i32;
    784
        *(*screen.offset_mut(robot.x as isize).as_mut())
    782
        screen[robot.x as isize as usize][robot.y as isize as usize] = 0i32;
    785
            .offset_mut(robot.y as isize)
    786
            .as_mut() = 0i32;
    787
    }
    783
    }
    788
    /*Global variables. Bite me, it's fun.*/
    784
    /*Global variables. Bite me, it's fun.*/
    789
    #[no_mangle]
    785
    #[no_mangle]
    790
    pub static mut robot: screen_object = screen_object {
    786
    pub static mut robot: screen_object = screen_object {
    791
        x: 0,
    787
        x: 0,

    797
    #[no_mangle]
    793
    #[no_mangle]
    798
    pub unsafe extern "C" fn initialize_kitten() {
    794
    pub unsafe extern "C" fn initialize_kitten() {
    799
        loop {
    795
        loop {
    800
            kitten.x = rand() % (COLS - 1i32) + 1i32;
    796
            kitten.x = rand() % (COLS - 1i32) + 1i32;
    801
            kitten.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
    797
            kitten.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
    802
            if !(*(*screen.offset_mut(kitten.x as isize).as_mut())
    798
            if !(screen[kitten.x as isize as usize][kitten.y as isize as usize] != -1i32) {
    803
                .offset_mut(kitten.y as isize)
    804
                .as_mut()
    805
                != -1i32)
    806
            {
    807
                break;
    799
                break;
    808
            }
    800
            }
    809
        }
    801
        }
    810
        loop {
    802
        loop {
    811
            kitten.character = (rand() % (126i32 - '!' as i32 + 1i32) + '!' as i32) as libc::c_char;
    803
            kitten.character = (rand() % (126i32 - '!' as i32 + 1i32) + '!' as i32) as libc::c_char;
    812
            if !(0 == validchar(kitten.character)) {
    804
            if !(0 == validchar(kitten.character)) {
    813
                break;
    805
                break;
    814
            }
    806
            }
    815
        }
    807
        }
    816
        *(*screen.offset_mut(kitten.x as isize).as_mut())
    808
        screen[kitten.x as isize as usize][kitten.y as isize as usize] = 1i32;
    817
            .offset_mut(kitten.y as isize)
    818
            .as_mut() = 1i32;
    819
        kitten.color = rand() % 6i32 + 1i32;
    809
        kitten.color = rand() % 6i32 + 1i32;
    820
        kitten.bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
    810
        kitten.bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
    821
    }
    811
    }
    822
    #[no_mangle]
    812
    #[no_mangle]
    823
    pub static mut kitten: screen_object = screen_object {
    813
    pub static mut kitten: screen_object = screen_object {

    852
                }
    842
                }
    853
            }
    843
            }
    854
            loop {
    844
            loop {
    855
                bogus[counter as usize].x = rand() % (COLS - 1i32) + 1i32;
    845
                bogus[counter as usize].x = rand() % (COLS - 1i32) + 1i32;
    856
                bogus[counter as usize].y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
    846
                bogus[counter as usize].y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
    857
                if !(*(*screen
    847
                if !(screen[bogus[counter as usize].x as isize as usize]
    858
                    .offset_mut(bogus[counter as usize].x as isize)
    848
                    [bogus[counter as usize].y as isize as usize]
    859
                    .as_mut())
    860
                .offset_mut(bogus[counter as usize].y as isize)
    861
                .as_mut()
    862
                    != -1i32)
    849
                    != -1i32)
    863
                {
    850
                {
    864
                    break;
    851
                    break;
    865
                }
    852
                }
    866
            }
    853
            }
    867
            *(*screen
    868
                .offset_mut(bogus[counter as usize].x as isize)
    869
                .as_mut())
    870
            .offset_mut(bogus[counter as usize].y as isize)
    854
            screen[bogus[counter as usize].x as isize as usize]
    871
            .as_mut() = counter + 2i32;
    855
                [bogus[counter as usize].y as isize as usize] = counter + 2i32;
    872
            loop {
    856
            loop {
    873
                index = (rand() as libc::c_ulong).wrapping_rem(
    857
                index = (rand() as libc::c_ulong).wrapping_rem(
    874
                    (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    858
                    (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    875
                        .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong),
    859
                        .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong),
    876
                ) as libc::c_int;
    860
                ) as libc::c_int;

    1012
            if !(old_x == robot.x && old_y == robot.y) {
    996
            if !(old_x == robot.x && old_y == robot.y) {
    1013
                if wmove(stdscr, old_y, old_x) == -1i32 {
    997
                if wmove(stdscr, old_y, old_x) == -1i32 {
    1014
                } else {
    998
                } else {
    1015
                    waddch(stdscr, ' ' as i32 as chtype);
    999
                    waddch(stdscr, ' ' as i32 as chtype);
    1016
                };
    1000
                };
    1017
                *(*screen.offset_mut(old_x as isize).as_mut())
    1001
                screen[old_x as isize as usize][old_y as isize as usize] = -1i32;
    1018
                    .offset_mut(old_y as isize)
    1019
                    .as_mut() = -1i32;
    1020
                draw(robot);
    1002
                draw(robot);
    1021
                wrefresh(stdscr);
    1003
                wrefresh(stdscr);
    1022
                *(*screen.offset_mut(robot.x as isize).as_mut())
    1004
                screen[robot.x as isize as usize][robot.y as isize as usize] = 0i32;
    1023
                    .offset_mut(robot.y as isize)
    1024
                    .as_mut() = 0i32;
    1025
                old_x = robot.x;
    1005
                old_x = robot.x;
    1026
                old_y = robot.y
    1006
                old_y = robot.y
    1027
            }
    1007
            }
    1028
            input = wgetch(stdscr)
    1008
            input = wgetch(stdscr)
    1029
        }
    1009
        }

    1085
            }
    1065
            }
    1086
        }
    1066
        }
    1087
        if check_y < 3i32 || check_y > LINES - 1i32 || check_x < 0i32 || check_x > COLS - 1i32 {
    1067
        if check_y < 3i32 || check_y > LINES - 1i32 || check_x < 0i32 || check_x > COLS - 1i32 {
    1088
            return;
    1068
            return;
    1089
        }
    1069
        }
    1090
        if *(*screen.offset_mut(check_x as isize).as_mut())
    1070
        if screen[check_x as isize as usize][check_y as isize as usize] != -1i32 {
    1091
            .offset_mut(check_y as isize)
    1071
            match screen[check_x as isize as usize][check_y as isize as usize] {
    1092
            .as_mut()
    1093
            != -1i32
    1094
        {
    1095
            match *(*screen.offset_mut(check_x as isize).as_mut())
    1096
                .offset_mut(check_y as isize)
    1097
                .as_mut()
    1098
            {
    1099
                0 => {}
    1072
                0 => {}
    1100
                1 => {
    1073
                1 => {
    1101
                    /*We didn't move, or we're stuck in a
    1074
                    /*We didn't move, or we're stuck in a
    1102
                    time warp or something.*/
    1075
                    time warp or something.*/
    1103
                    wmove(stdscr, 1i32, 0i32);
    1076
                    wmove(stdscr, 1i32, 0i32);
    1104
                    wclrtoeol(stdscr);
    1077
                    wclrtoeol(stdscr);
    1105
                    play_animation(input);
    1078
                    play_animation(input);
    1106
                }
    1079
                }
    1107
                _ => {
    1080
                _ => {
    1108
                    message(
    1081
                    message(
    1109
                        messages[bogus_messages[(*(*screen.offset_mut(check_x as isize).as_mut())
    1082
                        messages[bogus_messages[(screen[check_x as isize as usize]
    1110
                            .offset_mut(check_y as isize)
    1083
                            [check_y as isize as usize]
    1111
                            .as_mut()
    1112
                            - 2i32) as usize] as usize]
    1084
                            - 2i32) as usize] as usize]
    1113
                            .as_ptr() as *mut i8,
    1085
                            .as_ptr() as *mut i8,
    1114
                    );
    1086
                    );
    1115
                }
    1087
                }
    1116
            }
    1088
            }

    Finally, we remove unsafety from screen's static initializer. It currently calls CArray::from_ptr(0 as *mut _), which is unsafe because CArray::from_ptr requires its pointer argument to must satisfy certain properties. But CArray also provides a safe method specifically for initializing a CArray to null, which we can use instead:

    rewrite_expr
        '::c2rust_runtime::CArray::from_ptr(cast!(0))'
        '::c2rust_runtime::CArray::empty()' ;
    

    Diff #32

    src/robotfindskitten.rs
    765
    /* This array contains our internal representation of the screen. The
    765
    /* This array contains our internal representation of the screen. The
    766
    array is bigger than it needs to be, as we don't need to keep track
    766
    array is bigger than it needs to be, as we don't need to keep track
    767
    of the first few rows of the screen. But that requires making an
    767
    of the first few rows of the screen. But that requires making an
    768
    offset function and using that everywhere. So not right now. */
    768
    offset function and using that everywhere. So not right now. */
    769
    #[no_mangle]
    769
    #[no_mangle]
    770
    pub static mut screen: ::c2rust_runtime::CArray<::c2rust_runtime::CArray> = unsafe {
    770
    pub static mut screen: ::c2rust_runtime::CArray<::c2rust_runtime::CArray> =
    771
        ::c2rust_runtime::CArray::from_ptr(
    771
        unsafe { ::c2rust_runtime::CArray::empty() };
    772
            0 as *const *mut libc::c_int as *mut *mut libc::c_int as *mut ::c2rust_runtime::CArray<i32>,
    773
        )
    774
    };
    775
    #[no_mangle]
    772
    #[no_mangle]
    776
    pub unsafe extern "C" fn initialize_robot() {
    773
    pub unsafe extern "C" fn initialize_robot() {
    777
        robot.x = rand() % (COLS - 1i32) + 1i32;
    774
        robot.x = rand() % (COLS - 1i32) + 1i32;
    778
        robot.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
    775
        robot.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
    779
        robot.character = '#' as i32 as libc::c_char;
    776
        robot.character = '#' as i32 as libc::c_char;

    This completes the refactoring of screen, as all raw pointer manipulations have been replaced with safe CArray method calls. The only remaining unsafety arises from the fact that screen is a static mut, which we address in a later refactoring step.

    commit ;
    

    Using the pancurses library

    The pancurses library provides safe wrappers around ncurses APIs. Since the pancurses and ncurses APIs are so similar, we can automatically convert the unsafe ncurses FFI calls in robotfindskitten to safe pancurses calls, avoiding the need to maintain safe wrappers in robotfindskitten itself.

    There are two preliminary steps before we do the actual conversion. First, we must import the pancurses library:

    select target 'crate;' ;
    create_item 'extern crate pancurses;' inside ;
    

    Diff #33

    src/robotfindskitten.rs
    7
        unused_mut
    7
        unused_mut
    8
    )]
    8
    )]
    9
    #![feature(const_raw_ptr_to_usize_cast, extern_types, libc)]
    9
    #![feature(const_raw_ptr_to_usize_cast, extern_types, libc)]
    10
    extern crate c2rust_runtime;
    10
    extern crate c2rust_runtime;
    11
    extern crate libc;
    11
    extern crate libc;
    12
    extern crate pancurses;
    12
    extern "C" {
    13
    extern "C" {
    13
        pub type ldat;
    14
        pub type ldat;
    14
        #[no_mangle]
    15
        #[no_mangle]
    15
        fn printf(_: *const libc::c_char, ...) -> libc::c_int;
    16
        fn printf(_: *const libc::c_char, ...) -> libc::c_int;
    16
        #[no_mangle]
    17
        #[no_mangle]

    And second, we must create a global variable to store the main pancurses Window:

    select target 'crate;' ;
    create_item 'static mut win: Option<::pancurses::Window> = None;' inside ;
    

    Diff #34

    src/robotfindskitten.rs
    5
        non_snake_case,
    5
        non_snake_case,
    6
        non_upper_case_globals,
    6
        non_upper_case_globals,
    7
        unused_mut
    7
        unused_mut
    8
    )]
    8
    )]
    9
    #![feature(const_raw_ptr_to_usize_cast, extern_types, libc)]
    9
    #![feature(const_raw_ptr_to_usize_cast, extern_types, libc)]
    10
    static mut win: Option<::pancurses::Window> = None;
    10
    extern crate c2rust_runtime;
    11
    extern crate c2rust_runtime;
    11
    extern crate libc;
    12
    extern crate libc;
    12
    extern crate pancurses;
    13
    extern crate pancurses;
    13
    extern "C" {
    14
    extern "C" {
    14
        pub type ldat;
    15
        pub type ldat;

    pancurses doesn't have an equivalent of the global stdscr window that ncurses provides. Instead, the pancurses initialization function creates an initial Window object that must be passed around to each function that updates the display. We store that initial Window in the global win variable so that it's accessible everywhere that stdscr is used.

    Note that making win a static mut makes it unsafe to access. However, a later refactoring pass will gather up all static muts, including win, and collect them into a stack-allocated struct, at which point accessing win will no longer be unsafe.

    General library calls

    We convert ncurses library calls to pancurses ones in a few stages.

    First, for functions that don't require a window object, we simply replace each ncurses function with its equivalent in the pancurses library:

    rewrite_expr 'nonl' '::pancurses::nonl' ;
    rewrite_expr 'noecho' '::pancurses::noecho' ;
    rewrite_expr 'cbreak' '::pancurses::cbreak' ;
    rewrite_expr 'has_colors' '::pancurses::has_colors' ;
    rewrite_expr 'start_color' '::pancurses::start_color' ;
    rewrite_expr 'endwin' '::pancurses::endwin' ;
    rewrite_expr 'init_pair' '::pancurses::init_pair' ;
    

    Diff #35

    src/robotfindskitten.rs
    638
    #[no_mangle]
    638
    #[no_mangle]
    639
    pub unsafe extern "C" fn initialize_ncurses() {
    639
    pub unsafe extern "C" fn initialize_ncurses() {
    640
        signal(2i32, Some(finish));
    640
        signal(2i32, Some(finish));
    641
        initscr();
    641
        initscr();
    642
        keypad(stdscr, 0 != 1i32);
    642
        keypad(stdscr, 0 != 1i32);
    643
        nonl();
    643
        ::pancurses::nonl();
    644
        intrflush(stdscr, 0 != 0i32);
    644
        intrflush(stdscr, 0 != 0i32);
    645
        noecho();
    645
        ::pancurses::noecho();
    646
        cbreak();
    646
        ::pancurses::cbreak();
    647
        if has_colors() {
    647
        if ::pancurses::has_colors() {
    648
            start_color();
    648
            ::pancurses::start_color();
    649
            init_pair(
    649
            ::pancurses::init_pair(
    650
                0i32 as libc::c_short,
    650
                0i32 as libc::c_short,
    651
                0i32 as libc::c_short,
    651
                0i32 as libc::c_short,
    652
                0i32 as libc::c_short,
    652
                0i32 as libc::c_short,
    653
            );
    653
            );
    654
            init_pair(
    654
            ::pancurses::init_pair(
    655
                2i32 as libc::c_short,
    655
                2i32 as libc::c_short,
    656
                2i32 as libc::c_short,
    656
                2i32 as libc::c_short,
    657
                0i32 as libc::c_short,
    657
                0i32 as libc::c_short,
    658
            );
    658
            );
    659
            init_pair(
    659
            ::pancurses::init_pair(
    660
                1i32 as libc::c_short,
    660
                1i32 as libc::c_short,
    661
                1i32 as libc::c_short,
    661
                1i32 as libc::c_short,
    662
                0i32 as libc::c_short,
    662
                0i32 as libc::c_short,
    663
            );
    663
            );
    664
            init_pair(
    664
            ::pancurses::init_pair(
    665
                6i32 as libc::c_short,
    665
                6i32 as libc::c_short,
    666
                6i32 as libc::c_short,
    666
                6i32 as libc::c_short,
    667
                0i32 as libc::c_short,
    667
                0i32 as libc::c_short,
    668
            );
    668
            );
    669
            init_pair(
    669
            ::pancurses::init_pair(
    670
                7i32 as libc::c_short,
    670
                7i32 as libc::c_short,
    671
                7i32 as libc::c_short,
    671
                7i32 as libc::c_short,
    672
                0i32 as libc::c_short,
    672
                0i32 as libc::c_short,
    673
            );
    673
            );
    674
            init_pair(
    674
            ::pancurses::init_pair(
    675
                5i32 as libc::c_short,
    675
                5i32 as libc::c_short,
    676
                5i32 as libc::c_short,
    676
                5i32 as libc::c_short,
    677
                0i32 as libc::c_short,
    677
                0i32 as libc::c_short,
    678
            );
    678
            );
    679
            init_pair(
    679
            ::pancurses::init_pair(
    680
                4i32 as libc::c_short,
    680
                4i32 as libc::c_short,
    681
                4i32 as libc::c_short,
    681
                4i32 as libc::c_short,
    682
                0i32 as libc::c_short,
    682
                0i32 as libc::c_short,
    683
            );
    683
            );
    684
            init_pair(
    684
            ::pancurses::init_pair(
    685
                3i32 as libc::c_short,
    685
                3i32 as libc::c_short,
    686
                3i32 as libc::c_short,
    686
                3i32 as libc::c_short,
    687
                0i32 as libc::c_short,
    687
                0i32 as libc::c_short,
    688
            );
    688
            );
    689
        };
    689
        };
    690
    }
    690
    }
    691
    unsafe extern "C" fn finish(mut sig: libc::c_int) {
    691
    unsafe extern "C" fn finish(mut sig: libc::c_int) {
    692
        endwin();
    692
        ::pancurses::endwin();
    693
        fmt_printf(format_args!(
    693
        fmt_printf(format_args!(
    694
            "{:}{:}{:}",
    694
            "{:}{:}{:}",
    695
            27i32 as u8 as char, '(' as i32 as u8 as char, 'B' as i32 as u8 as char
    695
            27i32 as u8 as char, '(' as i32 as u8 as char, 'B' as i32 as u8 as char
    696
        ));
    696
        ));
    697
        exit(0i32);
    697
        exit(0i32);

    Next, functions taking a window are replaced with method calls on the static win variable we defined earlier:

    rewrite_expr 'wrefresh(stdscr)' 'win.refresh()' ;
    rewrite_expr 'wrefresh(curscr)' 'win.refresh()' ;
    rewrite_expr 'keypad(stdscr, __bf)' 'win.keypad(__bf)' ;
    rewrite_expr 'wmove(stdscr, __my, __mx)' 'win.mv(__my, __mx)' ;
    rewrite_expr 'wclear(stdscr)' 'win.clear()' ;
    rewrite_expr 'wclrtoeol(stdscr)' 'win.clrtoeol()' ;
    rewrite_expr 'waddch(stdscr, __ch)' 'win.addch(__ch)' ;
    
    rewrite_expr
        'wattr_get(stdscr, __attrs, __pair, __e)'
        '{
            let tmp = win.attrget();
            *__attrs = tmp.0;
            *__pair = tmp.1;
            0
        }' ;
    rewrite_expr
        'wattrset(stdscr, __attrs)'
        'win.attrset(__attrs as ::pancurses::chtype)' ;
    

    Diff #36

    src/robotfindskitten.rs
    637
    /*Initialization and setup functions*/
    637
    /*Initialization and setup functions*/
    638
    #[no_mangle]
    638
    #[no_mangle]
    639
    pub unsafe extern "C" fn initialize_ncurses() {
    639
    pub unsafe extern "C" fn initialize_ncurses() {
    640
        signal(2i32, Some(finish));
    640
        signal(2i32, Some(finish));
    641
        initscr();
    641
        initscr();
    642
        keypad(stdscr, 0 != 1i32);
    642
        win.keypad(0 != 1i32);
    643
        ::pancurses::nonl();
    643
        ::pancurses::nonl();
    644
        intrflush(stdscr, 0 != 0i32);
    644
        intrflush(stdscr, 0 != 0i32);
    645
        ::pancurses::noecho();
    645
        ::pancurses::noecho();
    646
        ::pancurses::cbreak();
    646
        ::pancurses::cbreak();
    647
        if ::pancurses::has_colors() {
    647
        if ::pancurses::has_colors() {

    890
           draw(bogus[counter as usize]);
    890
           draw(bogus[counter as usize]);
    891
            counter += 1
    891
            counter += 1
    892
        }
    892
        }
    893
        draw(kitten);
    893
        draw(kitten);
    894
        draw(robot);
    894
        draw(robot);
    895
        wrefresh(stdscr);
    895
        win.refresh();
    896
    }
    896
    }
    897
    #[no_mangle]
    897
    #[no_mangle]
    898
    pub unsafe extern "C" fn draw(mut o: screen_object) {
    898
    pub unsafe extern "C" fn draw(mut o: screen_object) {
    899
        full_draw(o, 0 != 0i32);
    899
        full_draw(o, 0 != 0i32);
    900
    }
    900
    }

    923
           new |= 1u64 << 14i32 + 8i32
    923
           new |= 1u64 << 14i32 + 8i32
    924
        }
    924
        }
    925
        if o.bold {
    925
        if o.bold {
    926
            new |= 1u64 << 13i32 + 8i32
    926
            new |= 1u64 << 13i32 + 8i32
    927
        }
    927
        }
    928
        wattrset(stdscr, new as libc::c_int);
    928
        win.attrset(new as libc::c_int as ::pancurses::chtype);
    929
        if in_place {
    929
        if in_place {
    930
            fmt_printw(format_args!(
    930
            fmt_printw(format_args!(
    931
                "{:}",
    931
                "{:}",
    932
                o.character as libc::c_int as u8 as char
    932
                o.character as libc::c_int as u8 as char
    933
            ));
    933
            ));

    935
           fmt_mvprintw(
    935
           fmt_mvprintw(
    936
                o.y,
    936
                o.y,
    937
                o.x,
    937
                o.x,
    938
                format_args!("{:}", o.character as libc::c_int as u8 as char),
    938
                format_args!("{:}", o.character as libc::c_int as u8 as char),
    939
            );
    939
            );
    940
            wmove(stdscr, o.y, o.x);
    940
            win.mv(o.y, o.x);
    941
        }
    941
        }
    942
        wattrset(stdscr, old as libc::c_int);
    942
        win.attrset(old as libc::c_int as ::pancurses::chtype);
    943
    }
    943
    }
    944
    #[no_mangle]
    944
    #[no_mangle]
    945
    pub unsafe extern "C" fn instructions() {
    945
    pub unsafe extern "C" fn instructions() {
    946
        let mut dummy: libc::c_char = 0;
    946
        let mut dummy: libc::c_char = 0;
    947
        fmt_mvprintw(
    947
        fmt_mvprintw(

    973
       ));
    973
       ));
    974
        fmt_printw(format_args!(
    974
        fmt_printw(format_args!(
    975
            "the Esc key. See the documentation for more information.\n\n"
    975
            "the Esc key. See the documentation for more information.\n\n"
    976
        ));
    976
        ));
    977
        fmt_printw(format_args!("Press any key to start.\n"));
    977
        fmt_printw(format_args!("Press any key to start.\n"));
    978
        wrefresh(stdscr);
    978
        win.refresh();
    979
        dummy = wgetch(stdscr) as libc::c_char;
    979
        dummy = wgetch(stdscr) as libc::c_char;
    980
        wclear(stdscr);
    980
        win.clear();
    981
    }
    981
    }
    982
    #[no_mangle]
    982
    #[no_mangle]
    983
    pub unsafe extern "C" fn draw_in_place(mut o: screen_object) {
    983
    pub unsafe extern "C" fn draw_in_place(mut o: screen_object) {
    984
        full_draw(o, 0 != 1i32);
    984
        full_draw(o, 0 != 1i32);
    985
    }
    985
    }

    991
       let mut input: libc::c_int = 0;
    991
       let mut input: libc::c_int = 0;
    992
        input = wgetch(stdscr);
    992
        input = wgetch(stdscr);
    993
        while input != 27i32 && input != 'q' as i32 && input != 'Q' as i32 {
    993
        while input != 27i32 && input != 'q' as i32 && input != 'Q' as i32 {
    994
            process_input(input);
    994
            process_input(input);
    995
            if !(old_x == robot.x && old_y == robot.y) {
    995
            if !(old_x == robot.x && old_y == robot.y) {
    996
                if wmove(stdscr, old_y, old_x) == -1i32 {
    996
                if win.mv(old_y, old_x) == -1i32 {
    997
                } else {
    997
                } else {
    998
                    waddch(stdscr, ' ' as i32 as chtype);
    998
                    win.addch(' ' as i32 as chtype);
    999
                };
    999
                };
    1000
                screen[old_x as isize as usize][old_y as isize as usize] = -1i32;
    1000
                screen[old_x as isize as usize][old_y as isize as usize] = -1i32;
    1001
                draw(robot);
    1001
                draw(robot);
    1002
                wrefresh(stdscr);
    1002
                win.refresh();
    1003
                screen[robot.x as isize as usize][robot.y as isize as usize] = 0i32;
    1003
                screen[robot.x as isize as usize][robot.y as isize as usize] = 0i32;
    1004
                old_x = robot.x;
    1004
                old_x = robot.x;
    1005
                old_y = robot.y
    1005
                old_y = robot.y
    1006
            }
    1006
            }
    1007
            input = wgetch(stdscr)
    1007
            input = wgetch(stdscr)
    1008
        }
    1008
        }
    1009
        message(b"Bye!\x00" as *const u8 as *const libc::c_char as *mut libc::c_char);
    1009
        message(b"Bye!\x00" as *const u8 as *const libc::c_char as *mut libc::c_char);
    1010
        wrefresh(stdscr);
    1010
        win.refresh();
    1011
        finish(0i32);
    1011
        finish(0i32);
    1012
    }
    1012
    }
    1013
    #[no_mangle]
    1013
    #[no_mangle]
    1014
    pub unsafe extern "C" fn message(mut message_0: *mut libc::c_char) {
    1014
    pub unsafe extern "C" fn message(mut message_0: *mut libc::c_char) {
    1015
        wmove(stdscr, 1i32, 0i32);
    1015
        win.mv(1i32, 0i32);
    1016
        wclrtoeol(stdscr);
    1016
        win.clrtoeol();
    1017
        fmt_mvprintw(
    1017
        fmt_mvprintw(
    1018
            1i32,
    1018
            1i32,
    1019
            0i32,
    1019
            0i32,
    1020
            format_args!("{:.*}", COLS as usize, unsafe {
    1020
            format_args!("{:.*}", COLS as usize, unsafe {
    1021
                ::std::ffi::CStr::from_ptr(message_0 as *const libc::c_char)
    1021
                ::std::ffi::CStr::from_ptr(message_0 as *const libc::c_char)
    1022
                    .to_str()
    1022
                    .to_str()
    1023
                    .unwrap()
    1023
                    .unwrap()
    1024
            }),
    1024
            }),
    1025
        );
    1025
        );
    1026
        wmove(stdscr, robot.y, robot.x);
    1026
        win.mv(robot.y, robot.x);
    1027
        wrefresh(stdscr);
    1027
        win.refresh();
    1028
    }
    1028
    }
    1029
    #[no_mangle]
    1029
    #[no_mangle]
    1030
    pub unsafe extern "C" fn process_input(mut input: libc::c_int) {
    1030
    pub unsafe extern "C" fn process_input(mut input: libc::c_int) {
    1031
        let mut check_x: libc::c_int = robot.x;
    1031
        let mut check_x: libc::c_int = robot.x;
    1032
        let mut check_y: libc::c_int = robot.y;
    1032
        let mut check_y: libc::c_int = robot.y;
    1033
        match input {
    1033
        match input {
    1034
            12 => {
    1034
            12 => {
    1035
                wrefresh(curscr);
    1035
                win.refresh();
    1036
            }
    1036
            }
    1037
            259 | 107 | 75 | 16 => check_y -= 1,
    1037
            259 | 107 | 75 | 16 => check_y -= 1,
    1038
            262 | 121 | 89 => {
    1038
            262 | 121 | 89 => {
    1039
                check_x -= 1;
    1039
                check_x -= 1;
    1040
                check_y -= 1
    1040
                check_y -= 1

    1070
           match screen[check_x as isize as usize][check_y as isize as usize] {
    1070
           match screen[check_x as isize as usize][check_y as isize as usize] {
    1071
                0 => {}
    1071
                0 => {}
    1072
                1 => {
    1072
                1 => {
    1073
                    /*We didn't move, or we're stuck in a
    1073
                    /*We didn't move, or we're stuck in a
    1074
                    time warp or something.*/
    1074
                    time warp or something.*/
    1075
                    wmove(stdscr, 1i32, 0i32);
    1075
                    win.mv(1i32, 0i32);
    1076
                    wclrtoeol(stdscr);
    1076
                    win.clrtoeol();
    1077
                    play_animation(input);
    1077
                    play_animation(input);
    1078
                }
    1078
                }
    1079
                _ => {
    1079
                _ => {
    1080
                    message(
    1080
                    message(
    1081
                        messages[bogus_messages[(screen[check_x as isize as usize]
    1081
                        messages[bogus_messages[(screen[check_x as isize as usize]

    1093
    #[no_mangle]
    1093
    #[no_mangle]
    1094
    pub unsafe extern "C" fn play_animation(mut input: libc::c_int) {
    1094
    pub unsafe extern "C" fn play_animation(mut input: libc::c_int) {
    1095
        let mut counter: libc::c_int = 0;
    1095
        let mut counter: libc::c_int = 0;
    1096
        counter = 4i32;
    1096
        counter = 4i32;
    1097
        while counter > 0i32 {
    1097
        while counter > 0i32 {
    1098
            if wmove(stdscr, 1i32, 50i32 + counter + 1i32) == -1i32 {
    1098
            if win.mv(1i32, 50i32 + counter + 1i32) == -1i32 {
    1099
            } else {
    1099
            } else {
    1100
                waddch(stdscr, ' ' as i32 as chtype);
    1100
                win.addch(' ' as i32 as chtype);
    1101
            };
    1101
            };
    1102
            wmove(stdscr, 1i32, 50i32 + counter);
    1102
            win.mv(1i32, 50i32 + counter);
    1103
            if input == 0o405i32 || input == 0o402i32 || input == 0o540i32 || input == 0o535i32 {
    1103
            if input == 0o405i32 || input == 0o402i32 || input == 0o540i32 || input == 0o535i32 {
    1104
                draw_in_place(kitten);
    1104
                draw_in_place(kitten);
    1105
            } else {
    1105
            } else {
    1106
                draw_in_place(robot);
    1106
                draw_in_place(robot);
    1107
            }
    1107
            }
    1108
            if wmove(stdscr, 1i32, 50i32 - counter) == -1i32 {
    1108
            if win.mv(1i32, 50i32 - counter) == -1i32 {
    1109
            } else {
    1109
            } else {
    1110
                waddch(stdscr, ' ' as i32 as chtype);
    1110
                win.addch(' ' as i32 as chtype);
    1111
            };
    1111
            };
    1112
            wmove(stdscr, 1i32, 50i32 - counter + 1i32);
    1112
            win.mv(1i32, 50i32 - counter + 1i32);
    1113
            if input == 0o405i32 || input == 0o402i32 || input == 0o540i32 || input == 0o535i32 {
    1113
            if input == 0o405i32 || input == 0o402i32 || input == 0o540i32 || input == 0o535i32 {
    1114
                draw_in_place(robot);
    1114
                draw_in_place(robot);
    1115
            } else {
    1115
            } else {
    1116
                draw_in_place(kitten);
    1116
                draw_in_place(kitten);
    1117
            }
    1117
            }
    1118
            wrefresh(stdscr);
    1118
            win.refresh();
    1119
            sleep(1i32 as libc::c_uint);
    1119
            sleep(1i32 as libc::c_uint);
    1120
            counter -= 1
    1120
            counter -= 1
    1121
        }
    1121
        }
    1122
        wmove(stdscr, 1i32, 0i32);
    1122
        win.mv(1i32, 0i32);
    1123
        waddnstr(
    1123
        waddnstr(
    1124
            stdscr,
    1124
            stdscr,
    1125
            b"You found kitten! Way to go, robot!\x00" as *const u8 as *const libc::c_char,
    1125
            b"You found kitten! Way to go, robot!\x00" as *const u8 as *const libc::c_char,
    1126
            -1i32,
    1126
            -1i32,
    1127
        );
    1127
        );
    1128
        wrefresh(stdscr);
    1128
        win.refresh();
    1129
        finish(0i32);
    1129
        finish(0i32);
    1130
    }
    1130
    }
    1131
    unsafe fn main_0(mut argc: libc::c_int, mut argv: *mut *mut libc::c_char) -> libc::c_int {
    1131
    unsafe fn main_0(mut argc: libc::c_int, mut argv: *mut *mut libc::c_char) -> libc::c_int {
    1132
        if argc == 1i32 {
    1132
        if argc == 1i32 {
    1133
            num_bogus = 20i32
    1133
            num_bogus = 20i32

    For simplicity, we write win.f(...) in the rewrite_expr replacement arguments, even though win is actually an Option<Window>, not a Window. Later, we replace win with win.as_ref().unwrap() throughout the crate to correct the resulting type errors.

    We next replace some ncurses global variables with calls to corresponding pancurses functions:

    rewrite_expr 'LINES' 'win.get_max_y()' ;
    rewrite_expr 'COLS' 'win.get_max_x()' ;
    

    Diff #37

    src/robotfindskitten.rs
    709
       };
    709
       };
    710
        let mut i: libc::c_int = 0i32;
    710
        let mut i: libc::c_int = 0i32;
    711
        screen = unsafe {
    711
        screen = unsafe {
    712
            ::c2rust_runtime::CArray::alloc(
    712
            ::c2rust_runtime::CArray::alloc(
    713
                (::std::mem::size_of::<*mut libc::c_int>() as libc::c_ulong)
    713
                (::std::mem::size_of::<*mut libc::c_int>() as libc::c_ulong)
    714
                    .wrapping_mul((COLS - 1i32 + 1i32) as libc::c_ulong) as usize
    714
                    .wrapping_mul((win.get_max_x() - 1i32 + 1i32) as libc::c_ulong)
    715
                    as usize
    715
                    / ::std::mem::size_of::<::c2rust_runtime::CArray<i32>>(),
    716
                    / ::std::mem::size_of::<::c2rust_runtime::CArray<i32>>(),
    716
            )
    717
            )
    717
        };
    718
        };
    718
        i = 0i32;
    719
        i = 0i32;
    719
        while i < COLS - 1i32 + 1i32 {
    720
        while i < win.get_max_x() - 1i32 + 1i32 {
    720
            let ref mut fresh0 = screen[i as isize as usize];
    721
            let ref mut fresh0 = screen[i as isize as usize];
    721
            *fresh0 = unsafe {
    722
            *fresh0 = unsafe {
    722
                ::c2rust_runtime::CArray::alloc(
    723
                ::c2rust_runtime::CArray::alloc(
    723
                    (::std::mem::size_of::() as libc::c_ulong)
    724
                    (::std::mem::size_of::() as libc::c_ulong)
    724
                        .wrapping_mul((LINES - 1i32 + 1i32) as libc::c_ulong) as usize
    725
                        .wrapping_mul((win.get_max_y() - 1i32 + 1i32) as libc::c_ulong)
    726
                        as usize
    725
                        / ::std::mem::size_of::(),
    727
                        / ::std::mem::size_of::(),
    726
                )
    728
                )
    727
            };
    729
            };
    728
            i += 1
    730
            i += 1
    729
        }
    731
        }

    731
       empty.y = -1i32;
    733
       empty.y = -1i32;
    732
        empty.color = 0i32;
    734
        empty.color = 0i32;
    733
        empty.bold = 0 != 0i32;
    735
        empty.bold = 0 != 0i32;
    734
        empty.character = ' ' as i32 as libc::c_char;
    736
        empty.character = ' ' as i32 as libc::c_char;
    735
        counter = 0i32;
    737
        counter = 0i32;
    736
        while counter <= COLS - 1i32 {
    738
        while counter <= win.get_max_x() - 1i32 {
    737
            counter2 = 0i32;
    739
            counter2 = 0i32;
    738
            while counter2 <= LINES - 1i32 {
    740
            while counter2 <= win.get_max_y() - 1i32 {
    739
                screen[counter as isize as usize][counter2 as isize as usize] = -1i32;
    741
                screen[counter as isize as usize][counter2 as isize as usize] = -1i32;
    740
                counter2 += 1
    742
                counter2 += 1
    741
            }
    743
            }
    742
            counter += 1
    744
            counter += 1
    743
        }
    745
        }

    771
    #[no_mangle]
    773
    #[no_mangle]
    772
    pub static mut screen: ::c2rust_runtime::CArray<::c2rust_runtime::CArray> =
    774
    pub static mut screen: ::c2rust_runtime::CArray<::c2rust_runtime::CArray> =
    773
        unsafe { ::c2rust_runtime::CArray::empty() };
    775
        unsafe { ::c2rust_runtime::CArray::empty() };
    774
    #[no_mangle]
    776
    #[no_mangle]
    775
    pub unsafe extern "C" fn initialize_robot() {
    777
    pub unsafe extern "C" fn initialize_robot() {
    776
        robot.x = rand() % (COLS - 1i32) + 1i32;
    778
        robot.x = rand() % (win.get_max_x() - 1i32) + 1i32;
    777
        robot.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
    779
        robot.y = rand() % (win.get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
    778
        robot.character = '#' as i32 as libc::c_char;
    780
        robot.character = '#' as i32 as libc::c_char;
    779
        robot.color = 0i32;
    781
        robot.color = 0i32;
    780
        robot.bold = 0 != 0i32;
    782
        robot.bold = 0 != 0i32;
    781
        screen[robot.x as isize as usize][robot.y as isize as usize] = 0i32;
    783
        screen[robot.x as isize as usize][robot.y as isize as usize] = 0i32;
    782
    }
    784
    }

    790
       character: 0,
    792
       character: 0,
    791
    };
    793
    };
    792
    #[no_mangle]
    794
    #[no_mangle]
    793
    pub unsafe extern "C" fn initialize_kitten() {
    795
    pub unsafe extern "C" fn initialize_kitten() {
    794
        loop {
    796
        loop {
    795
            kitten.x = rand() % (COLS - 1i32) + 1i32;
    797
            kitten.x = rand() % (win.get_max_x() - 1i32) + 1i32;
    796
            kitten.y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
    798
            kitten.y = rand() % (win.get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
    797
            if !(screen[kitten.x as isize as usize][kitten.y as isize as usize] != -1i32) {
    799
            if !(screen[kitten.x as isize as usize][kitten.y as isize as usize] != -1i32) {
    798
                break;
    800
                break;
    799
            }
    801
            }
    800
        }
    802
        }
    801
        loop {
    803
        loop {

    839
               if !(0 == validchar(bogus[counter as usize].character)) {
    841
               if !(0 == validchar(bogus[counter as usize].character)) {
    840
                    break;
    842
                    break;
    841
                }
    843
                }
    842
            }
    844
            }
    843
            loop {
    845
            loop {
    844
                bogus[counter as usize].x = rand() % (COLS - 1i32) + 1i32;
    846
                bogus[counter as usize].x = rand() % (win.get_max_x() - 1i32) + 1i32;
    845
                bogus[counter as usize].y = rand() % (LINES - 1i32 - 3i32 + 1i32) + 3i32;
    847
                bogus[counter as usize].y = rand() % (win.get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
    846
                if !(screen[bogus[counter as usize].x as isize as usize]
    848
                if !(screen[bogus[counter as usize].x as isize as usize]
    847
                    [bogus[counter as usize].y as isize as usize]
    849
                    [bogus[counter as usize].y as isize as usize]
    848
                    != -1i32)
    850
                    != -1i32)
    849
                {
    851
                {
    850
                    break;
    852
                    break;

    879
                   .to_str()
    881
                   .to_str()
    880
                    .unwrap()
    882
                    .unwrap()
    881
            }),
    883
            }),
    882
        );
    884
        );
    883
        counter = 0i32;
    885
        counter = 0i32;
    884
        while counter <= COLS - 1i32 {
    886
        while counter <= win.get_max_x() - 1i32 {
    885
            fmt_printw(format_args!("{:}", 95i32 as u8 as char));
    887
            fmt_printw(format_args!("{:}", 95i32 as u8 as char));
    886
            counter += 1
    888
            counter += 1
    887
        }
    889
        }
    888
        counter = 0i32;
    890
        counter = 0i32;
    889
        while counter < num_bogus {
    891
        while counter < num_bogus {

    1015
       win.mv(1i32, 0i32);
    1017
       win.mv(1i32, 0i32);
    1016
        win.clrtoeol();
    1018
        win.clrtoeol();
    1017
        fmt_mvprintw(
    1019
        fmt_mvprintw(
    1018
            1i32,
    1020
            1i32,
    1019
            0i32,
    1021
            0i32,
    1020
            format_args!("{:.*}", COLS as usize, unsafe {
    1022
            format_args!("{:.*}", win.get_max_x() as usize, unsafe {
    1021
                ::std::ffi::CStr::from_ptr(message_0 as *const libc::c_char)
    1023
                ::std::ffi::CStr::from_ptr(message_0 as *const libc::c_char)
    1022
                    .to_str()
    1024
                    .to_str()
    1023
                    .unwrap()
    1025
                    .unwrap()
    1024
            }),
    1026
            }),
    1025
        );
    1027
        );

    1061
                       as *mut libc::c_char,
    1063
                       as *mut libc::c_char,
    1062
                );
    1064
                );
    1063
                return;
    1065
                return;
    1064
            }
    1066
            }
    1065
        }
    1067
        }
    1066
        if check_y < 3i32 || check_y > LINES - 1i32 || check_x < 0i32 || check_x > COLS - 1i32 {
    1068
        if check_y < 3i32
    1069
            || check_y > win.get_max_y() - 1i32
    1070
            || check_x < 0i32
    1071
            || check_x > win.get_max_x() - 1i32
    1072
        {
    1067
            return;
    1073
            return;
    1068
        }
    1074
        }
    1069
        if screen[check_x as isize as usize][check_y as isize as usize] != -1i32 {
    1075
        if screen[check_x as isize as usize][check_y as isize as usize] != -1i32 {
    1070
            match screen[check_x as isize as usize][check_y as isize as usize] {
    1076
            match screen[check_x as isize as usize][check_y as isize as usize] {
    1071
                0 => {}
    1077
                0 => {}

    Finally, we handle a few special cases.

    waddnstr takes a string argument, which in general could be any *const c_char. However, robotfindskitten calls it only on string literals, which lets us perform a more specialized rewrite that avoids unsafe C string conversions:

    rewrite_expr
        'waddnstr(stdscr, __str as *const u8 as *const libc::c_char, __n)'
        "win.addnstr(::std::str::from_utf8(__str).unwrap().trim_end_matches('\0'),
                     __n as usize)" ;
    

    Diff #38

    src/robotfindskitten.rs
    1124
           win.refresh();
    1124
           win.refresh();
    1125
            sleep(1i32 as libc::c_uint);
    1125
            sleep(1i32 as libc::c_uint);
    1126
            counter -= 1
    1126
            counter -= 1
    1127
        }
    1127
        }
    1128
        win.mv(1i32, 0i32);
    1128
        win.mv(1i32, 0i32);
    1129
        waddnstr(
    1129
        win.addnstr(
    1130
            stdscr,
    1130
            ::std::str::from_utf8(b"You found kitten! Way to go, robot!\x00")
    1131
            b"You found kitten! Way to go, robot!\x00" as *const u8 as *const libc::c_char,
    1131
                .unwrap()
    1132
                .trim_end_matches('\u{0}'),
    1132
            -1i32,
    1133
            -1i32 as usize,
    1133
        );
    1134
        );
    1134
        win.refresh();
    1135
        win.refresh();
    1135
        finish(0i32);
    1136
        finish(0i32);
    1136
    }
    1137
    }
    1137
    unsafe fn main_0(mut argc: libc::c_int, mut argv: *mut *mut libc::c_char) -> libc::c_int {
    1138
    unsafe fn main_0(mut argc: libc::c_int, mut argv: *mut *mut libc::c_char) -> libc::c_int {

    intrflush has no pancurses equivalent, so we replace it with a no-op of the same type:

    rewrite_expr 'intrflush(__e, __f)' '0' ;
    

    Diff #39

    src/robotfindskitten.rs
    639
    pub unsafe extern "C" fn initialize_ncurses() {
    639
    pub unsafe extern "C" fn initialize_ncurses() {
    640
        signal(2i32, Some(finish));
    640
        signal(2i32, Some(finish));
    641
        initscr();
    641
        initscr();
    642
        win.keypad(0 != 1i32);
    642
        win.keypad(0 != 1i32);
    643
        ::pancurses::nonl();
    643
        ::pancurses::nonl();
    644
        intrflush(stdscr, 0 != 0i32);
    644
        0;
    645
        ::pancurses::noecho();
    645
        ::pancurses::noecho();
    646
        ::pancurses::cbreak();
    646
        ::pancurses::cbreak();
    647
        if ::pancurses::has_colors() {
    647
        if ::pancurses::has_colors() {
    648
            ::pancurses::start_color();
    648
            ::pancurses::start_color();
    649
            ::pancurses::init_pair(
    649
            ::pancurses::init_pair(

    That covers all of the "ordinary" ncurses functions used in robotfindskitten. The remaining subsections cover the more complex cases.

    String formatting

    We previously replaced calls to the ncurses printw and mvprintw string-formatting functions with code using Rust's safe string formatting macros. This removes unsafety from the call site, but uses wrapper functions (fmt_printw and fmt_mvprintw) that call unsafe code internally. But now that we are using the pancurses library, we can replace those wrappers with safer equivalents.

    select target 'item(fmt_printw);' ;
    create_item '
        fn fmt_printw(args: ::std::fmt::Arguments) -> libc::c_int {
            unsafe {
                win.printw(&format!("{}", args))
            }
        }
    ' after ;
    delete_items ;
    clear_marks ;
    
    select target 'item(fmt_mvprintw);' ;
    create_item '
        fn fmt_mvprintw(y: libc::c_int, x: libc::c_int,
                        args: ::std::fmt::Arguments) -> libc::c_int {
            unsafe {
                win.mvprintw(y, x, &format!("{}", args))
            }
        }
    ' after ;
    delete_items ;
    clear_marks ;
    

    Diff #40

    src/robotfindskitten.rs
    7
        unused_mut
    7
        unused_mut
    8
    )]
    8
    )]
    9
    #![feature(const_raw_ptr_to_usize_cast, extern_types, libc)]
    9
    #![feature(const_raw_ptr_to_usize_cast, extern_types, libc)]
    10
    static mut win: Option<::pancurses::Window> = None;
    10
    static mut win: Option<::pancurses::Window> = None;
    11
    extern crate c2rust_runtime;
    11
    extern crate c2rust_runtime;
    12
    extern crate libc;
    12
    extern crate libc;
    13
    extern crate pancurses;
    13
    extern crate pancurses;
    14
    extern "C" {
    14
    extern "C" {
    15
        pub type ldat;
    15
        pub type ldat;
    16
        #[no_mangle]
    16
        #[no_mangle]

    90
           opts: *mut libc::c_void,
    90
            opts: *mut libc::c_void,
    91
        ) -> libc::c_int;
    91
        ) -> libc::c_int;
    92
        fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
    92
        fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
    93
    }
    93
    }
    94
    fn fmt_mvprintw(y: libc::c_int, x: libc::c_int, args: ::std::fmt::Arguments) -> libc::c_int {
    94
    fn fmt_mvprintw(y: libc::c_int, x: libc::c_int, args: ::std::fmt::Arguments) -> libc::c_int {
    95
        unsafe {
    95
        unsafe { win.mvprintw(y, x, &format!("{}", args)) }
    96
            ::mvprintw(
    97
                y,
    98
                x,
    99
                b"%s\0" as *const u8 as *const libc::c_char,
    100
                ::std::ffi::CString::new(format!("{}", args))
    101
                    .unwrap()
    102
                    .as_ptr(),
    103
            )
    104
        }
    105
    }
    96
    }
    106
    fn fmt_printw(args: ::std::fmt::Arguments) -> libc::c_int {
    97
    fn fmt_printw(args: ::std::fmt::Arguments) -> libc::c_int {
    107
        unsafe {
    98
        unsafe { win.printw(&format!("{}", args)) }
    108
            ::printw(
    109
                b"%s\0" as *const u8 as *const libc::c_char,
    110
                ::std::ffi::CString::new(format!("{}", args))
    111
                    .unwrap()
    112
                    .as_ptr(),
    113
            )
    114
        }
    115
    }
    99
    }
    116
    fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
    100
    fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
    117
        print!("{}", args);
    101
        print!("{}", args);
    118
        0
    102
        0
    119
    }
    103
    }

    The wrappers still use unsafe code to access win, a static mut, but no longer make FFI calls or manipulate raw C strings. When we later remove all static muts from the program, these functions will become entirely safe.

    Input handling

    Adapting ncurses-based input handling to use pancurses requires some extra care. The pancurses getch function returns a Rust enum, while the ncurses version simply returns an integer. robotfindskitten matches those integers against various ncurses keycode constants, which, after macro expansion, become integer literals in the Rust code.

    The more idiomatic approach would be to replace each integer literal with the matching pancurses::Input enum variant when switching from ncurses getch to the pancurses version. However, we instead take the easier approach of converting pancurses::Input values back to ncurses integer keycodes, so the existing robotfindskitten input handling code can remain unchanged.

    First, we inject a translation function from pancurses to ncurses keycodes:

    select target 'item(initialize_ncurses);' ;
    create_item '
        fn encode_input(inp: Option<::pancurses::Input>) -> libc::c_int {
            use ::pancurses::Input::*;
            let inp = match inp {
                Some(x) => x,
                None => return -1,
            };
            match inp {
                // TODO: unicode inputs in the range 256 .. 512 can
                // collide with ncurses special keycodes
                Character(c) => c as u32 as libc::c_int,
                Unknown(i) => i,
                special => {
                    let idx = ::pancurses::SPECIAL_KEY_CODES.iter()
                        .position(|&k| k == special).unwrap();
                    let code = idx as i32 + ::pancurses::KEY_OFFSET;
                    if code > ::pancurses::KEY_F15 {
                        code + 48
                    } else {
                        code
                    }
                },
            }
        }
    ' after ;
    

    Diff #41

    src/robotfindskitten.rs
    619
     *Function definitions
    619
     *Function definitions
    620
     */
    620
     */
    621
    /*Initialization and setup functions*/
    621
    /*Initialization and setup functions*/
    622
    #[no_mangle]
    622
    #[no_mangle]
    623
    pub unsafe extern "C" fn initialize_ncurses() {
    623
    pub unsafe extern "C" fn initialize_ncurses() {
    624
        signal(2i32, Some(finish));
    624
        signal(2i32, Some(finish));
    625
        initscr();
    625
        initscr();
    626
        win.keypad(0 != 1i32);
    626
        win.keypad(0 != 1i32);
    627
        ::pancurses::nonl();
    627
        ::pancurses::nonl();

    670
                3i32 as libc::c_short,
    670
               3i32 as libc::c_short,
    671
                0i32 as libc::c_short,
    671
                0i32 as libc::c_short,
    672
            );
    672
            );
    673
        };
    673
        };
    674
    }
    674
    }
    675
    fn encode_input(inp: Option<::pancurses::Input>) -> libc::c_int {
    676
        use pancurses::Input::*;
    677
        let inp = match inp {
    678
            Some(x) => x,
    679
            None => return -1,
    680
        };
    681
        match inp {
    682
            // TODO: unicode inputs in the range 256 .. 512 can
    683
            // collide with ncurses special keycodes
    684
            Character(c) => c as u32 as libc::c_int,
    685
            Unknown(i) => i,
    686
            special => {
    687
                let idx = ::pancurses::SPECIAL_KEY_CODES
    688
                    .iter()
    689
                    .position(|&k| k == special)
    690
                    .unwrap();
    691
                let code = idx as i32 + ::pancurses::KEY_OFFSET;
    692
                if code > ::pancurses::KEY_F15 {
    693
                    code + 48
    694
                } else {
    695
                    code
    696
                }
    697
            }
    698
        }
    699
    }
    675
    unsafe extern "C" fn finish(mut sig: libc::c_int) {
    700
    unsafe extern "C" fn finish(mut sig: libc::c_int) {
    676
        ::pancurses::endwin();
    701
        ::pancurses::endwin();
    677
        fmt_printf(format_args!(
    702
        fmt_printf(format_args!(
    678
            "{:}{:}{:}",
    703
            "{:}{:}{:}",
    679
            27i32 as u8 as char, '(' as i32 as u8 as char, 'B' as i32 as u8 as char
    704
            27i32 as u8 as char, '(' as i32 as u8 as char, 'B' as i32 as u8 as char

    Then, we translate ncurses wgetch calls to use the pancurses getch method, wrapping the result in encode_input to keep the results unchanged.

    rewrite_expr 'wgetch(stdscr)' '::encode_input(win.getch())' ;
    

    Diff #42

    src/robotfindskitten.rs
    985
        fmt_printw(format_args!(
    985
        fmt_printw(format_args!(
    986
            "the Esc key. See the documentation for more information.\n\n"
    986
            "the Esc key. See the documentation for more information.\n\n"
    987
        ));
    987
        ));
    988
        fmt_printw(format_args!("Press any key to start.\n"));
    988
        fmt_printw(format_args!("Press any key to start.\n"));
    989
        win.refresh();
    989
        win.refresh();
    990
        dummy = wgetch(stdscr) as libc::c_char;
    990
        dummy = ::encode_input(win.getch()) as libc::c_char;
    991
        win.clear();
    991
        win.clear();
    992
    }
    992
    }
    993
    #[no_mangle]
    993
    #[no_mangle]
    994
    pub unsafe extern "C" fn draw_in_place(mut o: screen_object) {
    994
    pub unsafe extern "C" fn draw_in_place(mut o: screen_object) {
    995
        full_draw(o, 0 != 1i32);
    995
        full_draw(o, 0 != 1i32);

    998
    #[no_mangle]
    998
    #[no_mangle]
    999
    pub unsafe extern "C" fn play_game() {
    999
    pub unsafe extern "C" fn play_game() {
    1000
        let mut old_x: libc::c_int = robot.x;
    1000
        let mut old_x: libc::c_int = robot.x;
    1001
        let mut old_y: libc::c_int = robot.y;
    1001
        let mut old_y: libc::c_int = robot.y;
    1002
        let mut input: libc::c_int = 0;
    1002
        let mut input: libc::c_int = 0;
    1003
        input = wgetch(stdscr);
    1003
        input = ::encode_input(win.getch());
    1004
        while input != 27i32 && input != 'q' as i32 && input != 'Q' as i32 {
    1004
        while input != 27i32 && input != 'q' as i32 && input != 'Q' as i32 {
    1005
            process_input(input);
    1005
            process_input(input);
    1006
            if !(old_x == robot.x && old_y == robot.y) {
    1006
            if !(old_x == robot.x && old_y == robot.y) {
    1007
                if win.mv(old_y, old_x) == -1i32 {
    1007
                if win.mv(old_y, old_x) == -1i32 {
    1008
                } else {
    1008
                } else {

    1013
                win.refresh();
    1013
                win.refresh();
    1014
                screen[robot.x as isize as usize][robot.y as isize as usize] = 0i32;
    1014
                screen[robot.x as isize as usize][robot.y as isize as usize] = 0i32;
    1015
                old_x = robot.x;
    1015
                old_x = robot.x;
    1016
                old_y = robot.y
    1016
                old_y = robot.y
    1017
            }
    1017
            }
    1018
            input = wgetch(stdscr)
    1018
            input = ::encode_input(win.getch())
    1019
        }
    1019
        }
    1020
        message(b"Bye!\x00" as *const u8 as *const libc::c_char as *mut libc::c_char);
    1020
        message(b"Bye!\x00" as *const u8 as *const libc::c_char as *mut libc::c_char);
    1021
        win.refresh();
    1021
        win.refresh();
    1022
        finish(0i32);
    1022
        finish(0i32);
    1023
    }
    1023
    }

    Final steps

    As mentioned previously, we use win to obtain the current window object throughout the ncurses refactoring process, even though win is actually an Option<Window>, not a Window. Now that we are done with all the rewrites, we can update thote uses to access the Window properly:

    rewrite_expr 'win' 'win.as_ref().unwrap()' ;
    

    Diff #43

    src/robotfindskitten.rs
    90
            opts: *mut libc::c_void,
    90
            opts: *mut libc::c_void,
    91
        ) -> libc::c_int;
    91
        ) -> libc::c_int;
    92
        fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
    92
        fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
    93
    }
    93
    }
    94
    fn fmt_mvprintw(y: libc::c_int, x: libc::c_int, args: ::std::fmt::Arguments) -> libc::c_int {
    94
    fn fmt_mvprintw(y: libc::c_int, x: libc::c_int, args: ::std::fmt::Arguments) -> libc::c_int {
    95
        unsafe { win.mvprintw(y, x, &format!("{}", args)) }
    95
        unsafe { win.as_ref().unwrap().mvprintw(y, x, &format!("{}", args)) }
    96
    }
    96
    }
    97
    fn fmt_printw(args: ::std::fmt::Arguments) -> libc::c_int {
    97
    fn fmt_printw(args: ::std::fmt::Arguments) -> libc::c_int {
    98
        unsafe { win.printw(&format!("{}", args)) }
    98
        unsafe { win.as_ref().unwrap().printw(&format!("{}", args)) }
    99
    }
    99
    }
    100
    fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
    100
    fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
    101
        print!("{}", args);
    101
        print!("{}", args);
    102
        0
    102
        0
    103
    }
    103
    }

    621
    /*Initialization and setup functions*/
    621
    /*Initialization and setup functions*/
    622
    #[no_mangle]
    622
    #[no_mangle]
    623
    pub unsafe extern "C" fn initialize_ncurses() {
    623
    pub unsafe extern "C" fn initialize_ncurses() {
    624
        signal(2i32, Some(finish));
    624
        signal(2i32, Some(finish));
    625
        initscr();
    625
        initscr();
    626
        win.keypad(0 != 1i32);
    626
        win.as_ref().unwrap().keypad(0 != 1i32);
    627
        ::pancurses::nonl();
    627
        ::pancurses::nonl();
    628
        0;
    628
        0;
    629
        ::pancurses::noecho();
    629
        ::pancurses::noecho();
    630
        ::pancurses::cbreak();
    630
        ::pancurses::cbreak();
    631
        if ::pancurses::has_colors() {
    631
        if ::pancurses::has_colors() {

    718
        };
    718
        };
    719
        let mut i: libc::c_int = 0i32;
    719
        let mut i: libc::c_int = 0i32;
    720
        screen = unsafe {
    720
        screen = unsafe {
    721
            ::c2rust_runtime::CArray::alloc(
    721
            ::c2rust_runtime::CArray::alloc(
    722
                (::std::mem::size_of::<*mut libc::c_int>() as libc::c_ulong)
    722
                (::std::mem::size_of::<*mut libc::c_int>() as libc::c_ulong)
    723
                    .wrapping_mul((win.get_max_x() - 1i32 + 1i32) as libc::c_ulong)
    723
                    .wrapping_mul((win.as_ref().unwrap().get_max_x() - 1i32 + 1i32) as libc::c_ulong)
    724
                    as usize
    724
                    as usize
    725
                    / ::std::mem::size_of::<::c2rust_runtime::CArray<i32>>(),
    725
                    / ::std::mem::size_of::<::c2rust_runtime::CArray<i32>>(),
    726
            )
    726
            )
    727
        };
    727
        };
    728
        i = 0i32;
    728
        i = 0i32;
    729
        while i < win.get_max_x() - 1i32 + 1i32 {
    729
        while i < win.as_ref().unwrap().get_max_x() - 1i32 + 1i32 {
    730
            let ref mut fresh0 = screen[i as isize as usize];
    730
            let ref mut fresh0 = screen[i as isize as usize];
    731
            *fresh0 = unsafe {
    731
            *fresh0 = unsafe {
    732
                ::c2rust_runtime::CArray::alloc(
    732
                ::c2rust_runtime::CArray::alloc(
    733
                    (::std::mem::size_of::() as libc::c_ulong)
    733
                    (::std::mem::size_of::() as libc::c_ulong).wrapping_mul(
    734
                        .wrapping_mul((win.get_max_y() - 1i32 + 1i32) as libc::c_ulong)
    734
                        (win.as_ref().unwrap().get_max_y() - 1i32 + 1i32) as libc::c_ulong,
    735
                        as usize
    735
                    ) as usize
    736
                        / ::std::mem::size_of::(),
    736
                        / ::std::mem::size_of::(),
    737
                )
    737
                )
    738
            };
    738
            };
    739
            i += 1
    739
            i += 1
    740
        }
    740
        }

    742
        empty.y = -1i32;
    742
        empty.y = -1i32;
    743
        empty.color = 0i32;
    743
        empty.color = 0i32;
    744
        empty.bold = 0 != 0i32;
    744
        empty.bold = 0 != 0i32;
    745
        empty.character = ' ' as i32 as libc::c_char;
    745
        empty.character = ' ' as i32 as libc::c_char;
    746
        counter = 0i32;
    746
        counter = 0i32;
    747
        while counter <= win.get_max_x() - 1i32 {
    747
        while counter <= win.as_ref().unwrap().get_max_x() - 1i32 {
    748
            counter2 = 0i32;
    748
            counter2 = 0i32;
    749
            while counter2 <= win.get_max_y() - 1i32 {
    749
            while counter2 <= win.as_ref().unwrap().get_max_y() - 1i32 {
    750
                screen[counter as isize as usize][counter2 as isize as usize] = -1i32;
    750
                screen[counter as isize as usize][counter2 as isize as usize] = -1i32;
    751
                counter2 += 1
    751
                counter2 += 1
    752
            }
    752
            }
    753
            counter += 1
    753
            counter += 1
    754
        }
    754
        }

    782
    #[no_mangle]
    782
    #[no_mangle]
    783
    pub static mut screen: ::c2rust_runtime::CArray<::c2rust_runtime::CArray> =
    783
    pub static mut screen: ::c2rust_runtime::CArray<::c2rust_runtime::CArray> =
    784
        unsafe { ::c2rust_runtime::CArray::empty() };
    784
        unsafe { ::c2rust_runtime::CArray::empty() };
    785
    #[no_mangle]
    785
    #[no_mangle]
    786
    pub unsafe extern "C" fn initialize_robot() {
    786
    pub unsafe extern "C" fn initialize_robot() {
    787
        robot.x = rand() % (win.get_max_x() - 1i32) + 1i32;
    787
        robot.x = rand() % (win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
    788
        robot.y = rand() % (win.get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
    788
        robot.y = rand() % (win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
    789
        robot.character = '#' as i32 as libc::c_char;
    789
        robot.character = '#' as i32 as libc::c_char;
    790
        robot.color = 0i32;
    790
        robot.color = 0i32;
    791
        robot.bold = 0 != 0i32;
    791
        robot.bold = 0 != 0i32;
    792
        screen[robot.x as isize as usize][robot.y as isize as usize] = 0i32;
    792
        screen[robot.x as isize as usize][robot.y as isize as usize] = 0i32;
    793
    }
    793
    }

    801
        character: 0,
    801
        character: 0,
    802
    };
    802
    };
    803
    #[no_mangle]
    803
    #[no_mangle]
    804
    pub unsafe extern "C" fn initialize_kitten() {
    804
    pub unsafe extern "C" fn initialize_kitten() {
    805
        loop {
    805
        loop {
    806
            kitten.x = rand() % (win.get_max_x() - 1i32) + 1i32;
    806
            kitten.x = rand() % (win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
    807
            kitten.y = rand() % (win.get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
    807
            kitten.y = rand() % (win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
    808
            if !(screen[kitten.x as isize as usize][kitten.y as isize as usize] != -1i32) {
    808
            if !(screen[kitten.x as isize as usize][kitten.y as isize as usize] != -1i32) {
    809
                break;
    809
                break;
    810
            }
    810
            }
    811
        }
    811
        }
    812
        loop {
    812
        loop {

    850
                if !(0 == validchar(bogus[counter as usize].character)) {
    850
                if !(0 == validchar(bogus[counter as usize].character)) {
    851
                    break;
    851
                    break;
    852
                }
    852
                }
    853
            }
    853
            }
    854
            loop {
    854
            loop {
    855
                bogus[counter as usize].x = rand() % (win.get_max_x() - 1i32) + 1i32;
    855
                bogus[counter as usize].x = rand() % (win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
    856
                bogus[counter as usize].y = rand() % (win.get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
    856
                bogus[counter as usize].y =
    857
                    rand() % (win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
    857
                if !(screen[bogus[counter as usize].x as isize as usize]
    858
                if !(screen[bogus[counter as usize].x as isize as usize]
    858
                    [bogus[counter as usize].y as isize as usize]
    859
                    [bogus[counter as usize].y as isize as usize]
    859
                    != -1i32)
    860
                    != -1i32)
    860
                {
    861
                {
    861
                    break;
    862
                    break;

    890
                    .to_str()
    891
                    .to_str()
    891
                    .unwrap()
    892
                    .unwrap()
    892
            }),
    893
            }),
    893
        );
    894
        );
    894
        counter = 0i32;
    895
        counter = 0i32;
    895
        while counter <= win.get_max_x() - 1i32 {
    896
        while counter <= win.as_ref().unwrap().get_max_x() - 1i32 {
    896
            fmt_printw(format_args!("{:}", 95i32 as u8 as char));
    897
            fmt_printw(format_args!("{:}", 95i32 as u8 as char));
    897
            counter += 1
    898
            counter += 1
    898
        }
    899
        }
    899
        counter = 0i32;
    900
        counter = 0i32;
    900
        while counter < num_bogus {
    901
        while counter < num_bogus {
    901
            draw(bogus[counter as usize]);
    902
            draw(bogus[counter as usize]);
    902
            counter += 1
    903
            counter += 1
    903
        }
    904
        }
    904
        draw(kitten);
    905
        draw(kitten);
    905
        draw(robot);
    906
        draw(robot);
    906
        win.refresh();
    907
        win.as_ref().unwrap().refresh();
    907
    }
    908
    }
    908
    #[no_mangle]
    909
    #[no_mangle]
    909
    pub unsafe extern "C" fn draw(mut o: screen_object) {
    910
    pub unsafe extern "C" fn draw(mut o: screen_object) {
    910
        full_draw(o, 0 != 0i32);
    911
        full_draw(o, 0 != 0i32);
    911
    }
    912
    }

    934
            new |= 1u64 << 14i32 + 8i32
    935
            new |= 1u64 << 14i32 + 8i32
    935
        }
    936
        }
    936
        if o.bold {
    937
        if o.bold {
    937
            new |= 1u64 << 13i32 + 8i32
    938
            new |= 1u64 << 13i32 + 8i32
    938
        }
    939
        }
    940
        win.as_ref()
    941
            .unwrap()
    939
        win.attrset(new as libc::c_int as ::pancurses::chtype);
    942
            .attrset(new as libc::c_int as ::pancurses::chtype);
    940
        if in_place {
    943
        if in_place {
    941
            fmt_printw(format_args!(
    944
            fmt_printw(format_args!(
    942
                "{:}",
    945
                "{:}",
    943
                o.character as libc::c_int as u8 as char
    946
                o.character as libc::c_int as u8 as char
    944
            ));
    947
            ));

    946
            fmt_mvprintw(
    949
            fmt_mvprintw(
    947
                o.y,
    950
                o.y,
    948
                o.x,
    951
                o.x,
    949
                format_args!("{:}", o.character as libc::c_int as u8 as char),
    952
                format_args!("{:}", o.character as libc::c_int as u8 as char),
    950
            );
    953
            );
    951
            win.mv(o.y, o.x);
    954
            win.as_ref().unwrap().mv(o.y, o.x);
    952
        }
    955
        }
    956
        win.as_ref()
    957
            .unwrap()
    953
        win.attrset(old as libc::c_int as ::pancurses::chtype);
    958
            .attrset(old as libc::c_int as ::pancurses::chtype);
    954
    }
    959
    }
    955
    #[no_mangle]
    960
    #[no_mangle]
    956
    pub unsafe extern "C" fn instructions() {
    961
    pub unsafe extern "C" fn instructions() {
    957
        let mut dummy: libc::c_char = 0;
    962
        let mut dummy: libc::c_char = 0;
    958
        fmt_mvprintw(
    963
        fmt_mvprintw(

    984
        ));
    989
        ));
    985
        fmt_printw(format_args!(
    990
        fmt_printw(format_args!(
    986
            "the Esc key. See the documentation for more information.\n\n"
    991
            "the Esc key. See the documentation for more information.\n\n"
    987
        ));
    992
        ));
    988
        fmt_printw(format_args!("Press any key to start.\n"));
    993
        fmt_printw(format_args!("Press any key to start.\n"));
    989
        win.refresh();
    994
        win.as_ref().unwrap().refresh();
    990
        dummy = ::encode_input(win.getch()) as libc::c_char;
    995
        dummy = ::encode_input(win.as_ref().unwrap().getch()) as libc::c_char;
    991
        win.clear();
    996
        win.as_ref().unwrap().clear();
    992
    }
    997
    }
    993
    #[no_mangle]
    998
    #[no_mangle]
    994
    pub unsafe extern "C" fn draw_in_place(mut o: screen_object) {
    999
    pub unsafe extern "C" fn draw_in_place(mut o: screen_object) {
    995
        full_draw(o, 0 != 1i32);
    1000
        full_draw(o, 0 != 1i32);
    996
    }
    1001
    }

    998
    #[no_mangle]
    1003
    #[no_mangle]
    999
    pub unsafe extern "C" fn play_game() {
    1004
    pub unsafe extern "C" fn play_game() {
    1000
        let mut old_x: libc::c_int = robot.x;
    1005
        let mut old_x: libc::c_int = robot.x;
    1001
        let mut old_y: libc::c_int = robot.y;
    1006
        let mut old_y: libc::c_int = robot.y;
    1002
        let mut input: libc::c_int = 0;
    1007
        let mut input: libc::c_int = 0;
    1003
        input = ::encode_input(win.getch());
    1008
        input = ::encode_input(win.as_ref().unwrap().getch());
    1004
        while input != 27i32 && input != 'q' as i32 && input != 'Q' as i32 {
    1009
        while input != 27i32 && input != 'q' as i32 && input != 'Q' as i32 {
    1005
            process_input(input);
    1010
            process_input(input);
    1006
            if !(old_x == robot.x && old_y == robot.y) {
    1011
            if !(old_x == robot.x && old_y == robot.y) {
    1007
                if win.mv(old_y, old_x) == -1i32 {
    1012
                if win.as_ref().unwrap().mv(old_y, old_x) == -1i32 {
    1008
                } else {
    1013
                } else {
    1009
                    win.addch(' ' as i32 as chtype);
    1014
                    win.as_ref().unwrap().addch(' ' as i32 as chtype);
    1010
                };
    1015
                };
    1011
                screen[old_x as isize as usize][old_y as isize as usize] = -1i32;
    1016
                screen[old_x as isize as usize][old_y as isize as usize] = -1i32;
    1012
                draw(robot);
    1017
                draw(robot);
    1013
                win.refresh();
    1018
                win.as_ref().unwrap().refresh();
    1014
                screen[robot.x as isize as usize][robot.y as isize as usize] = 0i32;
    1019
                screen[robot.x as isize as usize][robot.y as isize as usize] = 0i32;
    1015
                old_x = robot.x;
    1020
                old_x = robot.x;
    1016
                old_y = robot.y
    1021
                old_y = robot.y
    1017
            }
    1022
            }
    1018
            input = ::encode_input(win.getch())
    1023
            input = ::encode_input(win.as_ref().unwrap().getch())
    1019
        }
    1024
        }
    1020
        message(b"Bye!\x00" as *const u8 as *const libc::c_char as *mut libc::c_char);
    1025
        message(b"Bye!\x00" as *const u8 as *const libc::c_char as *mut libc::c_char);
    1021
        win.refresh();
    1026
        win.as_ref().unwrap().refresh();
    1022
        finish(0i32);
    1027
        finish(0i32);
    1023
    }
    1028
    }
    1024
    #[no_mangle]
    1029
    #[no_mangle]
    1025
    pub unsafe extern "C" fn message(mut message_0: *mut libc::c_char) {
    1030
    pub unsafe extern "C" fn message(mut message_0: *mut libc::c_char) {
    1026
        win.mv(1i32, 0i32);
    1031
        win.as_ref().unwrap().mv(1i32, 0i32);
    1027
        win.clrtoeol();
    1032
        win.as_ref().unwrap().clrtoeol();
    1028
        fmt_mvprintw(
    1033
        fmt_mvprintw(
    1029
            1i32,
    1034
            1i32,
    1030
            0i32,
    1035
            0i32,
    1031
            format_args!("{:.*}", win.get_max_x() as usize, unsafe {
    1036
            format_args!(
    1037
                "{:.*}",
    1038
                win.as_ref().unwrap().get_max_x() as usize,
    1039
                unsafe {
    1032
                ::std::ffi::CStr::from_ptr(message_0 as *const libc::c_char)
    1040
                    ::std::ffi::CStr::from_ptr(message_0 as *const libc::c_char)
    1033
                    .to_str()
    1041
                        .to_str()
    1034
                    .unwrap()
    1042
                        .unwrap()
    1043
                }
    1035
            }),
    1044
            ),
    1036
        );
    1045
        );
    1037
        win.mv(robot.y, robot.x);
    1046
        win.as_ref().unwrap().mv(robot.y, robot.x);
    1038
        win.refresh();
    1047
        win.as_ref().unwrap().refresh();
    1039
    }
    1048
    }
    1040
    #[no_mangle]
    1049
    #[no_mangle]
    1041
    pub unsafe extern "C" fn process_input(mut input: libc::c_int) {
    1050
    pub unsafe extern "C" fn process_input(mut input: libc::c_int) {
    1042
        let mut check_x: libc::c_int = robot.x;
    1051
        let mut check_x: libc::c_int = robot.x;
    1043
        let mut check_y: libc::c_int = robot.y;
    1052
        let mut check_y: libc::c_int = robot.y;
    1044
        match input {
    1053
        match input {
    1045
            12 => {
    1054
            12 => {
    1046
                win.refresh();
    1055
                win.as_ref().unwrap().refresh();
    1047
            }
    1056
            }
    1048
            259 | 107 | 75 | 16 => check_y -= 1,
    1057
            259 | 107 | 75 | 16 => check_y -= 1,
    1049
            262 | 121 | 89 => {
    1058
            262 | 121 | 89 => {
    1050
                check_x -= 1;
    1059
                check_x -= 1;
    1051
                check_y -= 1
    1060
                check_y -= 1

    1073
                );
    1082
                );
    1074
                return;
    1083
                return;
    1075
            }
    1084
            }
    1076
        }
    1085
        }
    1077
        if check_y < 3i32
    1086
        if check_y < 3i32
    1078
            || check_y > win.get_max_y() - 1i32
    1087
            || check_y > win.as_ref().unwrap().get_max_y() - 1i32
    1079
            || check_x < 0i32
    1088
            || check_x < 0i32
    1080
            || check_x > win.get_max_x() - 1i32
    1089
            || check_x > win.as_ref().unwrap().get_max_x() - 1i32
    1081
        {
    1090
        {
    1082
            return;
    1091
            return;
    1083
        }
    1092
        }
    1084
        if screen[check_x as isize as usize][check_y as isize as usize] != -1i32 {
    1093
        if screen[check_x as isize as usize][check_y as isize as usize] != -1i32 {
    1085
            match screen[check_x as isize as usize][check_y as isize as usize] {
    1094
            match screen[check_x as isize as usize][check_y as isize as usize] {
    1086
                0 => {}
    1095
                0 => {}
    1087
                1 => {
    1096
                1 => {
    1088
                    /*We didn't move, or we're stuck in a
    1097
                    /*We didn't move, or we're stuck in a
    1089
                    time warp or something.*/
    1098
                    time warp or something.*/
    1090
                    win.mv(1i32, 0i32);
    1099
                    win.as_ref().unwrap().mv(1i32, 0i32);
    1091
                    win.clrtoeol();
    1100
                    win.as_ref().unwrap().clrtoeol();
    1092
                    play_animation(input);
    1101
                    play_animation(input);
    1093
                }
    1102
                }
    1094
                _ => {
    1103
                _ => {
    1095
                    message(
    1104
                    message(
    1096
                        messages[bogus_messages[(screen[check_x as isize as usize]
    1105
                        messages[bogus_messages[(screen[check_x as isize as usize]

    1108
    #[no_mangle]
    1117
    #[no_mangle]
    1109
    pub unsafe extern "C" fn play_animation(mut input: libc::c_int) {
    1118
    pub unsafe extern "C" fn play_animation(mut input: libc::c_int) {
    1110
        let mut counter: libc::c_int = 0;
    1119
        let mut counter: libc::c_int = 0;
    1111
        counter = 4i32;
    1120
        counter = 4i32;
    1112
        while counter > 0i32 {
    1121
        while counter > 0i32 {
    1113
            if win.mv(1i32, 50i32 + counter + 1i32) == -1i32 {
    1122
            if win.as_ref().unwrap().mv(1i32, 50i32 + counter + 1i32) == -1i32 {
    1114
            } else {
    1123
            } else {
    1115
                win.addch(' ' as i32 as chtype);
    1124
                win.as_ref().unwrap().addch(' ' as i32 as chtype);
    1116
            };
    1125
            };
    1117
            win.mv(1i32, 50i32 + counter);
    1126
            win.as_ref().unwrap().mv(1i32, 50i32 + counter);
    1118
            if input == 0o405i32 || input == 0o402i32 || input == 0o540i32 || input == 0o535i32 {
    1127
            if input == 0o405i32 || input == 0o402i32 || input == 0o540i32 || input == 0o535i32 {
    1119
                draw_in_place(kitten);
    1128
                draw_in_place(kitten);
    1120
            } else {
    1129
            } else {
    1121
                draw_in_place(robot);
    1130
                draw_in_place(robot);
    1122
            }
    1131
            }
    1123
            if win.mv(1i32, 50i32 - counter) == -1i32 {
    1132
            if win.as_ref().unwrap().mv(1i32, 50i32 - counter) == -1i32 {
    1124
            } else {
    1133
            } else {
    1125
                win.addch(' ' as i32 as chtype);
    1134
                win.as_ref().unwrap().addch(' ' as i32 as chtype);
    1126
            };
    1135
            };
    1127
            win.mv(1i32, 50i32 - counter + 1i32);
    1136
            win.as_ref().unwrap().mv(1i32, 50i32 - counter + 1i32);
    1128
            if input == 0o405i32 || input == 0o402i32 || input == 0o540i32 || input == 0o535i32 {
    1137
            if input == 0o405i32 || input == 0o402i32 || input == 0o540i32 || input == 0o535i32 {
    1129
                draw_in_place(robot);
    1138
                draw_in_place(robot);
    1130
            } else {
    1139
            } else {
    1131
                draw_in_place(kitten);
    1140
                draw_in_place(kitten);
    1132
            }
    1141
            }
    1133
            win.refresh();
    1142
            win.as_ref().unwrap().refresh();
    1134
            sleep(1i32 as libc::c_uint);
    1143
            sleep(1i32 as libc::c_uint);
    1135
            counter -= 1
    1144
            counter -= 1
    1136
        }
    1145
        }
    1137
        win.mv(1i32, 0i32);
    1146
        win.as_ref().unwrap().mv(1i32, 0i32);
    1138
        win.addnstr(
    1147
        win.as_ref().unwrap().addnstr(
    1139
            ::std::str::from_utf8(b"You found kitten! Way to go, robot!\x00")
    1148
            ::std::str::from_utf8(b"You found kitten! Way to go, robot!\x00")
    1140
                .unwrap()
    1149
                .unwrap()
    1141
                .trim_end_matches('\u{0}'),
    1150
                .trim_end_matches('\u{0}'),
    1142
            -1i32 as usize,
    1151
            -1i32 as usize,
    1143
        );
    1152
        );
    1144
        win.refresh();
    1153
        win.as_ref().unwrap().refresh();
    1145
        finish(0i32);
    1154
        finish(0i32);
    1146
    }
    1155
    }
    1147
    unsafe fn main_0(mut argc: libc::c_int, mut argv: *mut *mut libc::c_char) -> libc::c_int {
    1156
    unsafe fn main_0(mut argc: libc::c_int, mut argv: *mut *mut libc::c_char) -> libc::c_int {
    1148
        if argc == 1i32 {
    1157
        if argc == 1i32 {
    1149
            num_bogus = 20i32
    1158
            num_bogus = 20i32

    The final step is to initialize win. This corresponds to the call to the ncurses initscr initialization function:

    rewrite_expr 'initscr()' 'win = Some(::pancurses::initscr())' ;
    

    Diff #44

    src/robotfindskitten.rs
    620
     */
    620
     */
    621
    /*Initialization and setup functions*/
    621
    /*Initialization and setup functions*/
    622
    #[no_mangle]
    622
    #[no_mangle]
    623
    pub unsafe extern "C" fn initialize_ncurses() {
    623
    pub unsafe extern "C" fn initialize_ncurses() {
    624
        signal(2i32, Some(finish));
    624
        signal(2i32, Some(finish));
    625
        initscr();
    625
        win = Some(::pancurses::initscr());
    626
        win.as_ref().unwrap().keypad(0 != 1i32);
    626
        win.as_ref().unwrap().keypad(0 != 1i32);
    627
        ::pancurses::nonl();
    627
        ::pancurses::nonl();
    628
        0;
    628
        0;
    629
        ::pancurses::noecho();
    629
        ::pancurses::noecho();
    630
        ::pancurses::cbreak();
    630
        ::pancurses::cbreak();

    We save this for last only so that the win to win.as_ref().unwrap() rewrite doesn't produce an erroneous assignment win.as_ref().unwrap() = ....

    At this point, we are done with the current refactoring step: robotfindskitten has been fully adapted to use the safe pancurses API in place of raw ncurses FFI calls.

    commit
    

    Diff #45

    src/robotfindskitten.rs
    619
     *Function definitions
    619
     *Function definitions
    620
     */
    620
     */
    621
    /*Initialization and setup functions*/
    621
    /*Initialization and setup functions*/
    622
    #[no_mangle]
    622
    #[no_mangle]
    623
    pub unsafe extern "C" fn initialize_ncurses() {
    623
    pub unsafe extern "C" fn initialize_ncurses() {
    624
        signal(2i32, Some(finish));
    624
        signal(2i32, Some(finish));
    625
        win = Some(::pancurses::initscr());
    625
        win = Some(::pancurses::initscr());
    626
        win.as_ref().unwrap().keypad(0 != 1i32);
    626
        win.as_ref().unwrap().keypad(0 != 1i32);
    627
        ::pancurses::nonl();
    627
        ::pancurses::nonl();

    672
           );
    672
            );
    673
        };
    673
        };
    674
    }
    674
    }
    675
    fn encode_input(inp: Option<::pancurses::Input>) -> libc::c_int {
    675
    fn encode_input(inp: Option<::pancurses::Input>) -> libc::c_int {
    676
        use pancurses::Input::*;
    676
        use pancurses::Input::*;
    677
        let inp = match inp {
    677
        let inp = match inp {
    678
            Some(x) => x,
    678
            Some(x) => x,
    679
            None => return -1,
    679
            None => return -1,
    680
        };
    680
        };

    Moving global state to the stack

    robotfindskitten uses global variables - static muts in Rust - to store the game state. Accessing these globals is unsafe, due to the difficulty of preventing simultaneous borrowing and mutation. In this refactoring step, we move the global state onto the stack and pass it by reference to every function that needs it, which allows the borrow checker to analyze its usage and ensure safety.

    Most of the work in this step is handled by the static_to_local_ref refactoring command. This command identifies all functions that use a given static, and modifies those functions to access the global through a reference (passed as an argument to the function) instead of accessing it directly. (See the static_to_local_ref command documentation for examples.)

    However, running static_to_local_ref separately on each of robotfindskitten's seven global variables would add up to seven new arguments to many of robotfindskitten's functions, making their signatures difficult to read. Instead, we proceed in two steps. First, we gather up all the global variables into a single global struct. Then, we run static_to_local_ref on just the struct, achieving safety while adding only a single new argument to each affected function.

    We collect the statics into a struct using static_collect_to_struct:

    select target 'crate; child(static && mut);' ;
    static_collect_to_struct State S
    

    Diff #46

    src/robotfindskitten.rs
    5
        non_snake_case,
    5
        non_snake_case,
    6
        non_upper_case_globals,
    6
        non_upper_case_globals,
    7
        unused_mut
    7
        unused_mut
    8
    )]
    8
    )]
    9
    #![feature(const_raw_ptr_to_usize_cast, extern_types, libc)]
    9
    #![feature(const_raw_ptr_to_usize_cast, extern_types, libc)]
    10
    struct State {
    10
    static mut win: Option<::pancurses::Window> = None;
    11
        win: Option<::pancurses::Window>,
    12
        bogus: [screen_object; 406],
    13
        bogus_messages: [libc::c_int; 406],
    14
        used_messages: [libc::c_int; 406],
    15
        screen: ::c2rust_runtime::CArray<::c2rust_runtime::CArray>,
    16
        robot: screen_object,
    17
        kitten: screen_object,
    18
        num_bogus: libc::c_int,
    19
    }
    20
    static mut S: State = State {
    21
        win: None,
    22
        bogus: [screen_object {
    23
            x: 0,
    24
            y: 0,
    25
            color: 0,
    26
            bold: false,
    27
            character: 0,
    28
        }; 406],
    29
        bogus_messages: [0; 406],
    30
        used_messages: [0; 406],
    31
        screen: unsafe { ::c2rust_runtime::CArray::empty() },
    32
        robot: screen_object {
    33
            x: 0,
    34
            y: 0,
    35
            color: 0,
    36
            bold: false,
    37
            character: 0,
    38
        },
    39
        kitten: screen_object {
    40
            x: 0,
    41
            y: 0,
    42
            color: 0,
    43
            bold: false,
    44
            character: 0,
    45
        },
    46
        num_bogus: 0,
    47
    };
    11
    extern crate c2rust_runtime;
    48
    extern crate c2rust_runtime;
    12
    extern crate libc;
    49
    extern crate libc;
    13
    extern crate pancurses;
    50
    extern crate pancurses;
    14
    extern "C" {
    51
    extern "C" {
    15
        pub type ldat;
    52
        pub type ldat;

    90
            opts: *mut libc::c_void,
    127
            opts: *mut libc::c_void,
    91
        ) -> libc::c_int;
    128
        ) -> libc::c_int;
    92
        fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
    129
        fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
    93
    }
    130
    }
    94
    fn fmt_mvprintw(y: libc::c_int, x: libc::c_int, args: ::std::fmt::Arguments) -> libc::c_int {
    131
    fn fmt_mvprintw(y: libc::c_int, x: libc::c_int, args: ::std::fmt::Arguments) -> libc::c_int {
    95
        unsafe { win.as_ref().unwrap().mvprintw(y, x, &format!("{}", args)) }
    132
        unsafe { S.win.as_ref().unwrap().mvprintw(y, x, &format!("{}", args)) }
    96
    }
    133
    }
    97
    fn fmt_printw(args: ::std::fmt::Arguments) -> libc::c_int {
    134
    fn fmt_printw(args: ::std::fmt::Arguments) -> libc::c_int {
    98
        unsafe { win.as_ref().unwrap().printw(&format!("{}", args)) }
    135
        unsafe { S.win.as_ref().unwrap().printw(&format!("{}", args)) }
    99
    }
    136
    }
    100
    fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
    137
    fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
    101
        print!("{}", args);
    138
        print!("{}", args);
    102
        0
    139
        0
    103
    }
    140
    }

    620
     */
    657
     */
    621
    /*Initialization and setup functions*/
    658
    /*Initialization and setup functions*/
    622
    #[no_mangle]
    659
    #[no_mangle]
    623
    pub unsafe extern "C" fn initialize_ncurses() {
    660
    pub unsafe extern "C" fn initialize_ncurses() {
    624
        signal(2i32, Some(finish));
    661
        signal(2i32, Some(finish));
    625
        win = Some(::pancurses::initscr());
    662
        S.win = Some(::pancurses::initscr());
    626
        win.as_ref().unwrap().keypad(0 != 1i32);
    663
        S.win.as_ref().unwrap().keypad(0 != 1i32);
    627
        ::pancurses::nonl();
    664
        ::pancurses::nonl();
    628
        0;
    665
        0;
    629
        ::pancurses::noecho();
    666
        ::pancurses::noecho();
    630
        ::pancurses::cbreak();
    667
        ::pancurses::cbreak();
    631
        if ::pancurses::has_colors() {
    668
        if ::pancurses::has_colors() {

    715
            color: 0,
    752
            color: 0,
    716
            bold: false,
    753
            bold: false,
    717
            character: 0,
    754
            character: 0,
    718
        };
    755
        };
    719
        let mut i: libc::c_int = 0i32;
    756
        let mut i: libc::c_int = 0i32;
    720
        screen = unsafe {
    757
        S.screen = unsafe {
    721
            ::c2rust_runtime::CArray::alloc(
    758
            ::c2rust_runtime::CArray::alloc(
    722
                (::std::mem::size_of::<*mut libc::c_int>() as libc::c_ulong)
    759
                (::std::mem::size_of::<*mut libc::c_int>() as libc::c_ulong)
    723
                    .wrapping_mul((win.as_ref().unwrap().get_max_x() - 1i32 + 1i32) as libc::c_ulong)
    760
                    .wrapping_mul((S.win.as_ref().unwrap().get_max_x() - 1i32 + 1i32) as libc::c_ulong)
    724
                    as usize
    761
                    as usize
    725
                    / ::std::mem::size_of::<::c2rust_runtime::CArray<i32>>(),
    762
                    / ::std::mem::size_of::<::c2rust_runtime::CArray<i32>>(),
    726
            )
    763
            )
    727
        };
    764
        };
    728
        i = 0i32;
    765
        i = 0i32;
    729
        while i < win.as_ref().unwrap().get_max_x() - 1i32 + 1i32 {
    766
        while i < S.win.as_ref().unwrap().get_max_x() - 1i32 + 1i32 {
    730
            let ref mut fresh0 = screen[i as isize as usize];
    767
            let ref mut fresh0 = S.screen[i as isize as usize];
    731
            *fresh0 = unsafe {
    768
            *fresh0 = unsafe {
    732
                ::c2rust_runtime::CArray::alloc(
    769
                ::c2rust_runtime::CArray::alloc(
    733
                    (::std::mem::size_of::() as libc::c_ulong).wrapping_mul(
    770
                    (::std::mem::size_of::() as libc::c_ulong).wrapping_mul(
    734
                        (win.as_ref().unwrap().get_max_y() - 1i32 + 1i32) as libc::c_ulong,
    771
                        (S.win.as_ref().unwrap().get_max_y() - 1i32 + 1i32) as libc::c_ulong,
    735
                    ) as usize
    772
                    ) as usize
    736
                        / ::std::mem::size_of::(),
    773
                        / ::std::mem::size_of::(),
    737
                )
    774
                )
    738
            };
    775
            };
    739
            i += 1
    776
            i += 1

    742
        empty.y = -1i32;
    779
        empty.y = -1i32;
    743
        empty.color = 0i32;
    780
        empty.color = 0i32;
    744
        empty.bold = 0 != 0i32;
    781
        empty.bold = 0 != 0i32;
    745
        empty.character = ' ' as i32 as libc::c_char;
    782
        empty.character = ' ' as i32 as libc::c_char;
    746
        counter = 0i32;
    783
        counter = 0i32;
    747
        while counter <= win.as_ref().unwrap().get_max_x() - 1i32 {
    784
        while counter <= S.win.as_ref().unwrap().get_max_x() - 1i32 {
    748
            counter2 = 0i32;
    785
            counter2 = 0i32;
    749
            while counter2 <= win.as_ref().unwrap().get_max_y() - 1i32 {
    786
            while counter2 <= S.win.as_ref().unwrap().get_max_y() - 1i32 {
    750
                screen[counter as isize as usize][counter2 as isize as usize] = -1i32;
    787
                S.screen[counter as isize as usize][counter2 as isize as usize] = -1i32;
    751
                counter2 += 1
    788
                counter2 += 1
    752
            }
    789
            }
    753
            counter += 1
    790
            counter += 1
    754
        }
    791
        }
    755
        counter = 0i32;
    792
        counter = 0i32;
    756
        while (counter as libc::c_ulong)
    793
        while (counter as libc::c_ulong)
    757
            < (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    794
            < (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    758
                .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
    795
                .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
    759
        {
    796
        {
    760
            used_messages[counter as usize] = 0i32;
    797
            S.used_messages[counter as usize] = 0i32;
    761
            bogus_messages[counter as usize] = 0i32;
    798
            S.bogus_messages[counter as usize] = 0i32;
    762
            bogus[counter as usize] = empty;
    799
            S.bogus[counter as usize] = empty;
    763
            counter += 1
    800
            counter += 1
    764
        }
    801
        }
    765
    }
    802
    }
    766
    #[no_mangle]
    803
    767
    pub static mut bogus: [screen_object; 406] = [screen_object {
    768
        x: 0,
    769
        y: 0,
    770
        color: 0,
    771
        bold: false,
    772
        character: 0,
    773
    }; 406];
    774
    #[no_mangle]
    775
    pub static mut bogus_messages: [libc::c_int; 406] = [0; 406];
    776
    #[no_mangle]
    777
    pub static mut used_messages: [libc::c_int; 406] = [0; 406];
    778
    /* This array contains our internal representation of the screen. The
    804
    /* This array contains our internal representation of the screen. The
    779
    array is bigger than it needs to be, as we don't need to keep track
    805
    array is bigger than it needs to be, as we don't need to keep track
    780
    of the first few rows of the screen. But that requires making an
    806
    of the first few rows of the screen. But that requires making an
    781
    offset function and using that everywhere. So not right now. */
    807
    offset function and using that everywhere. So not right now. */
    782
    #[no_mangle]
    808
    783
    pub static mut screen: ::c2rust_runtime::CArray<::c2rust_runtime::CArray> =
    784
        unsafe { ::c2rust_runtime::CArray::empty() };
    785
    #[no_mangle]
    809
    #[no_mangle]
    786
    pub unsafe extern "C" fn initialize_robot() {
    810
    pub unsafe extern "C" fn initialize_robot() {
    787
        robot.x = rand() % (win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
    811
        S.robot.x = rand() % (S.win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
    788
        robot.y = rand() % (win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
    812
        S.robot.y = rand() % (S.win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
    789
        robot.character = '#' as i32 as libc::c_char;
    813
        S.robot.character = '#' as i32 as libc::c_char;
    790
        robot.color = 0i32;
    814
        S.robot.color = 0i32;
    791
        robot.bold = 0 != 0i32;
    815
        S.robot.bold = 0 != 0i32;
    792
        screen[robot.x as isize as usize][robot.y as isize as usize] = 0i32;
    816
        S.screen[S.robot.x as isize as usize][S.robot.y as isize as usize] = 0i32;
    793
    }
    817
    }
    794
    /*Global variables. Bite me, it's fun.*/
    818
    /*Global variables. Bite me, it's fun.*/
    795
    #[no_mangle]
    819
    796
    pub static mut robot: screen_object = screen_object {
    797
        x: 0,
    798
        y: 0,
    799
        color: 0,
    800
        bold: false,
    801
        character: 0,
    802
    };
    803
    #[no_mangle]
    820
    #[no_mangle]
    804
    pub unsafe extern "C" fn initialize_kitten() {
    821
    pub unsafe extern "C" fn initialize_kitten() {
    805
        loop {
    822
        loop {
    806
            kitten.x = rand() % (win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
    823
            S.kitten.x = rand() % (S.win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
    807
            kitten.y = rand() % (win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
    824
            S.kitten.y = rand() % (S.win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
    808
            if !(screen[kitten.x as isize as usize][kitten.y as isize as usize] != -1i32) {
    825
            if !(S.screen[S.kitten.x as isize as usize][S.kitten.y as isize as usize] != -1i32) {
    809
                break;
    826
                break;
    810
            }
    827
            }
    811
        }
    828
        }
    812
        loop {
    829
        loop {
    813
            kitten.character = (rand() % (126i32 - '!' as i32 + 1i32) + '!' as i32) as libc::c_char;
    830
            S.kitten.character = (rand() % (126i32 - '!' as i32 + 1i32) + '!' as i32) as libc::c_char;
    814
            if !(0 == validchar(kitten.character)) {
    831
            if !(0 == validchar(S.kitten.character)) {
    815
                break;
    832
                break;
    816
            }
    833
            }
    817
        }
    834
        }
    818
        screen[kitten.x as isize as usize][kitten.y as isize as usize] = 1i32;
    835
        S.screen[S.kitten.x as isize as usize][S.kitten.y as isize as usize] = 1i32;
    819
        kitten.color = rand() % 6i32 + 1i32;
    836
        S.kitten.color = rand() % 6i32 + 1i32;
    820
        kitten.bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
    837
        S.kitten.bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
    821
    }
    822
    #[no_mangle]
    823
    pub static mut kitten: screen_object = screen_object {
    824
        x: 0,
    825
        y: 0,
    826
        color: 0,
    827
        bold: false,
    828
        character: 0,
    829
    };
    838
    }
    839
    830
    /*Helper functions*/
    840
    /*Helper functions*/
    831
    #[no_mangle]
    841
    #[no_mangle]
    832
    pub unsafe extern "C" fn validchar(mut a: libc::c_char) -> libc::c_int {
    842
    pub unsafe extern "C" fn validchar(mut a: libc::c_char) -> libc::c_int {
    833
        match a as libc::c_int {
    843
        match a as libc::c_int {
    834
            35 | 32 | 127 => return 0i32,
    844
            35 | 32 | 127 => return 0i32,

    839
    #[no_mangle]
    849
    #[no_mangle]
    840
    pub unsafe extern "C" fn initialize_bogus() {
    850
    pub unsafe extern "C" fn initialize_bogus() {
    841
        let mut counter: libc::c_int = 0;
    851
        let mut counter: libc::c_int = 0;
    842
        let mut index: libc::c_int = 0;
    852
        let mut index: libc::c_int = 0;
    843
        counter = 0i32;
    853
        counter = 0i32;
    844
        while counter < num_bogus {
    854
        while counter < S.num_bogus {
    845
            bogus[counter as usize].color = rand() % 6i32 + 1i32;
    855
            S.bogus[counter as usize].color = rand() % 6i32 + 1i32;
    846
            bogus[counter as usize].bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
    856
            S.bogus[counter as usize].bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
    847
            loop {
    857
            loop {
    848
                bogus[counter as usize].character =
    858
                S.bogus[counter as usize].character =
    849
                    (rand() % (126i32 - '!' as i32 + 1i32) + '!' as i32) as libc::c_char;
    859
                    (rand() % (126i32 - '!' as i32 + 1i32) + '!' as i32) as libc::c_char;
    850
                if !(0 == validchar(bogus[counter as usize].character)) {
    860
                if !(0 == validchar(S.bogus[counter as usize].character)) {
    851
                    break;
    861
                    break;
    852
                }
    862
                }
    853
            }
    863
            }
    854
            loop {
    864
            loop {
    865
                S.bogus[counter as usize].x =
    855
                bogus[counter as usize].x = rand() % (win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
    866
                    rand() % (S.win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
    856
                bogus[counter as usize].y =
    867
                S.bogus[counter as usize].y =
    857
                    rand() % (win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
    868
                    rand() % (S.win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
    858
                if !(screen[bogus[counter as usize].x as isize as usize]
    869
                if !(S.screen[S.bogus[counter as usize].x as isize as usize]
    859
                    [bogus[counter as usize].y as isize as usize]
    870
                    [S.bogus[counter as usize].y as isize as usize]
    860
                    != -1i32)
    871
                    != -1i32)
    861
                {
    872
                {
    862
                    break;
    873
                    break;
    863
                }
    874
                }
    864
            }
    875
            }
    865
            screen[bogus[counter as usize].x as isize as usize]
    876
            S.screen[S.bogus[counter as usize].x as isize as usize]
    866
                [bogus[counter as usize].y as isize as usize] = counter + 2i32;
    877
                [S.bogus[counter as usize].y as isize as usize] = counter + 2i32;
    867
            loop {
    878
            loop {
    868
                index = (rand() as libc::c_ulong).wrapping_rem(
    879
                index = (rand() as libc::c_ulong).wrapping_rem(
    869
                    (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    880
                    (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    870
                        .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong),
    881
                        .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong),
    871
                ) as libc::c_int;
    882
                ) as libc::c_int;
    872
                if !(used_messages[index as usize] != 0i32) {
    883
                if !(S.used_messages[index as usize] != 0i32) {
    873
                    break;
    884
                    break;
    874
                }
    885
                }
    875
            }
    886
            }
    876
            bogus_messages[counter as usize] = index;
    887
            S.bogus_messages[counter as usize] = index;
    877
            used_messages[index as usize] = 1i32;
    888
            S.used_messages[index as usize] = 1i32;
    878
            counter += 1
    889
            counter += 1
    879
        }
    890
        }
    880
    }
    891
    }
    881
    #[no_mangle]
    892
    882
    pub static mut num_bogus: libc::c_int = 0;
    883
    #[no_mangle]
    893
    #[no_mangle]
    884
    pub unsafe extern "C" fn initialize_screen() {
    894
    pub unsafe extern "C" fn initialize_screen() {
    885
        let mut counter: libc::c_int = 0;
    895
        let mut counter: libc::c_int = 0;
    886
        fmt_mvprintw(
    896
        fmt_mvprintw(
    887
            0i32,
    897
            0i32,

    891
                    .to_str()
    901
                    .to_str()
    892
                    .unwrap()
    902
                    .unwrap()
    893
            }),
    903
            }),
    894
        );
    904
        );
    895
        counter = 0i32;
    905
        counter = 0i32;
    896
        while counter <= win.as_ref().unwrap().get_max_x() - 1i32 {
    906
        while counter <= S.win.as_ref().unwrap().get_max_x() - 1i32 {
    897
            fmt_printw(format_args!("{:}", 95i32 as u8 as char));
    907
            fmt_printw(format_args!("{:}", 95i32 as u8 as char));
    898
            counter += 1
    908
            counter += 1
    899
        }
    909
        }
    900
        counter = 0i32;
    910
        counter = 0i32;
    901
        while counter < num_bogus {
    911
        while counter < S.num_bogus {
    902
            draw(bogus[counter as usize]);
    912
            draw(S.bogus[counter as usize]);
    903
            counter += 1
    913
            counter += 1
    904
        }
    914
        }
    905
        draw(kitten);
    915
        draw(S.kitten);
    906
        draw(robot);
    916
        draw(S.robot);
    907
        win.as_ref().unwrap().refresh();
    917
        S.win.as_ref().unwrap().refresh();
    908
    }
    918
    }
    909
    #[no_mangle]
    919
    #[no_mangle]
    910
    pub unsafe extern "C" fn draw(mut o: screen_object) {
    920
    pub unsafe extern "C" fn draw(mut o: screen_object) {
    911
        full_draw(o, 0 != 0i32);
    921
        full_draw(o, 0 != 0i32);
    912
    }
    922
    }

    935
            new |= 1u64 << 14i32 + 8i32
    945
            new |= 1u64 << 14i32 + 8i32
    936
        }
    946
        }
    937
        if o.bold {
    947
        if o.bold {
    938
            new |= 1u64 << 13i32 + 8i32
    948
            new |= 1u64 << 13i32 + 8i32
    939
        }
    949
        }
    950
        S.win
    940
        win.as_ref()
    951
            .as_ref()
    941
            .unwrap()
    952
            .unwrap()
    942
            .attrset(new as libc::c_int as ::pancurses::chtype);
    953
            .attrset(new as libc::c_int as ::pancurses::chtype);
    943
        if in_place {
    954
        if in_place {
    944
            fmt_printw(format_args!(
    955
            fmt_printw(format_args!(
    945
                "{:}",
    956
                "{:}",

    949
            fmt_mvprintw(
    960
            fmt_mvprintw(
    950
                o.y,
    961
                o.y,
    951
                o.x,
    962
                o.x,
    952
                format_args!("{:}", o.character as libc::c_int as u8 as char),
    963
                format_args!("{:}", o.character as libc::c_int as u8 as char),
    953
            );
    964
            );
    954
            win.as_ref().unwrap().mv(o.y, o.x);
    965
            S.win.as_ref().unwrap().mv(o.y, o.x);
    955
        }
    966
        }
    967
        S.win
    956
        win.as_ref()
    968
            .as_ref()
    957
            .unwrap()
    969
            .unwrap()
    958
            .attrset(old as libc::c_int as ::pancurses::chtype);
    970
            .attrset(old as libc::c_int as ::pancurses::chtype);
    959
    }
    971
    }
    960
    #[no_mangle]
    972
    #[no_mangle]
    961
    pub unsafe extern "C" fn instructions() {
    973
    pub unsafe extern "C" fn instructions() {

    974
        ));
    986
        ));
    975
        fmt_printw(format_args!(
    987
        fmt_printw(format_args!(
    976
            "Written originally for the Nerth Pork robotfindskitten contest\n\n"
    988
            "Written originally for the Nerth Pork robotfindskitten contest\n\n"
    977
        ));
    989
        ));
    978
        fmt_printw(format_args!("In this game, you are robot ("));
    990
        fmt_printw(format_args!("In this game, you are robot ("));
    979
        draw_in_place(robot);
    991
        draw_in_place(S.robot);
    980
        fmt_printw(format_args!("). Your job is to find kitten. This task\n"));
    992
        fmt_printw(format_args!("). Your job is to find kitten. This task\n"));
    981
        fmt_printw(format_args!(
    993
        fmt_printw(format_args!(
    982
            "is complicated by the existence of various things which are not kitten.\n"
    994
            "is complicated by the existence of various things which are not kitten.\n"
    983
        ));
    995
        ));
    984
        fmt_printw(format_args!(
    996
        fmt_printw(format_args!(

    989
        ));
    1001
        ));
    990
        fmt_printw(format_args!(
    1002
        fmt_printw(format_args!(
    991
            "the Esc key. See the documentation for more information.\n\n"
    1003
            "the Esc key. See the documentation for more information.\n\n"
    992
        ));
    1004
        ));
    993
        fmt_printw(format_args!("Press any key to start.\n"));
    1005
        fmt_printw(format_args!("Press any key to start.\n"));
    994
        win.as_ref().unwrap().refresh();
    1006
        S.win.as_ref().unwrap().refresh();
    995
        dummy = ::encode_input(win.as_ref().unwrap().getch()) as libc::c_char;
    1007
        dummy = ::encode_input(S.win.as_ref().unwrap().getch()) as libc::c_char;
    996
        win.as_ref().unwrap().clear();
    1008
        S.win.as_ref().unwrap().clear();
    997
    }
    1009
    }
    998
    #[no_mangle]
    1010
    #[no_mangle]
    999
    pub unsafe extern "C" fn draw_in_place(mut o: screen_object) {
    1011
    pub unsafe extern "C" fn draw_in_place(mut o: screen_object) {
    1000
        full_draw(o, 0 != 1i32);
    1012
        full_draw(o, 0 != 1i32);
    1001
    }
    1013
    }
    1002
    /*Game functions*/
    1014
    /*Game functions*/
    1003
    #[no_mangle]
    1015
    #[no_mangle]
    1004
    pub unsafe extern "C" fn play_game() {
    1016
    pub unsafe extern "C" fn play_game() {
    1005
        let mut old_x: libc::c_int = robot.x;
    1017
        let mut old_x: libc::c_int = S.robot.x;
    1006
        let mut old_y: libc::c_int = robot.y;
    1018
        let mut old_y: libc::c_int = S.robot.y;
    1007
        let mut input: libc::c_int = 0;
    1019
        let mut input: libc::c_int = 0;
    1008
        input = ::encode_input(win.as_ref().unwrap().getch());
    1020
        input = ::encode_input(S.win.as_ref().unwrap().getch());
    1009
        while input != 27i32 && input != 'q' as i32 && input != 'Q' as i32 {
    1021
        while input != 27i32 && input != 'q' as i32 && input != 'Q' as i32 {
    1010
            process_input(input);
    1022
            process_input(input);
    1011
            if !(old_x == robot.x && old_y == robot.y) {
    1023
            if !(old_x == S.robot.x && old_y == S.robot.y) {
    1012
                if win.as_ref().unwrap().mv(old_y, old_x) == -1i32 {
    1024
                if S.win.as_ref().unwrap().mv(old_y, old_x) == -1i32 {
    1013
                } else {
    1025
                } else {
    1014
                    win.as_ref().unwrap().addch(' ' as i32 as chtype);
    1026
                    S.win.as_ref().unwrap().addch(' ' as i32 as chtype);
    1015
                };
    1027
                };
    1016
                screen[old_x as isize as usize][old_y as isize as usize] = -1i32;
    1028
                S.screen[old_x as isize as usize][old_y as isize as usize] = -1i32;
    1017
                draw(robot);
    1029
                draw(S.robot);
    1018
                win.as_ref().unwrap().refresh();
    1030
                S.win.as_ref().unwrap().refresh();
    1019
                screen[robot.x as isize as usize][robot.y as isize as usize] = 0i32;
    1031
                S.screen[S.robot.x as isize as usize][S.robot.y as isize as usize] = 0i32;
    1020
                old_x = robot.x;
    1032
                old_x = S.robot.x;
    1021
                old_y = robot.y
    1033
                old_y = S.robot.y
    1022
            }
    1034
            }
    1023
            input = ::encode_input(win.as_ref().unwrap().getch())
    1035
            input = ::encode_input(S.win.as_ref().unwrap().getch())
    1024
        }
    1036
        }
    1025
        message(b"Bye!\x00" as *const u8 as *const libc::c_char as *mut libc::c_char);
    1037
        message(b"Bye!\x00" as *const u8 as *const libc::c_char as *mut libc::c_char);
    1026
        win.as_ref().unwrap().refresh();
    1038
        S.win.as_ref().unwrap().refresh();
    1027
        finish(0i32);
    1039
        finish(0i32);
    1028
    }
    1040
    }
    1029
    #[no_mangle]
    1041
    #[no_mangle]
    1030
    pub unsafe extern "C" fn message(mut message_0: *mut libc::c_char) {
    1042
    pub unsafe extern "C" fn message(mut message_0: *mut libc::c_char) {
    1031
        win.as_ref().unwrap().mv(1i32, 0i32);
    1043
        S.win.as_ref().unwrap().mv(1i32, 0i32);
    1032
        win.as_ref().unwrap().clrtoeol();
    1044
        S.win.as_ref().unwrap().clrtoeol();
    1033
        fmt_mvprintw(
    1045
        fmt_mvprintw(
    1034
            1i32,
    1046
            1i32,
    1035
            0i32,
    1047
            0i32,
    1036
            format_args!(
    1048
            format_args!(
    1037
                "{:.*}",
    1049
                "{:.*}",
    1038
                win.as_ref().unwrap().get_max_x() as usize,
    1050
                S.win.as_ref().unwrap().get_max_x() as usize,
    1039
                unsafe {
    1051
                unsafe {
    1040
                    ::std::ffi::CStr::from_ptr(message_0 as *const libc::c_char)
    1052
                    ::std::ffi::CStr::from_ptr(message_0 as *const libc::c_char)
    1041
                        .to_str()
    1053
                        .to_str()
    1042
                        .unwrap()
    1054
                        .unwrap()
    1043
                }
    1055
                }
    1044
            ),
    1056
            ),
    1045
        );
    1057
        );
    1046
        win.as_ref().unwrap().mv(robot.y, robot.x);
    1058
        S.win.as_ref().unwrap().mv(S.robot.y, S.robot.x);
    1047
        win.as_ref().unwrap().refresh();
    1059
        S.win.as_ref().unwrap().refresh();
    1048
    }
    1060
    }
    1049
    #[no_mangle]
    1061
    #[no_mangle]
    1050
    pub unsafe extern "C" fn process_input(mut input: libc::c_int) {
    1062
    pub unsafe extern "C" fn process_input(mut input: libc::c_int) {
    1051
        let mut check_x: libc::c_int = robot.x;
    1063
        let mut check_x: libc::c_int = S.robot.x;
    1052
        let mut check_y: libc::c_int = robot.y;
    1064
        let mut check_y: libc::c_int = S.robot.y;
    1053
        match input {
    1065
        match input {
    1054
            12 => {
    1066
            12 => {
    1055
                win.as_ref().unwrap().refresh();
    1067
                S.win.as_ref().unwrap().refresh();
    1056
            }
    1068
            }
    1057
            259 | 107 | 75 | 16 => check_y -= 1,
    1069
            259 | 107 | 75 | 16 => check_y -= 1,
    1058
            262 | 121 | 89 => {
    1070
            262 | 121 | 89 => {
    1059
                check_x -= 1;
    1071
                check_x -= 1;
    1060
                check_y -= 1
    1072
                check_y -= 1

    1082
                );
    1094
                );
    1083
                return;
    1095
                return;
    1084
            }
    1096
            }
    1085
        }
    1097
        }
    1086
        if check_y < 3i32
    1098
        if check_y < 3i32
    1087
            || check_y > win.as_ref().unwrap().get_max_y() - 1i32
    1099
            || check_y > S.win.as_ref().unwrap().get_max_y() - 1i32
    1088
            || check_x < 0i32
    1100
            || check_x < 0i32
    1089
            || check_x > win.as_ref().unwrap().get_max_x() - 1i32
    1101
            || check_x > S.win.as_ref().unwrap().get_max_x() - 1i32
    1090
        {
    1102
        {
    1091
            return;
    1103
            return;
    1092
        }
    1104
        }
    1093
        if screen[check_x as isize as usize][check_y as isize as usize] != -1i32 {
    1105
        if S.screen[check_x as isize as usize][check_y as isize as usize] != -1i32 {
    1094
            match screen[check_x as isize as usize][check_y as isize as usize] {
    1106
            match S.screen[check_x as isize as usize][check_y as isize as usize] {
    1095
                0 => {}
    1107
                0 => {}
    1096
                1 => {
    1108
                1 => {
    1097
                    /*We didn't move, or we're stuck in a
    1109
                    /*We didn't move, or we're stuck in a
    1098
                    time warp or something.*/
    1110
                    time warp or something.*/
    1099
                    win.as_ref().unwrap().mv(1i32, 0i32);
    1111
                    S.win.as_ref().unwrap().mv(1i32, 0i32);
    1100
                    win.as_ref().unwrap().clrtoeol();
    1112
                    S.win.as_ref().unwrap().clrtoeol();
    1101
                    play_animation(input);
    1113
                    play_animation(input);
    1102
                }
    1114
                }
    1103
                _ => {
    1115
                _ => {
    1104
                    message(
    1116
                    message(
    1105
                        messages[bogus_messages[(screen[check_x as isize as usize]
    1117
                        messages[S.bogus_messages[(S.screen[check_x as isize as usize]
    1106
                            [check_y as isize as usize]
    1118
                            [check_y as isize as usize]
    1107
                            - 2i32) as usize] as usize]
    1119
                            - 2i32) as usize] as usize]
    1108
                            .as_ptr() as *mut i8,
    1120
                            .as_ptr() as *mut i8,
    1109
                    );
    1121
                    );
    1110
                }
    1122
                }
    1111
            }
    1123
            }
    1112
            return;
    1124
            return;
    1113
        }
    1125
        }
    1114
        robot.x = check_x;
    1126
        S.robot.x = check_x;
    1115
        robot.y = check_y;
    1127
        S.robot.y = check_y;
    1116
    }
    1128
    }
    1117
    #[no_mangle]
    1129
    #[no_mangle]
    1118
    pub unsafe extern "C" fn play_animation(mut input: libc::c_int) {
    1130
    pub unsafe extern "C" fn play_animation(mut input: libc::c_int) {
    1119
        let mut counter: libc::c_int = 0;
    1131
        let mut counter: libc::c_int = 0;
    1120
        counter = 4i32;
    1132
        counter = 4i32;
    1121
        while counter > 0i32 {
    1133
        while counter > 0i32 {
    1122
            if win.as_ref().unwrap().mv(1i32, 50i32 + counter + 1i32) == -1i32 {
    1134
            if S.win.as_ref().unwrap().mv(1i32, 50i32 + counter + 1i32) == -1i32 {
    1123
            } else {
    1135
            } else {
    1124
                win.as_ref().unwrap().addch(' ' as i32 as chtype);
    1136
                S.win.as_ref().unwrap().addch(' ' as i32 as chtype);
    1125
            };
    1137
            };
    1126
            win.as_ref().unwrap().mv(1i32, 50i32 + counter);
    1138
            S.win.as_ref().unwrap().mv(1i32, 50i32 + counter);
    1127
            if input == 0o405i32 || input == 0o402i32 || input == 0o540i32 || input == 0o535i32 {
    1139
            if input == 0o405i32 || input == 0o402i32 || input == 0o540i32 || input == 0o535i32 {
    1128
                draw_in_place(kitten);
    1140
                draw_in_place(S.kitten);
    1129
            } else {
    1141
            } else {
    1130
                draw_in_place(robot);
    1142
                draw_in_place(S.robot);
    1131
            }
    1143
            }
    1132
            if win.as_ref().unwrap().mv(1i32, 50i32 - counter) == -1i32 {
    1144
            if S.win.as_ref().unwrap().mv(1i32, 50i32 - counter) == -1i32 {
    1133
            } else {
    1145
            } else {
    1134
                win.as_ref().unwrap().addch(' ' as i32 as chtype);
    1146
                S.win.as_ref().unwrap().addch(' ' as i32 as chtype);
    1135
            };
    1147
            };
    1136
            win.as_ref().unwrap().mv(1i32, 50i32 - counter + 1i32);
    1148
            S.win.as_ref().unwrap().mv(1i32, 50i32 - counter + 1i32);
    1137
            if input == 0o405i32 || input == 0o402i32 || input == 0o540i32 || input == 0o535i32 {
    1149
            if input == 0o405i32 || input == 0o402i32 || input == 0o540i32 || input == 0o535i32 {
    1138
                draw_in_place(robot);
    1150
                draw_in_place(S.robot);
    1139
            } else {
    1151
            } else {
    1140
                draw_in_place(kitten);
    1152
                draw_in_place(S.kitten);
    1141
            }
    1153
            }
    1142
            win.as_ref().unwrap().refresh();
    1154
            S.win.as_ref().unwrap().refresh();
    1143
            sleep(1i32 as libc::c_uint);
    1155
            sleep(1i32 as libc::c_uint);
    1144
            counter -= 1
    1156
            counter -= 1
    1145
        }
    1157
        }
    1146
        win.as_ref().unwrap().mv(1i32, 0i32);
    1158
        S.win.as_ref().unwrap().mv(1i32, 0i32);
    1147
        win.as_ref().unwrap().addnstr(
    1159
        S.win.as_ref().unwrap().addnstr(
    1148
            ::std::str::from_utf8(b"You found kitten! Way to go, robot!\x00")
    1160
            ::std::str::from_utf8(b"You found kitten! Way to go, robot!\x00")
    1149
                .unwrap()
    1161
                .unwrap()
    1150
                .trim_end_matches('\u{0}'),
    1162
                .trim_end_matches('\u{0}'),
    1151
            -1i32 as usize,
    1163
            -1i32 as usize,
    1152
        );
    1164
        );
    1153
        win.as_ref().unwrap().refresh();
    1165
        S.win.as_ref().unwrap().refresh();
    1154
        finish(0i32);
    1166
        finish(0i32);
    1155
    }
    1167
    }
    1156
    unsafe fn main_0(mut argc: libc::c_int, mut argv: *mut *mut libc::c_char) -> libc::c_int {
    1168
    unsafe fn main_0(mut argc: libc::c_int, mut argv: *mut *mut libc::c_char) -> libc::c_int {
    1157
        if argc == 1i32 {
    1169
        if argc == 1i32 {
    1158
            num_bogus = 20i32
    1170
            S.num_bogus = 20i32
    1159
        } else {
    1171
        } else {
    1160
            num_bogus = atoi(*argv.offset(1isize));
    1172
            S.num_bogus = atoi(*argv.offset(1isize));
    1161
            if num_bogus < 0i32
    1173
            if S.num_bogus < 0i32
    1162
                || num_bogus as libc::c_ulong
    1174
                || S.num_bogus as libc::c_ulong
    1163
                    > (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    1175
                    > (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    1164
                        .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
    1176
                        .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
    1165
            {
    1177
            {
    1166
                fmt_printf(format_args!(
    1178
                fmt_printf(format_args!(
    1167
                    "Run-time parameter must be between 0 and {:}.\n",
    1179
                    "Run-time parameter must be between 0 and {:}.\n",

    Then we run static_to_local_ref to pass a reference to the new State object everywhere it is used:

    select target 'crate; child(static && name("S"));' ;
    select user 'crate; desc(fn && !name("main|main_0"));' ;
    static_to_local_ref ;
    

    Diff #47

    src/robotfindskitten.rs
    17
        kitten: screen_object,
    17
        kitten: screen_object,
    18
        num_bogus: libc::c_int,
    18
        num_bogus: libc::c_int,
    19
    }
    19
    }
    20
    static mut S: State = State {
    20
    static mut S: State = State {
    21
        win: None,
    21
        win: None,
    22
        bogus: [screen_object {
    22
        bogus: [screen_object {
    23
            x: 0,
    23
            x: 0,
    24
            y: 0,
    24
            y: 0,
    25
            color: 0,
    25
            color: 0,

    50
    extern crate pancurses;
    50
    extern crate pancurses;
    51
    extern "C" {
    51
    extern "C" {
    52
        pub type ldat;
    52
        pub type ldat;
    53
        #[no_mangle]
    53
        #[no_mangle]
    54
        fn printf(_: *const libc::c_char, ...) -> libc::c_int;
    54
        fn printf(_: *const libc::c_char, ...) -> libc::c_int;
    55
        #[no_mangle]
    55
        #[no_mangle]
    56
        fn cbreak() -> libc::c_int;
    56
        fn cbreak() -> libc::c_int;
    57
        #[no_mangle]
    57
        #[no_mangle]
    58
        fn endwin() -> libc::c_int;
    58
        fn endwin() -> libc::c_int;
    59
        #[no_mangle]
    59
        #[no_mangle]
    60
        fn has_colors() -> bool;
    60
        fn has_colors() -> bool;
    61
        #[no_mangle]
    61
        #[no_mangle]
    62
        fn initscr() -> *mut WINDOW;
    62
        fn initscr() -> *mut WINDOW;
    63
        #[no_mangle]
    63
        #[no_mangle]
    64
        fn init_pair(_: libc::c_short, _: libc::c_short, _: libc::c_short) -> libc::c_int;
    64
        fn init_pair(_: libc::c_short, _: libc::c_short, _: libc::c_short) -> libc::c_int;
    65
        #[no_mangle]
    65
        #[no_mangle]
    66
        fn intrflush(_: *mut WINDOW, _: bool) -> libc::c_int;
    66
        fn intrflush(_: *mut WINDOW, _: bool) -> libc::c_int;
    67
        #[no_mangle]
    67
        #[no_mangle]
    68
        fn keypad(_: *mut WINDOW, _: bool) -> libc::c_int;
    68
        fn keypad(_: *mut WINDOW, _: bool) -> libc::c_int;
    69
        #[no_mangle]
    69
        #[no_mangle]
    70
        fn mvprintw(_: libc::c_int, _: libc::c_int, _: *const libc::c_char, ...) -> libc::c_int;
    70
        fn mvprintw(_: libc::c_int, _: libc::c_int, _: *const libc::c_char, ...) -> libc::c_int;
    71
        #[no_mangle]
    71
        #[no_mangle]
    72
        fn noecho() -> libc::c_int;
    72
        fn noecho() -> libc::c_int;
    73
        #[no_mangle]
    73
        #[no_mangle]
    74
        fn nonl() -> libc::c_int;
    74
        fn nonl() -> libc::c_int;
    75
        #[no_mangle]
    75
        #[no_mangle]
    76
        fn printw(_: *const libc::c_char, ...) -> libc::c_int;
    76
        fn printw(_: *const libc::c_char, ...) -> libc::c_int;
    77
        #[no_mangle]
    77
        #[no_mangle]
    78
        fn start_color() -> libc::c_int;
    78
        fn start_color() -> libc::c_int;
    79
        #[no_mangle]
    79
        #[no_mangle]
    80
        fn waddch(_: *mut WINDOW, _: chtype) -> libc::c_int;
    80
        fn waddch(_: *mut WINDOW, _: chtype) -> libc::c_int;
    81
        #[no_mangle]
    81
        #[no_mangle]
    82
        fn waddnstr(_: *mut WINDOW, _: *const libc::c_char, _: libc::c_int) -> libc::c_int;
    82
        fn waddnstr(_: *mut WINDOW, _: *const libc::c_char, _: libc::c_int) -> libc::c_int;
    83
        #[no_mangle]
    83
        #[no_mangle]
    84
        fn wclear(_: *mut WINDOW) -> libc::c_int;
    84
        fn wclear(_: *mut WINDOW) -> libc::c_int;
    85
        #[no_mangle]
    85
        #[no_mangle]
    86
        fn wclrtoeol(_: *mut WINDOW) -> libc::c_int;
    86
        fn wclrtoeol(_: *mut WINDOW) -> libc::c_int;
    87
        #[no_mangle]
    87
        #[no_mangle]
    88
        fn wgetch(_: *mut WINDOW) -> libc::c_int;
    88
        fn wgetch(_: *mut WINDOW) -> libc::c_int;
    89
        #[no_mangle]
    89
        #[no_mangle]
    90
        fn wmove(_: *mut WINDOW, _: libc::c_int, _: libc::c_int) -> libc::c_int;
    90
        fn wmove(_: *mut WINDOW, _: libc::c_int, _: libc::c_int) -> libc::c_int;
    91
        #[no_mangle]
    91
        #[no_mangle]
    92
        fn wrefresh(_: *mut WINDOW) -> libc::c_int;
    92
        fn wrefresh(_: *mut WINDOW) -> libc::c_int;
    93
        #[no_mangle]
    93
        #[no_mangle]
    94
        static mut curscr: *mut WINDOW;
    94
        static mut curscr: *mut WINDOW;
    95
        #[no_mangle]
    95
        #[no_mangle]
    96
        static mut stdscr: *mut WINDOW;
    96
        static mut stdscr: *mut WINDOW;

    98
        static mut COLS: libc::c_int;
    98
        static mut COLS: libc::c_int;
    99
        #[no_mangle]
    99
        #[no_mangle]
    100
        static mut LINES: libc::c_int;
    100
        static mut LINES: libc::c_int;
    101
        #[no_mangle]
    101
        #[no_mangle]
    102
        fn signal(__sig: libc::c_int, __handler: __sighandler_t) -> __sighandler_t;
    102
        fn signal(__sig: libc::c_int, __handler: __sighandler_t) -> __sighandler_t;
    103
        #[no_mangle]
    103
        #[no_mangle]
    104
        fn strtol(
    104
        fn strtol(
    105
            __nptr: *const libc::c_char,
    105
            __nptr: *const libc::c_char,
    106
            __endptr: *mut *mut libc::c_char,
    106
            __endptr: *mut *mut libc::c_char,
    107
            __base: libc::c_int,
    107
            __base: libc::c_int,
    108
        ) -> libc::c_long;
    108
        ) -> libc::c_long;
    109
        #[no_mangle]
    109
        #[no_mangle]
    110
        fn rand() -> libc::c_int;
    110
        fn rand() -> libc::c_int;
    111
        #[no_mangle]
    111
        #[no_mangle]
    112
        fn srand(__seed: libc::c_uint);
    112
        fn srand(__seed: libc::c_uint);
    113
        #[no_mangle]
    113
        #[no_mangle]
    114
        fn malloc(_: libc::c_ulong) -> *mut libc::c_void;
    114
        fn malloc(_: libc::c_ulong) -> *mut libc::c_void;
    115
        #[no_mangle]
    115
        #[no_mangle]
    116
        fn exit(_: libc::c_int) -> !;
    116
        fn exit(_: libc::c_int) -> !;
    117
        #[no_mangle]
    117
        #[no_mangle]
    118
        fn time(__timer: *mut time_t) -> time_t;
    118
        fn time(__timer: *mut time_t) -> time_t;
    119
        #[no_mangle]
    119
        #[no_mangle]
    120
        fn sleep(__seconds: libc::c_uint) -> libc::c_uint;
    120
        fn sleep(__seconds: libc::c_uint) -> libc::c_uint;
    121
    }
    121
    }
    122
    extern "C" {
    122
    extern "C" {
    123
        fn wattr_get(
    123
        fn wattr_get(
    124
            win: *mut WINDOW,
    124
            win: *mut WINDOW,
    125
            attrs: *mut attr_t,
    125
            attrs: *mut attr_t,
    126
            pair: *mut libc::c_short,
    126
            pair: *mut libc::c_short,
    127
            opts: *mut libc::c_void,
    127
            opts: *mut libc::c_void,
    128
        ) -> libc::c_int;
    128
        ) -> libc::c_int;
    129
        fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
    129
        fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
    130
    }
    130
    }
    131
    fn fmt_mvprintw(y: libc::c_int, x: libc::c_int, args: ::std::fmt::Arguments) -> libc::c_int {
    131
    fn fmt_mvprintw(
    132
        unsafe { S.win.as_ref().unwrap().mvprintw(y, x, &format!("{}", args)) }
    132
        y: libc::c_int,
    133
        x: libc::c_int,
    134
        args: ::std::fmt::Arguments,
    135
        S_: &mut State,
    136
    ) -> libc::c_int {
    137
        unsafe {
    138
            (*S_)
    139
                .win
    140
                .as_ref()
    141
                .unwrap()
    142
                .mvprintw(y, x, &format!("{}", args))
    143
        }
    133
    }
    144
    }
    134
    fn fmt_printw(args: ::std::fmt::Arguments) -> libc::c_int {
    145
    fn fmt_printw(args: ::std::fmt::Arguments, S_: &mut State) -> libc::c_int {
    135
        unsafe { S.win.as_ref().unwrap().printw(&format!("{}", args)) }
    146
        unsafe { (*S_).win.as_ref().unwrap().printw(&format!("{}", args)) }
    136
    }
    147
    }
    137
    fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
    148
    fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
    138
        print!("{}", args);
    149
        print!("{}", args);
    139
        0
    150
        0
    140
    }
    151
    }
    141
    pub type __time_t = libc::c_long;
    152
    pub type __time_t = libc::c_long;
    142
    pub type chtype = libc::c_ulong;
    153
    pub type chtype = libc::c_ulong;

    230
        pub character: libc::c_char,
    241
        pub character: libc::c_char,
    231
    }
    242
    }
    232
    static ver: &'static str = "1.7320508.406\u{0}";
    243
    static ver: &'static str = "1.7320508.406\u{0}";
    233
    #[inline]
    244
    #[inline]
    234
    unsafe extern "C" fn atoi(mut __nptr: *const libc::c_char) -> libc::c_int {
    245
    unsafe extern "C" fn atoi(mut __nptr: *const libc::c_char) -> libc::c_int {
    235
        return strtol(
    246
        return strtol(
    236
            __nptr,
    247
            __nptr,
    237
            0 as *mut libc::c_void as *mut *mut libc::c_char,
    248
            0 as *mut libc::c_void as *mut *mut libc::c_char,
    238
            10i32,
    249
            10i32,

    655
    /*
    666
    /*
    656
     *Function definitions
    667
     *Function definitions
    657
     */
    668
     */
    658
    /*Initialization and setup functions*/
    669
    /*Initialization and setup functions*/
    659
    #[no_mangle]
    670
    #[no_mangle]
    660
    pub unsafe extern "C" fn initialize_ncurses() {
    671
    pub unsafe extern "C" fn initialize_ncurses(S_: &mut State) {
    661
        signal(2i32, Some(finish));
    672
        signal(2i32, Some(finish));
    662
        S.win = Some(::pancurses::initscr());
    673
        (*S_).win = Some(::pancurses::initscr());
    663
        S.win.as_ref().unwrap().keypad(0 != 1i32);
    674
        (*S_).win.as_ref().unwrap().keypad(0 != 1i32);
    664
        ::pancurses::nonl();
    675
        ::pancurses::nonl();
    665
        0;
    676
        0;
    666
        ::pancurses::noecho();
    677
        ::pancurses::noecho();
    667
        ::pancurses::cbreak();
    678
        ::pancurses::cbreak();
    668
        if ::pancurses::has_colors() {
    679
        if ::pancurses::has_colors() {

    709
            );
    720
            );
    710
        };
    721
        };
    711
    }
    722
    }
    712
    fn encode_input(inp: Option<::pancurses::Input>) -> libc::c_int {
    723
    fn encode_input(inp: Option<::pancurses::Input>) -> libc::c_int {
    713
        use pancurses::Input::*;
    724
        use pancurses::Input::*;
    714
        let inp = match inp {
    725
        let inp = match inp {
    715
            Some(x) => x,
    726
            Some(x) => x,
    716
            None => return -1,
    727
            None => return -1,
    717
        };
    728
        };

    734
            }
    745
           }
    735
        }
    746
        }
    736
    }
    747
    }
    737
    unsafe extern "C" fn finish(mut sig: libc::c_int) {
    748
    unsafe extern "C" fn finish(mut sig: libc::c_int) {
    738
        ::pancurses::endwin();
    749
        ::pancurses::endwin();
    739
        fmt_printf(format_args!(
    750
        fmt_printf(format_args!(
    740
            "{:}{:}{:}",
    751
            "{:}{:}{:}",
    741
            27i32 as u8 as char, '(' as i32 as u8 as char, 'B' as i32 as u8 as char
    752
            27i32 as u8 as char, '(' as i32 as u8 as char, 'B' as i32 as u8 as char
    742
        ));
    753
        ));
    743
        exit(0i32);
    754
        exit(0i32);
    744
    }
    755
    }
    745
    #[no_mangle]
    756
    #[no_mangle]
    746
    pub unsafe extern "C" fn initialize_arrays() {
    757
    pub unsafe extern "C" fn initialize_arrays(S_: &mut State) {
    747
        let mut counter: libc::c_int = 0;
    758
        let mut counter: libc::c_int = 0;
    748
        let mut counter2: libc::c_int = 0;
    759
        let mut counter2: libc::c_int = 0;
    749
        let mut empty: screen_object = screen_object {
    760
        let mut empty: screen_object = screen_object {
    750
            x: 0,
    761
            x: 0,
    751
            y: 0,
    762
            y: 0,
    752
            color: 0,
    763
            color: 0,
    753
            bold: false,
    764
            bold: false,
    754
            character: 0,
    765
            character: 0,
    755
        };
    766
        };
    756
        let mut i: libc::c_int = 0i32;
    767
        let mut i: libc::c_int = 0i32;
    757
        S.screen = unsafe {
    768
        (*S_).screen = unsafe {
    758
            ::c2rust_runtime::CArray::alloc(
    769
            ::c2rust_runtime::CArray::alloc(
    759
                (::std::mem::size_of::<*mut libc::c_int>() as libc::c_ulong)
    770
                (::std::mem::size_of::<*mut libc::c_int>() as libc::c_ulong).wrapping_mul(
    760
                    .wrapping_mul((S.win.as_ref().unwrap().get_max_x() - 1i32 + 1i32) as libc::c_ulong)
    771
                    ((*S_).win.as_ref().unwrap().get_max_x() - 1i32 + 1i32) as libc::c_ulong,
    761
                    as usize
    772
                ) as usize
    762
                    / ::std::mem::size_of::<::c2rust_runtime::CArray<i32>>(),
    773
                    / ::std::mem::size_of::<::c2rust_runtime::CArray<i32>>(),
    763
            )
    774
            )
    764
        };
    775
        };
    765
        i = 0i32;
    776
        i = 0i32;
    766
        while i < S.win.as_ref().unwrap().get_max_x() - 1i32 + 1i32 {
    777
        while i < (*S_).win.as_ref().unwrap().get_max_x() - 1i32 + 1i32 {
    767
            let ref mut fresh0 = S.screen[i as isize as usize];
    778
            let ref mut fresh0 = (*S_).screen[i as isize as usize];
    768
            *fresh0 = unsafe {
    779
            *fresh0 = unsafe {
    769
                ::c2rust_runtime::CArray::alloc(
    780
                ::c2rust_runtime::CArray::alloc(
    770
                    (::std::mem::size_of::() as libc::c_ulong).wrapping_mul(
    781
                    (::std::mem::size_of::() as libc::c_ulong).wrapping_mul(
    771
                        (S.win.as_ref().unwrap().get_max_y() - 1i32 + 1i32) as libc::c_ulong,
    782
                        ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 + 1i32) as libc::c_ulong,
    772
                    ) as usize
    783
                    ) as usize
    773
                        / ::std::mem::size_of::(),
    784
                        / ::std::mem::size_of::(),
    774
                )
    785
                )
    775
            };
    786
            };
    776
            i += 1
    787
            i += 1

    779
        empty.y = -1i32;
    790
        empty.y = -1i32;
    780
        empty.color = 0i32;
    791
        empty.color = 0i32;
    781
        empty.bold = 0 != 0i32;
    792
        empty.bold = 0 != 0i32;
    782
        empty.character = ' ' as i32 as libc::c_char;
    793
        empty.character = ' ' as i32 as libc::c_char;
    783
        counter = 0i32;
    794
        counter = 0i32;
    784
        while counter <= S.win.as_ref().unwrap().get_max_x() - 1i32 {
    795
        while counter <= (*S_).win.as_ref().unwrap().get_max_x() - 1i32 {
    785
            counter2 = 0i32;
    796
            counter2 = 0i32;
    786
            while counter2 <= S.win.as_ref().unwrap().get_max_y() - 1i32 {
    797
            while counter2 <= (*S_).win.as_ref().unwrap().get_max_y() - 1i32 {
    787
                S.screen[counter as isize as usize][counter2 as isize as usize] = -1i32;
    798
                (*S_).screen[counter as isize as usize][counter2 as isize as usize] = -1i32;
    788
                counter2 += 1
    799
                counter2 += 1
    789
            }
    800
            }
    790
            counter += 1
    801
            counter += 1
    791
        }
    802
        }
    792
        counter = 0i32;
    803
        counter = 0i32;
    793
        while (counter as libc::c_ulong)
    804
        while (counter as libc::c_ulong)
    794
            < (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    805
            < (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    795
                .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
    806
                .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
    796
        {
    807
        {
    797
            S.used_messages[counter as usize] = 0i32;
    808
            (*S_).used_messages[counter as usize] = 0i32;
    798
            S.bogus_messages[counter as usize] = 0i32;
    809
            (*S_).bogus_messages[counter as usize] = 0i32;
    799
            S.bogus[counter as usize] = empty;
    810
            (*S_).bogus[counter as usize] = empty;
    800
            counter += 1
    811
            counter += 1
    801
        }
    812
        }
    802
    }
    813
    }
    803
    814
    804
    /* This array contains our internal representation of the screen. The
    815
    /* This array contains our internal representation of the screen. The
    805
    array is bigger than it needs to be, as we don't need to keep track
    816
    array is bigger than it needs to be, as we don't need to keep track
    806
    of the first few rows of the screen. But that requires making an
    817
    of the first few rows of the screen. But that requires making an
    807
    offset function and using that everywhere. So not right now. */
    818
    offset function and using that everywhere. So not right now. */
    808
    819
    809
    #[no_mangle]
    820
    #[no_mangle]
    810
    pub unsafe extern "C" fn initialize_robot() {
    821
    pub unsafe extern "C" fn initialize_robot(S_: &mut State) {
    811
        S.robot.x = rand() % (S.win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
    822
        (*S_).robot.x = rand() % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
    812
        S.robot.y = rand() % (S.win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
    823
        (*S_).robot.y = rand() % ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
    813
        S.robot.character = '#' as i32 as libc::c_char;
    824
        (*S_).robot.character = '#' as i32 as libc::c_char;
    814
        S.robot.color = 0i32;
    825
        (*S_).robot.color = 0i32;
    815
        S.robot.bold = 0 != 0i32;
    826
        (*S_).robot.bold = 0 != 0i32;
    816
        S.screen[S.robot.x as isize as usize][S.robot.y as isize as usize] = 0i32;
    827
        (*S_).screen[(*S_).robot.x as isize as usize][(*S_).robot.y as isize as usize] = 0i32;
    817
    }
    828
    }
    818
    /*Global variables. Bite me, it's fun.*/
    829
    /*Global variables. Bite me, it's fun.*/
    819
    830
    820
    #[no_mangle]
    831
    #[no_mangle]
    821
    pub unsafe extern "C" fn initialize_kitten() {
    832
    pub unsafe extern "C" fn initialize_kitten(S_: &mut State) {
    822
        loop {
    833
        loop {
    823
            S.kitten.x = rand() % (S.win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
    834
            (*S_).kitten.x = rand() % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
    835
            (*S_).kitten.y =
    824
            S.kitten.y = rand() % (S.win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
    836
                rand() % ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
    825
            if !(S.screen[S.kitten.x as isize as usize][S.kitten.y as isize as usize] != -1i32) {
    837
            if !((*S_).screen[(*S_).kitten.x as isize as usize][(*S_).kitten.y as isize as usize]
    838
                != -1i32)
    839
            {
    826
                break;
    840
                break;
    827
            }
    841
            }
    828
        }
    842
        }
    829
        loop {
    843
        loop {
    844
            (*S_).kitten.character =
    830
            S.kitten.character = (rand() % (126i32 - '!' as i32 + 1i32) + '!' as i32) as libc::c_char;
    845
                (rand() % (126i32 - '!' as i32 + 1i32) + '!' as i32) as libc::c_char;
    831
            if !(0 == validchar(S.kitten.character)) {
    846
            if !(0 == validchar((*S_).kitten.character)) {
    832
                break;
    847
                break;
    833
            }
    848
            }
    834
        }
    849
        }
    835
        S.screen[S.kitten.x as isize as usize][S.kitten.y as isize as usize] = 1i32;
    850
        (*S_).screen[(*S_).kitten.x as isize as usize][(*S_).kitten.y as isize as usize] = 1i32;
    836
        S.kitten.color = rand() % 6i32 + 1i32;
    851
        (*S_).kitten.color = rand() % 6i32 + 1i32;
    837
        S.kitten.bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
    852
        (*S_).kitten.bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
    838
    }
    853
    }
    839
    854
    840
    /*Helper functions*/
    855
    /*Helper functions*/
    841
    #[no_mangle]
    856
    #[no_mangle]
    842
    pub unsafe extern "C" fn validchar(mut a: libc::c_char) -> libc::c_int {
    857
    pub unsafe extern "C" fn validchar(mut a: libc::c_char) -> libc::c_int {
    843
        match a as libc::c_int {
    858
        match a as libc::c_int {
    844
            35 | 32 | 127 => return 0i32,
    859
            35 | 32 | 127 => return 0i32,
    845
            _ => {}
    860
            _ => {}
    846
        }
    861
        }
    847
        return 1i32;
    862
        return 1i32;
    848
    }
    863
    }
    849
    #[no_mangle]
    864
    #[no_mangle]
    850
    pub unsafe extern "C" fn initialize_bogus() {
    865
    pub unsafe extern "C" fn initialize_bogus(S_: &mut State) {
    851
        let mut counter: libc::c_int = 0;
    866
        let mut counter: libc::c_int = 0;
    852
        let mut index: libc::c_int = 0;
    867
        let mut index: libc::c_int = 0;
    853
        counter = 0i32;
    868
        counter = 0i32;
    854
        while counter < S.num_bogus {
    869
        while counter < (*S_).num_bogus {
    855
            S.bogus[counter as usize].color = rand() % 6i32 + 1i32;
    870
            (*S_).bogus[counter as usize].color = rand() % 6i32 + 1i32;
    856
            S.bogus[counter as usize].bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
    871
            (*S_).bogus[counter as usize].bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
    857
            loop {
    872
            loop {
    858
                S.bogus[counter as usize].character =
    873
                (*S_).bogus[counter as usize].character =
    859
                    (rand() % (126i32 - '!' as i32 + 1i32) + '!' as i32) as libc::c_char;
    874
                    (rand() % (126i32 - '!' as i32 + 1i32) + '!' as i32) as libc::c_char;
    860
                if !(0 == validchar(S.bogus[counter as usize].character)) {
    875
                if !(0 == validchar((*S_).bogus[counter as usize].character)) {
    861
                    break;
    876
                    break;
    862
                }
    877
                }
    863
            }
    878
            }
    864
            loop {
    879
            loop {
    865
                S.bogus[counter as usize].x =
    880
                (*S_).bogus[counter as usize].x =
    866
                    rand() % (S.win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
    881
                    rand() % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
    867
                S.bogus[counter as usize].y =
    882
                (*S_).bogus[counter as usize].y =
    868
                    rand() % (S.win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
    883
                    rand() % ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
    869
                if !(S.screen[S.bogus[counter as usize].x as isize as usize]
    884
                if !((*S_).screen[(*S_).bogus[counter as usize].x as isize as usize]
    870
                    [S.bogus[counter as usize].y as isize as usize]
    885
                    [(*S_).bogus[counter as usize].y as isize as usize]
    871
                    != -1i32)
    886
                    != -1i32)
    872
                {
    887
                {
    873
                    break;
    888
                    break;
    874
                }
    889
                }
    875
            }
    890
            }
    876
            S.screen[S.bogus[counter as usize].x as isize as usize]
    891
            (*S_).screen[(*S_).bogus[counter as usize].x as isize as usize]
    877
                [S.bogus[counter as usize].y as isize as usize] = counter + 2i32;
    892
                [(*S_).bogus[counter as usize].y as isize as usize] = counter + 2i32;
    878
            loop {
    893
            loop {
    879
                index = (rand() as libc::c_ulong).wrapping_rem(
    894
                index = (rand() as libc::c_ulong).wrapping_rem(
    880
                    (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    895
                    (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    881
                        .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong),
    896
                        .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong),
    882
                ) as libc::c_int;
    897
                ) as libc::c_int;
    883
                if !(S.used_messages[index as usize] != 0i32) {
    898
                if !((*S_).used_messages[index as usize] != 0i32) {
    884
                    break;
    899
                    break;
    885
                }
    900
                }
    886
            }
    901
            }
    887
            S.bogus_messages[counter as usize] = index;
    902
            (*S_).bogus_messages[counter as usize] = index;
    888
            S.used_messages[index as usize] = 1i32;
    903
            (*S_).used_messages[index as usize] = 1i32;
    889
            counter += 1
    904
            counter += 1
    890
        }
    905
        }
    891
    }
    906
    }
    892
    907
    893
    #[no_mangle]
    908
    #[no_mangle]
    894
    pub unsafe extern "C" fn initialize_screen() {
    909
    pub unsafe extern "C" fn initialize_screen(S_: &mut State) {
    895
        let mut counter: libc::c_int = 0;
    910
        let mut counter: libc::c_int = 0;
    896
        fmt_mvprintw(
    911
        fmt_mvprintw(
    897
            0i32,
    912
            0i32,
    898
            0i32,
    913
            0i32,
    899
            format_args!("robotfindskitten v{:}\n\n", unsafe {
    914
            format_args!("robotfindskitten v{:}\n\n", unsafe {
    900
                ::std::ffi::CStr::from_ptr(ver.as_ptr() as *const libc::c_char)
    915
                ::std::ffi::CStr::from_ptr(ver.as_ptr() as *const libc::c_char)
    901
                    .to_str()
    916
                    .to_str()
    902
                    .unwrap()
    917
                    .unwrap()
    903
            }),
    918
            }),
    919
            S_,
    904
        );
    920
        );
    905
        counter = 0i32;
    921
        counter = 0i32;
    906
        while counter <= S.win.as_ref().unwrap().get_max_x() - 1i32 {
    922
        while counter <= (*S_).win.as_ref().unwrap().get_max_x() - 1i32 {
    907
            fmt_printw(format_args!("{:}", 95i32 as u8 as char));
    923
            fmt_printw(format_args!("{:}", 95i32 as u8 as char), S_);
    908
            counter += 1
    924
            counter += 1
    909
        }
    925
        }
    910
        counter = 0i32;
    926
        counter = 0i32;
    911
        while counter < S.num_bogus {
    927
        while counter < (*S_).num_bogus {
    912
            draw(S.bogus[counter as usize]);
    928
            draw((*S_).bogus[counter as usize], S_);
    913
            counter += 1
    929
            counter += 1
    914
        }
    930
        }
    915
        draw(S.kitten);
    931
        draw((*S_).kitten, S_);
    916
        draw(S.robot);
    932
        draw((*S_).robot, S_);
    917
        S.win.as_ref().unwrap().refresh();
    933
        (*S_).win.as_ref().unwrap().refresh();
    918
    }
    934
    }
    919
    #[no_mangle]
    935
    #[no_mangle]
    920
    pub unsafe extern "C" fn draw(mut o: screen_object) {
    936
    pub unsafe extern "C" fn draw(mut o: screen_object, S_: &mut State) {
    921
        full_draw(o, 0 != 0i32);
    937
        full_draw(o, 0 != 0i32, S_);
    922
    }
    938
    }
    923
    #[no_mangle]
    939
    #[no_mangle]
    924
    pub unsafe extern "C" fn full_draw(mut o: screen_object, mut in_place: bool) {
    940
    pub unsafe extern "C" fn full_draw(mut o: screen_object, mut in_place: bool, S_: &mut State) {
    925
        let mut old: attr_t = 0;
    941
        let mut old: attr_t = 0;
    926
        let mut dummy: libc::c_short = 0;
    942
        let mut dummy: libc::c_short = 0;
    927
        let mut new: attr_t = 0;
    943
        let mut new: attr_t = 0;
    928
        if !(stdscr as *const libc::c_void).is_null() {
    944
        if !(stdscr as *const libc::c_void).is_null() {
    929
            if !(&mut old as *mut attr_t as *const libc::c_void).is_null() {
    945
            if !(&mut old as *mut attr_t as *const libc::c_void).is_null() {

    945
            new |= 1u64 << 14i32 + 8i32
    961
            new |= 1u64 << 14i32 + 8i32
    946
        }
    962
        }
    947
        if o.bold {
    963
        if o.bold {
    948
            new |= 1u64 << 13i32 + 8i32
    964
            new |= 1u64 << 13i32 + 8i32
    949
        }
    965
        }
    966
        (*S_)
    950
        S.win
    967
            .win
    951
            .as_ref()
    968
            .as_ref()
    952
            .unwrap()
    969
            .unwrap()
    953
            .attrset(new as libc::c_int as ::pancurses::chtype);
    970
            .attrset(new as libc::c_int as ::pancurses::chtype);
    954
        if in_place {
    971
        if in_place {
    955
            fmt_printw(format_args!(
    972
            fmt_printw(
    956
                "{:}",
    957
                o.character as libc::c_int as u8 as char
    973
                format_args!("{:}", o.character as libc::c_int as u8 as char),
    974
                S_,
    958
            ));
    975
            );
    959
        } else {
    976
        } else {
    960
            fmt_mvprintw(
    977
            fmt_mvprintw(
    961
                o.y,
    978
                o.y,
    962
                o.x,
    979
                o.x,
    963
                format_args!("{:}", o.character as libc::c_int as u8 as char),
    980
                format_args!("{:}", o.character as libc::c_int as u8 as char),
    981
                S_,
    964
            );
    982
            );
    965
            S.win.as_ref().unwrap().mv(o.y, o.x);
    983
            (*S_).win.as_ref().unwrap().mv(o.y, o.x);
    966
        }
    984
        }
    985
        (*S_)
    967
        S.win
    986
            .win
    968
            .as_ref()
    987
            .as_ref()
    969
            .unwrap()
    988
            .unwrap()
    970
            .attrset(old as libc::c_int as ::pancurses::chtype);
    989
            .attrset(old as libc::c_int as ::pancurses::chtype);
    971
    }
    990
    }
    972
    #[no_mangle]
    991
    #[no_mangle]
    973
    pub unsafe extern "C" fn instructions() {
    992
    pub unsafe extern "C" fn instructions(S_: &mut State) {
    974
        let mut dummy: libc::c_char = 0;
    993
        let mut dummy: libc::c_char = 0;
    975
        fmt_mvprintw(
    994
        fmt_mvprintw(
    976
            0i32,
    995
            0i32,
    977
            0i32,
    996
            0i32,
    978
            format_args!("robotfindskitten v{:}\n", unsafe {
    997
            format_args!("robotfindskitten v{:}\n", unsafe {
    979
                ::std::ffi::CStr::from_ptr(ver.as_ptr() as *const libc::c_char)
    998
                ::std::ffi::CStr::from_ptr(ver.as_ptr() as *const libc::c_char)
    980
                    .to_str()
    999
                    .to_str()
    981
                    .unwrap()
    1000
                    .unwrap()
    982
            }),
    1001
            }),
    1002
            S_,
    983
        );
    1003
        );
    984
        fmt_printw(format_args!(
    1004
        fmt_printw(
    985
            "By the illustrious Leonard Richardson (C) 1997, 2000\n"
    1005
            format_args!("By the illustrious Leonard Richardson (C) 1997, 2000\n"),
    1006
            S_,
    986
        ));
    1007
        );
    987
        fmt_printw(format_args!(
    1008
        fmt_printw(
    988
            "Written originally for the Nerth Pork robotfindskitten contest\n\n"
    1009
            format_args!("Written originally for the Nerth Pork robotfindskitten contest\n\n"),
    1010
            S_,
    989
        ));
    1011
        );
    990
        fmt_printw(format_args!("In this game, you are robot ("));
    1012
        fmt_printw(format_args!("In this game, you are robot ("), S_);
    991
        draw_in_place(S.robot);
    1013
        draw_in_place((*S_).robot, S_);
    1014
        fmt_printw(
    992
        fmt_printw(format_args!("). Your job is to find kitten. This task\n"));
    1015
            format_args!("). Your job is to find kitten. This task\n"),
    993
        fmt_printw(format_args!(
    1016
            S_,
    994
            "is complicated by the existence of various things which are not kitten.\n"
    995
        ));
    1017
        );
    996
        fmt_printw(format_args!(
    1018
        fmt_printw(
    997
            "Robot must touch items to determine if they are kitten or not. The game\n"
    1019
            format_args!("is complicated by the existence of various things which are not kitten.\n"),
    1020
            S_,
    998
        ));
    1021
        );
    999
        fmt_printw(format_args!(
    1022
        fmt_printw(
    1023
            format_args!("Robot must touch items to determine if they are kitten or not. The game\n"),
    1024
            S_,
    1025
        );
    1026
        fmt_printw(
    1027
            format_args!(
    1000
            "ends when robotfindskitten. Alternatively, you may end the game by hitting\n"
    1028
                "ends when robotfindskitten. Alternatively, you may end the game by hitting\n"
    1029
            ),
    1030
            S_,
    1001
        ));
    1031
        );
    1002
        fmt_printw(format_args!(
    1032
        fmt_printw(
    1003
            "the Esc key. See the documentation for more information.\n\n"
    1033
            format_args!("the Esc key. See the documentation for more information.\n\n"),
    1034
            S_,
    1004
        ));
    1035
        );
    1005
        fmt_printw(format_args!("Press any key to start.\n"));
    1036
        fmt_printw(format_args!("Press any key to start.\n"), S_);
    1006
        S.win.as_ref().unwrap().refresh();
    1037
        (*S_).win.as_ref().unwrap().refresh();
    1007
        dummy = ::encode_input(S.win.as_ref().unwrap().getch()) as libc::c_char;
    1038
        dummy = ::encode_input((*S_).win.as_ref().unwrap().getch()) as libc::c_char;
    1008
        S.win.as_ref().unwrap().clear();
    1039
        (*S_).win.as_ref().unwrap().clear();
    1009
    }
    1040
    }
    1010
    #[no_mangle]
    1041
    #[no_mangle]
    1011
    pub unsafe extern "C" fn draw_in_place(mut o: screen_object) {
    1042
    pub unsafe extern "C" fn draw_in_place(mut o: screen_object, S_: &mut State) {
    1012
        full_draw(o, 0 != 1i32);
    1043
        full_draw(o, 0 != 1i32, S_);
    1013
    }
    1044
    }
    1014
    /*Game functions*/
    1045
    /*Game functions*/
    1015
    #[no_mangle]
    1046
    #[no_mangle]
    1016
    pub unsafe extern "C" fn play_game() {
    1047
    pub unsafe extern "C" fn play_game(S_: &mut State) {
    1017
        let mut old_x: libc::c_int = S.robot.x;
    1048
        let mut old_x: libc::c_int = (*S_).robot.x;
    1018
        let mut old_y: libc::c_int = S.robot.y;
    1049
        let mut old_y: libc::c_int = (*S_).robot.y;
    1019
        let mut input: libc::c_int = 0;
    1050
        let mut input: libc::c_int = 0;
    1020
        input = ::encode_input(S.win.as_ref().unwrap().getch());
    1051
        input = ::encode_input((*S_).win.as_ref().unwrap().getch());
    1021
        while input != 27i32 && input != 'q' as i32 && input != 'Q' as i32 {
    1052
        while input != 27i32 && input != 'q' as i32 && input != 'Q' as i32 {
    1022
            process_input(input);
    1053
            process_input(input, S_);
    1023
            if !(old_x == S.robot.x && old_y == S.robot.y) {
    1054
            if !(old_x == (*S_).robot.x && old_y == (*S_).robot.y) {
    1024
                if S.win.as_ref().unwrap().mv(old_y, old_x) == -1i32 {
    1055
                if (*S_).win.as_ref().unwrap().mv(old_y, old_x) == -1i32 {
    1025
                } else {
    1056
                } else {
    1026
                    S.win.as_ref().unwrap().addch(' ' as i32 as chtype);
    1057
                    (*S_).win.as_ref().unwrap().addch(' ' as i32 as chtype);
    1027
                };
    1058
                };
    1028
                S.screen[old_x as isize as usize][old_y as isize as usize] = -1i32;
    1059
                (*S_).screen[old_x as isize as usize][old_y as isize as usize] = -1i32;
    1029
                draw(S.robot);
    1060
                draw((*S_).robot, S_);
    1030
                S.win.as_ref().unwrap().refresh();
    1061
                (*S_).win.as_ref().unwrap().refresh();
    1031
                S.screen[S.robot.x as isize as usize][S.robot.y as isize as usize] = 0i32;
    1062
                (*S_).screen[(*S_).robot.x as isize as usize][(*S_).robot.y as isize as usize] = 0i32;
    1032
                old_x = S.robot.x;
    1063
                old_x = (*S_).robot.x;
    1033
                old_y = S.robot.y
    1064
                old_y = (*S_).robot.y
    1034
            }
    1065
            }
    1035
            input = ::encode_input(S.win.as_ref().unwrap().getch())
    1066
            input = ::encode_input((*S_).win.as_ref().unwrap().getch())
    1036
        }
    1067
        }
    1068
        message(
    1037
        message(b"Bye!\x00" as *const u8 as *const libc::c_char as *mut libc::c_char);
    1069
            b"Bye!\x00" as *const u8 as *const libc::c_char as *mut libc::c_char,
    1070
            S_,
    1071
        );
    1038
        S.win.as_ref().unwrap().refresh();
    1072
        (*S_).win.as_ref().unwrap().refresh();
    1039
        finish(0i32);
    1073
        finish(0i32);
    1040
    }
    1074
    }
    1041
    #[no_mangle]
    1075
    #[no_mangle]
    1042
    pub unsafe extern "C" fn message(mut message_0: *mut libc::c_char) {
    1076
    pub unsafe extern "C" fn message(mut message_0: *mut libc::c_char, S_: &mut State) {
    1043
        S.win.as_ref().unwrap().mv(1i32, 0i32);
    1077
        (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
    1044
        S.win.as_ref().unwrap().clrtoeol();
    1078
        (*S_).win.as_ref().unwrap().clrtoeol();
    1045
        fmt_mvprintw(
    1079
        fmt_mvprintw(
    1046
            1i32,
    1080
            1i32,
    1047
            0i32,
    1081
            0i32,
    1048
            format_args!(
    1082
            format_args!(
    1049
                "{:.*}",
    1083
                "{:.*}",
    1050
                S.win.as_ref().unwrap().get_max_x() as usize,
    1084
                (*S_).win.as_ref().unwrap().get_max_x() as usize,
    1051
                unsafe {
    1085
                unsafe {
    1052
                    ::std::ffi::CStr::from_ptr(message_0 as *const libc::c_char)
    1086
                    ::std::ffi::CStr::from_ptr(message_0 as *const libc::c_char)
    1053
                        .to_str()
    1087
                        .to_str()
    1054
                        .unwrap()
    1088
                        .unwrap()
    1055
                }
    1089
                }
    1056
            ),
    1090
            ),
    1091
            S_,
    1057
        );
    1092
        );
    1058
        S.win.as_ref().unwrap().mv(S.robot.y, S.robot.x);
    1093
        (*S_).win.as_ref().unwrap().mv((*S_).robot.y, (*S_).robot.x);
    1059
        S.win.as_ref().unwrap().refresh();
    1094
        (*S_).win.as_ref().unwrap().refresh();
    1060
    }
    1095
    }
    1061
    #[no_mangle]
    1096
    #[no_mangle]
    1062
    pub unsafe extern "C" fn process_input(mut input: libc::c_int) {
    1097
    pub unsafe extern "C" fn process_input(mut input: libc::c_int, S_: &mut State) {
    1063
        let mut check_x: libc::c_int = S.robot.x;
    1098
        let mut check_x: libc::c_int = (*S_).robot.x;
    1064
        let mut check_y: libc::c_int = S.robot.y;
    1099
        let mut check_y: libc::c_int = (*S_).robot.y;
    1065
        match input {
    1100
        match input {
    1066
            12 => {
    1101
            12 => {
    1067
                S.win.as_ref().unwrap().refresh();
    1102
                (*S_).win.as_ref().unwrap().refresh();
    1068
            }
    1103
            }
    1069
            259 | 107 | 75 | 16 => check_y -= 1,
    1104
            259 | 107 | 75 | 16 => check_y -= 1,
    1070
            262 | 121 | 89 => {
    1105
            262 | 121 | 89 => {
    1071
                check_x -= 1;
    1106
                check_x -= 1;
    1072
                check_y -= 1
    1107
                check_y -= 1

    1089
            0 => {}
    1124
            0 => {}
    1090
            _ => {
    1125
            _ => {
    1091
                message(
    1126
                message(
    1092
                    b"Invalid input: Use direction keys or Esc.\x00" as *const u8 as *const libc::c_char
    1127
                    b"Invalid input: Use direction keys or Esc.\x00" as *const u8 as *const libc::c_char
    1093
                        as *mut libc::c_char,
    1128
                        as *mut libc::c_char,
    1129
                    S_,
    1094
                );
    1130
                );
    1095
                return;
    1131
                return;
    1096
            }
    1132
            }
    1097
        }
    1133
        }
    1098
        if check_y < 3i32
    1134
        if check_y < 3i32
    1099
            || check_y > S.win.as_ref().unwrap().get_max_y() - 1i32
    1135
            || check_y > (*S_).win.as_ref().unwrap().get_max_y() - 1i32
    1100
            || check_x < 0i32
    1136
            || check_x < 0i32
    1101
            || check_x > S.win.as_ref().unwrap().get_max_x() - 1i32
    1137
            || check_x > (*S_).win.as_ref().unwrap().get_max_x() - 1i32
    1102
        {
    1138
        {
    1103
            return;
    1139
            return;
    1104
        }
    1140
        }
    1105
        if S.screen[check_x as isize as usize][check_y as isize as usize] != -1i32 {
    1141
        if (*S_).screen[check_x as isize as usize][check_y as isize as usize] != -1i32 {
    1106
            match S.screen[check_x as isize as usize][check_y as isize as usize] {
    1142
            match (*S_).screen[check_x as isize as usize][check_y as isize as usize] {
    1107
                0 => {}
    1143
                0 => {}
    1108
                1 => {
    1144
                1 => {
    1109
                    /*We didn't move, or we're stuck in a
    1145
                    /*We didn't move, or we're stuck in a
    1110
                    time warp or something.*/
    1146
                    time warp or something.*/
    1111
                    S.win.as_ref().unwrap().mv(1i32, 0i32);
    1147
                    (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
    1112
                    S.win.as_ref().unwrap().clrtoeol();
    1148
                    (*S_).win.as_ref().unwrap().clrtoeol();
    1113
                    play_animation(input);
    1149
                    play_animation(input, S_);
    1114
                }
    1150
                }
    1115
                _ => {
    1151
                _ => {
    1116
                    message(
    1152
                    message(
    1117
                        messages[S.bogus_messages[(S.screen[check_x as isize as usize]
    1153
                        messages[(*S_).bogus_messages[((*S_).screen[check_x as isize as usize]
    1118
                            [check_y as isize as usize]
    1154
                            [check_y as isize as usize]
    1119
                            - 2i32) as usize] as usize]
    1155
                            - 2i32) as usize] as usize]
    1120
                            .as_ptr() as *mut i8,
    1156
                            .as_ptr() as *mut i8,
    1157
                        S_,
    1121
                    );
    1158
                    );
    1122
                }
    1159
                }
    1123
            }
    1160
            }
    1124
            return;
    1161
            return;
    1125
        }
    1162
        }
    1126
        S.robot.x = check_x;
    1163
        (*S_).robot.x = check_x;
    1127
        S.robot.y = check_y;
    1164
        (*S_).robot.y = check_y;
    1128
    }
    1165
    }
    1129
    #[no_mangle]
    1166
    #[no_mangle]
    1130
    pub unsafe extern "C" fn play_animation(mut input: libc::c_int) {
    1167
    pub unsafe extern "C" fn play_animation(mut input: libc::c_int, S_: &mut State) {
    1131
        let mut counter: libc::c_int = 0;
    1168
        let mut counter: libc::c_int = 0;
    1132
        counter = 4i32;
    1169
        counter = 4i32;
    1133
        while counter > 0i32 {
    1170
        while counter > 0i32 {
    1134
            if S.win.as_ref().unwrap().mv(1i32, 50i32 + counter + 1i32) == -1i32 {
    1171
            if (*S_).win.as_ref().unwrap().mv(1i32, 50i32 + counter + 1i32) == -1i32 {
    1135
            } else {
    1172
            } else {
    1136
                S.win.as_ref().unwrap().addch(' ' as i32 as chtype);
    1173
                (*S_).win.as_ref().unwrap().addch(' ' as i32 as chtype);
    1137
            };
    1174
            };
    1138
            S.win.as_ref().unwrap().mv(1i32, 50i32 + counter);
    1175
            (*S_).win.as_ref().unwrap().mv(1i32, 50i32 + counter);
    1139
            if input == 0o405i32 || input == 0o402i32 || input == 0o540i32 || input == 0o535i32 {
    1176
            if input == 0o405i32 || input == 0o402i32 || input == 0o540i32 || input == 0o535i32 {
    1140
                draw_in_place(S.kitten);
    1177
                draw_in_place((*S_).kitten, S_);
    1141
            } else {
    1178
            } else {
    1142
                draw_in_place(S.robot);
    1179
                draw_in_place((*S_).robot, S_);
    1143
            }
    1180
            }
    1144
            if S.win.as_ref().unwrap().mv(1i32, 50i32 - counter) == -1i32 {
    1181
            if (*S_).win.as_ref().unwrap().mv(1i32, 50i32 - counter) == -1i32 {
    1145
            } else {
    1182
            } else {
    1146
                S.win.as_ref().unwrap().addch(' ' as i32 as chtype);
    1183
                (*S_).win.as_ref().unwrap().addch(' ' as i32 as chtype);
    1147
            };
    1184
            };
    1148
            S.win.as_ref().unwrap().mv(1i32, 50i32 - counter + 1i32);
    1185
            (*S_).win.as_ref().unwrap().mv(1i32, 50i32 - counter + 1i32);
    1149
            if input == 0o405i32 || input == 0o402i32 || input == 0o540i32 || input == 0o535i32 {
    1186
            if input == 0o405i32 || input == 0o402i32 || input == 0o540i32 || input == 0o535i32 {
    1150
                draw_in_place(S.robot);
    1187
                draw_in_place((*S_).robot, S_);
    1151
            } else {
    1188
            } else {
    1152
                draw_in_place(S.kitten);
    1189
                draw_in_place((*S_).kitten, S_);
    1153
            }
    1190
            }
    1154
            S.win.as_ref().unwrap().refresh();
    1191
            (*S_).win.as_ref().unwrap().refresh();
    1155
            sleep(1i32 as libc::c_uint);
    1192
            sleep(1i32 as libc::c_uint);
    1156
            counter -= 1
    1193
            counter -= 1
    1157
        }
    1194
        }
    1158
        S.win.as_ref().unwrap().mv(1i32, 0i32);
    1195
        (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
    1159
        S.win.as_ref().unwrap().addnstr(
    1196
        (*S_).win.as_ref().unwrap().addnstr(
    1160
            ::std::str::from_utf8(b"You found kitten! Way to go, robot!\x00")
    1197
            ::std::str::from_utf8(b"You found kitten! Way to go, robot!\x00")
    1161
                .unwrap()
    1198
                .unwrap()
    1162
                .trim_end_matches('\u{0}'),
    1199
                .trim_end_matches('\u{0}'),
    1163
            -1i32 as usize,
    1200
            -1i32 as usize,
    1164
        );
    1201
        );
    1165
        S.win.as_ref().unwrap().refresh();
    1202
        (*S_).win.as_ref().unwrap().refresh();
    1166
        finish(0i32);
    1203
        finish(0i32);
    1167
    }
    1204
    }
    1168
    unsafe fn main_0(mut argc: libc::c_int, mut argv: *mut *mut libc::c_char) -> libc::c_int {
    1205
    unsafe fn main_0(mut argc: libc::c_int, mut argv: *mut *mut libc::c_char) -> libc::c_int {
    1169
        if argc == 1i32 {
    1206
        if argc == 1i32 {
    1170
            S.num_bogus = 20i32
    1207
            S.num_bogus = 20i32

    1187
        srand(time(0 as *mut time_t) as libc::c_uint);
    1224
        srand(time(0 as *mut time_t) as libc::c_uint);
    1188
        fmt_printf(format_args!(
    1225
        fmt_printf(format_args!(
    1189
            "{:}{:}{:}",
    1226
            "{:}{:}{:}",
    1190
            27i32 as u8 as char, '(' as i32 as u8 as char, 'U' as i32 as u8 as char
    1227
            27i32 as u8 as char, '(' as i32 as u8 as char, 'U' as i32 as u8 as char
    1191
        ));
    1228
        ));
    1192
        initialize_ncurses();
    1229
        initialize_ncurses(&mut S);
    1193
        initialize_arrays();
    1230
        initialize_arrays(&mut S);
    1194
        initialize_robot();
    1231
        initialize_robot(&mut S);
    1195
        initialize_kitten();
    1232
        initialize_kitten(&mut S);
    1196
        initialize_bogus();
    1233
        initialize_bogus(&mut S);
    1197
        instructions();
    1234
        instructions(&mut S);
    1198
        initialize_screen();
    1235
        initialize_screen(&mut S);
    1199
        play_game();
    1236
        play_game(&mut S);
    1200
        return 0;
    1237
        return 0;
    1201
    }
    1238
    }
    1202
    pub fn main() {
    1239
    pub fn main() {
    1203
        let mut args: Vec<*mut libc::c_char> = Vec::new();
    1240
        let mut args: Vec<*mut libc::c_char> = Vec::new();
    1204
        for arg in ::std::env::args() {
    1241
        for arg in ::std::env::args() {

    The functions that previously accessed the global S now use a reference argument S_, removing a source of unsafety.

    The only function that still accesses S directly is main_0. And since main_0 is called only once per run of the program, we can replace the global S with a local variable declared inside main_0 without affecting the behavior of the program. The static_to_local command performs the necessary transformation (using the marks we previous set up for static_to_local_ref):

    static_to_local
    

    Diff #48

    src/robotfindskitten.rs
    15
        screen: ::c2rust_runtime::CArray<::c2rust_runtime::CArray>,
    15
        screen: ::c2rust_runtime::CArray<::c2rust_runtime::CArray>,
    16
        robot: screen_object,
    16
        robot: screen_object,
    17
        kitten: screen_object,
    17
        kitten: screen_object,
    18
        num_bogus: libc::c_int,
    18
        num_bogus: libc::c_int,
    19
    }
    19
    }
    20
    static mut S: State = State {
    21
        win: None,
    22
        bogus: [screen_object {
    23
            x: 0,
    24
            y: 0,
    25
            color: 0,
    26
            bold: false,
    27
            character: 0,
    28
        }; 406],
    29
        bogus_messages: [0; 406],
    30
        used_messages: [0; 406],
    31
        screen: unsafe { ::c2rust_runtime::CArray::empty() },
    32
        robot: screen_object {
    33
            x: 0,
    34
            y: 0,
    35
            color: 0,
    36
            bold: false,
    37
            character: 0,
    38
        },
    39
        kitten: screen_object {
    40
            x: 0,
    41
            y: 0,
    42
            color: 0,
    43
            bold: false,
    44
            character: 0,
    45
        },
    46
        num_bogus: 0,
    47
    };
    48
    extern crate c2rust_runtime;
    20
    extern crate c2rust_runtime;
    49
    extern crate libc;
    21
    extern crate libc;
    50
    extern crate pancurses;
    22
    extern crate pancurses;
    51
    extern "C" {
    23
    extern "C" {
    52
        pub type ldat;
    24
        pub type ldat;

    1201
        );
    1173
        );
    1202
        (*S_).win.as_ref().unwrap().refresh();
    1174
        (*S_).win.as_ref().unwrap().refresh();
    1203
        finish(0i32);
    1175
        finish(0i32);
    1204
    }
    1176
    }
    1205
    unsafe fn main_0(mut argc: libc::c_int, mut argv: *mut *mut libc::c_char) -> libc::c_int {
    1177
    unsafe fn main_0(mut argc: libc::c_int, mut argv: *mut *mut libc::c_char) -> libc::c_int {
    1178
        let mut S: State = State {
    1179
            win: None,
    1180
            bogus: [screen_object {
    1181
                x: 0,
    1182
                y: 0,
    1183
                color: 0,
    1184
                bold: false,
    1185
                character: 0,
    1186
            }; 406],
    1187
            bogus_messages: [0; 406],
    1188
            used_messages: [0; 406],
    1189
            screen: unsafe { ::c2rust_runtime::CArray::empty() },
    1190
            robot: screen_object {
    1191
                x: 0,
    1192
                y: 0,
    1193
                color: 0,
    1194
                bold: false,
    1195
                character: 0,
    1196
            },
    1197
            kitten: screen_object {
    1198
                x: 0,
    1199
                y: 0,
    1200
                color: 0,
    1201
                bold: false,
    1202
                character: 0,
    1203
            },
    1204
            num_bogus: 0,
    1205
        };
    1206
        if argc == 1i32 {
    1206
        if argc == 1i32 {
    1207
            S.num_bogus = 20i32
    1207
            S.num_bogus = 20i32
    1208
        } else {
    1208
        } else {
    1209
            S.num_bogus = atoi(*argv.offset(1isize));
    1209
            S.num_bogus = atoi(*argv.offset(1isize));
    1210
            if S.num_bogus < 0i32
    1210
            if S.num_bogus < 0i32

    Now there are no static muts remaining in the program.

    There is one final cleanup step to perform. The struct State appears in the signature of several public functions, but State itself is not public, so rustc reports an error. We could make State public, but since there is no reason for the functions in question to be public in the first place, we make the functions private instead:

    select target 'crate; desc(fn && !name("main"));' ;
    set_visibility '' ;
    
    commit
    

    Diff #49

    src/robotfindskitten.rs
    22
    extern crate pancurses;
    22
    extern crate pancurses;
    23
    extern "C" {
    23
    extern "C" {
    24
        pub type ldat;
    24
        pub type ldat;
    25
        #[no_mangle]
    25
        #[no_mangle]
    26
        fn printf(_: *const libc::c_char, ...) -> libc::c_int;
    26
        fn printf(_: *const libc::c_char, ...) -> libc::c_int;
    27
        #[no_mangle]
    27
        #[no_mangle]
    28
        fn cbreak() -> libc::c_int;
    28
        fn cbreak() -> libc::c_int;
    29
        #[no_mangle]
    29
        #[no_mangle]
    30
        fn endwin() -> libc::c_int;
    30
        fn endwin() -> libc::c_int;
    31
        #[no_mangle]
    31
        #[no_mangle]
    32
        fn has_colors() -> bool;
    32
        fn has_colors() -> bool;
    33
        #[no_mangle]
    33
        #[no_mangle]
    34
        fn initscr() -> *mut WINDOW;
    34
        fn initscr() -> *mut WINDOW;
    35
        #[no_mangle]
    35
        #[no_mangle]
    36
        fn init_pair(_: libc::c_short, _: libc::c_short, _: libc::c_short) -> libc::c_int;
    36
        fn init_pair(_: libc::c_short, _: libc::c_short, _: libc::c_short) -> libc::c_int;
    37
        #[no_mangle]
    37
        #[no_mangle]
    38
        fn intrflush(_: *mut WINDOW, _: bool) -> libc::c_int;
    38
        fn intrflush(_: *mut WINDOW, _: bool) -> libc::c_int;
    39
        #[no_mangle]
    39
        #[no_mangle]
    40
        fn keypad(_: *mut WINDOW, _: bool) -> libc::c_int;
    40
        fn keypad(_: *mut WINDOW, _: bool) -> libc::c_int;
    41
        #[no_mangle]
    41
        #[no_mangle]
    42
        fn mvprintw(_: libc::c_int, _: libc::c_int, _: *const libc::c_char, ...) -> libc::c_int;
    42
        fn mvprintw(_: libc::c_int, _: libc::c_int, _: *const libc::c_char, ...) -> libc::c_int;
    43
        #[no_mangle]
    43
        #[no_mangle]
    44
        fn noecho() -> libc::c_int;
    44
        fn noecho() -> libc::c_int;
    45
        #[no_mangle]
    45
        #[no_mangle]
    46
        fn nonl() -> libc::c_int;
    46
        fn nonl() -> libc::c_int;
    47
        #[no_mangle]
    47
        #[no_mangle]
    48
        fn printw(_: *const libc::c_char, ...) -> libc::c_int;
    48
        fn printw(_: *const libc::c_char, ...) -> libc::c_int;
    49
        #[no_mangle]
    49
        #[no_mangle]
    50
        fn start_color() -> libc::c_int;
    50
        fn start_color() -> libc::c_int;
    51
        #[no_mangle]
    51
        #[no_mangle]
    52
        fn waddch(_: *mut WINDOW, _: chtype) -> libc::c_int;
    52
        fn waddch(_: *mut WINDOW, _: chtype) -> libc::c_int;
    53
        #[no_mangle]
    53
        #[no_mangle]
    54
        fn waddnstr(_: *mut WINDOW, _: *const libc::c_char, _: libc::c_int) -> libc::c_int;
    54
        fn waddnstr(_: *mut WINDOW, _: *const libc::c_char, _: libc::c_int) -> libc::c_int;
    55
        #[no_mangle]
    55
        #[no_mangle]
    56
        fn wclear(_: *mut WINDOW) -> libc::c_int;
    56
        fn wclear(_: *mut WINDOW) -> libc::c_int;
    57
        #[no_mangle]
    57
        #[no_mangle]
    58
        fn wclrtoeol(_: *mut WINDOW) -> libc::c_int;
    58
        fn wclrtoeol(_: *mut WINDOW) -> libc::c_int;
    59
        #[no_mangle]
    59
        #[no_mangle]
    60
        fn wgetch(_: *mut WINDOW) -> libc::c_int;
    60
        fn wgetch(_: *mut WINDOW) -> libc::c_int;
    61
        #[no_mangle]
    61
        #[no_mangle]
    62
        fn wmove(_: *mut WINDOW, _: libc::c_int, _: libc::c_int) -> libc::c_int;
    62
        fn wmove(_: *mut WINDOW, _: libc::c_int, _: libc::c_int) -> libc::c_int;
    63
        #[no_mangle]
    63
        #[no_mangle]
    64
        fn wrefresh(_: *mut WINDOW) -> libc::c_int;
    64
        fn wrefresh(_: *mut WINDOW) -> libc::c_int;
    65
        #[no_mangle]
    65
        #[no_mangle]
    66
        static mut curscr: *mut WINDOW;
    66
        static mut curscr: *mut WINDOW;
    67
        #[no_mangle]
    67
        #[no_mangle]
    68
        static mut stdscr: *mut WINDOW;
    68
        static mut stdscr: *mut WINDOW;

    70
        static mut COLS: libc::c_int;
    70
        static mut COLS: libc::c_int;
    71
        #[no_mangle]
    71
        #[no_mangle]
    72
        static mut LINES: libc::c_int;
    72
        static mut LINES: libc::c_int;
    73
        #[no_mangle]
    73
        #[no_mangle]
    74
        fn signal(__sig: libc::c_int, __handler: __sighandler_t) -> __sighandler_t;
    74
        fn signal(__sig: libc::c_int, __handler: __sighandler_t) -> __sighandler_t;
    75
        #[no_mangle]
    75
        #[no_mangle]
    76
        fn strtol(
    76
        fn strtol(
    77
            __nptr: *const libc::c_char,
    77
            __nptr: *const libc::c_char,
    78
            __endptr: *mut *mut libc::c_char,
    78
            __endptr: *mut *mut libc::c_char,
    79
            __base: libc::c_int,
    79
            __base: libc::c_int,
    80
        ) -> libc::c_long;
    80
        ) -> libc::c_long;
    81
        #[no_mangle]
    81
        #[no_mangle]
    82
        fn rand() -> libc::c_int;
    82
        fn rand() -> libc::c_int;
    83
        #[no_mangle]
    83
        #[no_mangle]
    84
        fn srand(__seed: libc::c_uint);
    84
        fn srand(__seed: libc::c_uint);
    85
        #[no_mangle]
    85
        #[no_mangle]
    86
        fn malloc(_: libc::c_ulong) -> *mut libc::c_void;
    86
        fn malloc(_: libc::c_ulong) -> *mut libc::c_void;
    87
        #[no_mangle]
    87
        #[no_mangle]
    88
        fn exit(_: libc::c_int) -> !;
    88
        fn exit(_: libc::c_int) -> !;
    89
        #[no_mangle]
    89
        #[no_mangle]
    90
        fn time(__timer: *mut time_t) -> time_t;
    90
        fn time(__timer: *mut time_t) -> time_t;
    91
        #[no_mangle]
    91
        #[no_mangle]
    92
        fn sleep(__seconds: libc::c_uint) -> libc::c_uint;
    92
        fn sleep(__seconds: libc::c_uint) -> libc::c_uint;
    93
    }
    93
    }
    94
    extern "C" {
    94
    extern "C" {
    95
        fn wattr_get(
    95
        fn wattr_get(
    96
            win: *mut WINDOW,
    96
            win: *mut WINDOW,
    97
            attrs: *mut attr_t,
    97
            attrs: *mut attr_t,
    98
            pair: *mut libc::c_short,
    98
            pair: *mut libc::c_short,
    99
            opts: *mut libc::c_void,
    99
            opts: *mut libc::c_void,
    100
        ) -> libc::c_int;
    100
        ) -> libc::c_int;
    101
        fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
    101
        fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
    102
    }
    102
    }
    103
    fn fmt_mvprintw(
    103
    fn fmt_mvprintw(
    104
        y: libc::c_int,
    104
        y: libc::c_int,
    105
        x: libc::c_int,
    105
        x: libc::c_int,
    106
        args: ::std::fmt::Arguments,
    106
        args: ::std::fmt::Arguments,

    117
    fn fmt_printw(args: ::std::fmt::Arguments, S_: &mut State) -> libc::c_int {
    117
    fn fmt_printw(args: ::std::fmt::Arguments, S_: &mut State) -> libc::c_int {
    118
        unsafe { (*S_).win.as_ref().unwrap().printw(&format!("{}", args)) }
    118
        unsafe { (*S_).win.as_ref().unwrap().printw(&format!("{}", args)) }
    119
    }
    119
    }
    120
    fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
    120
    fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
    121
        print!("{}", args);
    121
        print!("{}", args);
    122
        0
    122
        0
    123
    }
    123
    }
    124
    pub type __time_t = libc::c_long;
    124
    pub type __time_t = libc::c_long;
    125
    pub type chtype = libc::c_ulong;
    125
    pub type chtype = libc::c_ulong;

    213
        pub character: libc::c_char,
    213
        pub character: libc::c_char,
    214
    }
    214
    }
    215
    static ver: &'static str = "1.7320508.406\u{0}";
    215
    static ver: &'static str = "1.7320508.406\u{0}";
    216
    #[inline]
    216
    #[inline]
    217
    unsafe extern "C" fn atoi(mut __nptr: *const libc::c_char) -> libc::c_int {
    217
    unsafe extern "C" fn atoi(mut __nptr: *const libc::c_char) -> libc::c_int {
    218
        return strtol(
    218
        return strtol(
    219
            __nptr,
    219
            __nptr,
    220
            0 as *mut libc::c_void as *mut *mut libc::c_char,
    220
            0 as *mut libc::c_void as *mut *mut libc::c_char,
    221
            10i32,
    221
            10i32,

    638
    /*
    638
    /*
    639
     *Function definitions
    639
     *Function definitions
    640
     */
    640
     */
    641
    /*Initialization and setup functions*/
    641
    /*Initialization and setup functions*/
    642
    #[no_mangle]
    642
    #[no_mangle]
    643
    pub unsafe extern "C" fn initialize_ncurses(S_: &mut State) {
    643
    unsafe extern "C" fn initialize_ncurses(S_: &mut State) {
    644
        signal(2i32, Some(finish));
    644
        signal(2i32, Some(finish));
    645
        (*S_).win = Some(::pancurses::initscr());
    645
        (*S_).win = Some(::pancurses::initscr());
    646
        (*S_).win.as_ref().unwrap().keypad(0 != 1i32);
    646
        (*S_).win.as_ref().unwrap().keypad(0 != 1i32);
    647
        ::pancurses::nonl();
    647
        ::pancurses::nonl();
    648
        0;
    648
        0;

    692
            );
    692
            );
    693
        };
    693
        };
    694
    }
    694
    }
    695
    fn encode_input(inp: Option<::pancurses::Input>) -> libc::c_int {
    695
    fn encode_input(inp: Option<::pancurses::Input>) -> libc::c_int {
    696
        use pancurses::Input::*;
    696
        use pancurses::Input::*;
    697
        let inp = match inp {
    697
        let inp = match inp {
    698
            Some(x) => x,
    698
            Some(x) => x,
    699
            None => return -1,
    699
            None => return -1,
    700
        };
    700
        };

    717
           }
    717
            }
    718
        }
    718
        }
    719
    }
    719
    }
    720
    unsafe extern "C" fn finish(mut sig: libc::c_int) {
    720
    unsafe extern "C" fn finish(mut sig: libc::c_int) {
    721
        ::pancurses::endwin();
    721
        ::pancurses::endwin();
    722
        fmt_printf(format_args!(
    722
        fmt_printf(format_args!(
    723
            "{:}{:}{:}",
    723
            "{:}{:}{:}",
    724
            27i32 as u8 as char, '(' as i32 as u8 as char, 'B' as i32 as u8 as char
    724
            27i32 as u8 as char, '(' as i32 as u8 as char, 'B' as i32 as u8 as char
    725
        ));
    725
        ));
    726
        exit(0i32);
    726
        exit(0i32);
    727
    }
    727
    }
    728
    #[no_mangle]
    728
    #[no_mangle]
    729
    pub unsafe extern "C" fn initialize_arrays(S_: &mut State) {
    729
    unsafe extern "C" fn initialize_arrays(S_: &mut State) {
    730
        let mut counter: libc::c_int = 0;
    730
        let mut counter: libc::c_int = 0;
    731
        let mut counter2: libc::c_int = 0;
    731
        let mut counter2: libc::c_int = 0;
    732
        let mut empty: screen_object = screen_object {
    732
        let mut empty: screen_object = screen_object {
    733
            x: 0,
    733
            x: 0,
    734
            y: 0,
    734
            y: 0,

    788
    array is bigger than it needs to be, as we don't need to keep track
    788
    array is bigger than it needs to be, as we don't need to keep track
    789
    of the first few rows of the screen. But that requires making an
    789
    of the first few rows of the screen. But that requires making an
    790
    offset function and using that everywhere. So not right now. */
    790
    offset function and using that everywhere. So not right now. */
    791
    791
    792
    #[no_mangle]
    792
    #[no_mangle]
    793
    pub unsafe extern "C" fn initialize_robot(S_: &mut State) {
    793
    unsafe extern "C" fn initialize_robot(S_: &mut State) {
    794
        (*S_).robot.x = rand() % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
    794
        (*S_).robot.x = rand() % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
    795
        (*S_).robot.y = rand() % ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
    795
        (*S_).robot.y = rand() % ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
    796
        (*S_).robot.character = '#' as i32 as libc::c_char;
    796
        (*S_).robot.character = '#' as i32 as libc::c_char;
    797
        (*S_).robot.color = 0i32;
    797
        (*S_).robot.color = 0i32;
    798
        (*S_).robot.bold = 0 != 0i32;
    798
        (*S_).robot.bold = 0 != 0i32;
    799
        (*S_).screen[(*S_).robot.x as isize as usize][(*S_).robot.y as isize as usize] = 0i32;
    799
        (*S_).screen[(*S_).robot.x as isize as usize][(*S_).robot.y as isize as usize] = 0i32;
    800
    }
    800
    }
    801
    /*Global variables. Bite me, it's fun.*/
    801
    /*Global variables. Bite me, it's fun.*/
    802
    802
    803
    #[no_mangle]
    803
    #[no_mangle]
    804
    pub unsafe extern "C" fn initialize_kitten(S_: &mut State) {
    804
    unsafe extern "C" fn initialize_kitten(S_: &mut State) {
    805
        loop {
    805
        loop {
    806
            (*S_).kitten.x = rand() % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
    806
            (*S_).kitten.x = rand() % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
    807
            (*S_).kitten.y =
    807
            (*S_).kitten.y =
    808
                rand() % ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
    808
                rand() % ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
    809
            if !((*S_).screen[(*S_).kitten.x as isize as usize][(*S_).kitten.y as isize as usize]
    809
            if !((*S_).screen[(*S_).kitten.x as isize as usize][(*S_).kitten.y as isize as usize]

    824
        (*S_).kitten.bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
    824
        (*S_).kitten.bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
    825
    }
    825
    }
    826
    826
    827
    /*Helper functions*/
    827
    /*Helper functions*/
    828
    #[no_mangle]
    828
    #[no_mangle]
    829
    pub unsafe extern "C" fn validchar(mut a: libc::c_char) -> libc::c_int {
    829
    unsafe extern "C" fn validchar(mut a: libc::c_char) -> libc::c_int {
    830
        match a as libc::c_int {
    830
        match a as libc::c_int {
    831
            35 | 32 | 127 => return 0i32,
    831
            35 | 32 | 127 => return 0i32,
    832
            _ => {}
    832
            _ => {}
    833
        }
    833
        }
    834
        return 1i32;
    834
        return 1i32;
    835
    }
    835
    }
    836
    #[no_mangle]
    836
    #[no_mangle]
    837
    pub unsafe extern "C" fn initialize_bogus(S_: &mut State) {
    837
    unsafe extern "C" fn initialize_bogus(S_: &mut State) {
    838
        let mut counter: libc::c_int = 0;
    838
        let mut counter: libc::c_int = 0;
    839
        let mut index: libc::c_int = 0;
    839
        let mut index: libc::c_int = 0;
    840
        counter = 0i32;
    840
        counter = 0i32;
    841
        while counter < (*S_).num_bogus {
    841
        while counter < (*S_).num_bogus {
    842
            (*S_).bogus[counter as usize].color = rand() % 6i32 + 1i32;
    842
            (*S_).bogus[counter as usize].color = rand() % 6i32 + 1i32;

    876
            counter += 1
    876
            counter += 1
    877
        }
    877
        }
    878
    }
    878
    }
    879
    879
    880
    #[no_mangle]
    880
    #[no_mangle]
    881
    pub unsafe extern "C" fn initialize_screen(S_: &mut State) {
    881
    unsafe extern "C" fn initialize_screen(S_: &mut State) {
    882
        let mut counter: libc::c_int = 0;
    882
        let mut counter: libc::c_int = 0;
    883
        fmt_mvprintw(
    883
        fmt_mvprintw(
    884
            0i32,
    884
            0i32,
    885
            0i32,
    885
            0i32,
    886
            format_args!("robotfindskitten v{:}\n\n", unsafe {
    886
            format_args!("robotfindskitten v{:}\n\n", unsafe {

    903
        draw((*S_).kitten, S_);
    903
        draw((*S_).kitten, S_);
    904
        draw((*S_).robot, S_);
    904
        draw((*S_).robot, S_);
    905
        (*S_).win.as_ref().unwrap().refresh();
    905
        (*S_).win.as_ref().unwrap().refresh();
    906
    }
    906
    }
    907
    #[no_mangle]
    907
    #[no_mangle]
    908
    pub unsafe extern "C" fn draw(mut o: screen_object, S_: &mut State) {
    908
    unsafe extern "C" fn draw(mut o: screen_object, S_: &mut State) {
    909
        full_draw(o, 0 != 0i32, S_);
    909
        full_draw(o, 0 != 0i32, S_);
    910
    }
    910
    }
    911
    #[no_mangle]
    911
    #[no_mangle]
    912
    pub unsafe extern "C" fn full_draw(mut o: screen_object, mut in_place: bool, S_: &mut State) {
    912
    unsafe extern "C" fn full_draw(mut o: screen_object, mut in_place: bool, S_: &mut State) {
    913
        let mut old: attr_t = 0;
    913
        let mut old: attr_t = 0;
    914
        let mut dummy: libc::c_short = 0;
    914
        let mut dummy: libc::c_short = 0;
    915
        let mut new: attr_t = 0;
    915
        let mut new: attr_t = 0;
    916
        if !(stdscr as *const libc::c_void).is_null() {
    916
        if !(stdscr as *const libc::c_void).is_null() {
    917
            if !(&mut old as *mut attr_t as *const libc::c_void).is_null() {
    917
            if !(&mut old as *mut attr_t as *const libc::c_void).is_null() {

    959
            .as_ref()
    959
            .as_ref()
    960
            .unwrap()
    960
            .unwrap()
    961
            .attrset(old as libc::c_int as ::pancurses::chtype);
    961
            .attrset(old as libc::c_int as ::pancurses::chtype);
    962
    }
    962
    }
    963
    #[no_mangle]
    963
    #[no_mangle]
    964
    pub unsafe extern "C" fn instructions(S_: &mut State) {
    964
    unsafe extern "C" fn instructions(S_: &mut State) {
    965
        let mut dummy: libc::c_char = 0;
    965
        let mut dummy: libc::c_char = 0;
    966
        fmt_mvprintw(
    966
        fmt_mvprintw(
    967
            0i32,
    967
            0i32,
    968
            0i32,
    968
            0i32,
    969
            format_args!("robotfindskitten v{:}\n", unsafe {
    969
            format_args!("robotfindskitten v{:}\n", unsafe {

    1009
        (*S_).win.as_ref().unwrap().refresh();
    1009
        (*S_).win.as_ref().unwrap().refresh();
    1010
        dummy = ::encode_input((*S_).win.as_ref().unwrap().getch()) as libc::c_char;
    1010
        dummy = ::encode_input((*S_).win.as_ref().unwrap().getch()) as libc::c_char;
    1011
        (*S_).win.as_ref().unwrap().clear();
    1011
        (*S_).win.as_ref().unwrap().clear();
    1012
    }
    1012
    }
    1013
    #[no_mangle]
    1013
    #[no_mangle]
    1014
    pub unsafe extern "C" fn draw_in_place(mut o: screen_object, S_: &mut State) {
    1014
    unsafe extern "C" fn draw_in_place(mut o: screen_object, S_: &mut State) {
    1015
        full_draw(o, 0 != 1i32, S_);
    1015
        full_draw(o, 0 != 1i32, S_);
    1016
    }
    1016
    }
    1017
    /*Game functions*/
    1017
    /*Game functions*/
    1018
    #[no_mangle]
    1018
    #[no_mangle]
    1019
    pub unsafe extern "C" fn play_game(S_: &mut State) {
    1019
    unsafe extern "C" fn play_game(S_: &mut State) {
    1020
        let mut old_x: libc::c_int = (*S_).robot.x;
    1020
        let mut old_x: libc::c_int = (*S_).robot.x;
    1021
        let mut old_y: libc::c_int = (*S_).robot.y;
    1021
        let mut old_y: libc::c_int = (*S_).robot.y;
    1022
        let mut input: libc::c_int = 0;
    1022
        let mut input: libc::c_int = 0;
    1023
        input = ::encode_input((*S_).win.as_ref().unwrap().getch());
    1023
        input = ::encode_input((*S_).win.as_ref().unwrap().getch());
    1024
        while input != 27i32 && input != 'q' as i32 && input != 'Q' as i32 {
    1024
        while input != 27i32 && input != 'q' as i32 && input != 'Q' as i32 {

    1043
        );
    1043
        );
    1044
        (*S_).win.as_ref().unwrap().refresh();
    1044
        (*S_).win.as_ref().unwrap().refresh();
    1045
        finish(0i32);
    1045
        finish(0i32);
    1046
    }
    1046
    }
    1047
    #[no_mangle]
    1047
    #[no_mangle]
    1048
    pub unsafe extern "C" fn message(mut message_0: *mut libc::c_char, S_: &mut State) {
    1048
    unsafe extern "C" fn message(mut message_0: *mut libc::c_char, S_: &mut State) {
    1049
        (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
    1049
        (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
    1050
        (*S_).win.as_ref().unwrap().clrtoeol();
    1050
        (*S_).win.as_ref().unwrap().clrtoeol();
    1051
        fmt_mvprintw(
    1051
        fmt_mvprintw(
    1052
            1i32,
    1052
            1i32,
    1053
            0i32,
    1053
            0i32,

    1064
        );
    1064
        );
    1065
        (*S_).win.as_ref().unwrap().mv((*S_).robot.y, (*S_).robot.x);
    1065
        (*S_).win.as_ref().unwrap().mv((*S_).robot.y, (*S_).robot.x);
    1066
        (*S_).win.as_ref().unwrap().refresh();
    1066
        (*S_).win.as_ref().unwrap().refresh();
    1067
    }
    1067
    }
    1068
    #[no_mangle]
    1068
    #[no_mangle]
    1069
    pub unsafe extern "C" fn process_input(mut input: libc::c_int, S_: &mut State) {
    1069
    unsafe extern "C" fn process_input(mut input: libc::c_int, S_: &mut State) {
    1070
        let mut check_x: libc::c_int = (*S_).robot.x;
    1070
        let mut check_x: libc::c_int = (*S_).robot.x;
    1071
        let mut check_y: libc::c_int = (*S_).robot.y;
    1071
        let mut check_y: libc::c_int = (*S_).robot.y;
    1072
        match input {
    1072
        match input {
    1073
            12 => {
    1073
            12 => {
    1074
                (*S_).win.as_ref().unwrap().refresh();
    1074
                (*S_).win.as_ref().unwrap().refresh();

    1134
        }
    1134
        }
    1135
        (*S_).robot.x = check_x;
    1135
        (*S_).robot.x = check_x;
    1136
        (*S_).robot.y = check_y;
    1136
        (*S_).robot.y = check_y;
    1137
    }
    1137
    }
    1138
    #[no_mangle]
    1138
    #[no_mangle]
    1139
    pub unsafe extern "C" fn play_animation(mut input: libc::c_int, S_: &mut State) {
    1139
    unsafe extern "C" fn play_animation(mut input: libc::c_int, S_: &mut State) {
    1140
        let mut counter: libc::c_int = 0;
    1140
        let mut counter: libc::c_int = 0;
    1141
        counter = 4i32;
    1141
        counter = 4i32;
    1142
        while counter > 0i32 {
    1142
        while counter > 0i32 {
    1143
            if (*S_).win.as_ref().unwrap().mv(1i32, 50i32 + counter + 1i32) == -1i32 {
    1143
            if (*S_).win.as_ref().unwrap().mv(1i32, 50i32 + counter + 1i32) == -1i32 {
    1144
            } else {
    1144
            } else {

    libc calls

    robotfindskitten makes a number of calls to libc functions, such as sleep and rand, using the FFI. Rust's standard library provides most of the same functionality, so we can replace these libc calls with safe equivalents.

    We replace sleep with std::thread::sleep:

    rewrite_expr 'sleep(__e)'
        '::std::thread::sleep(
            ::std::time::Duration::from_secs(__e as u64))' ;
    

    Diff #50

    src/robotfindskitten.rs
    1159
                draw_in_place((*S_).robot, S_);
    1159
                draw_in_place((*S_).robot, S_);
    1160
            } else {
    1160
            } else {
    1161
                draw_in_place((*S_).kitten, S_);
    1161
                draw_in_place((*S_).kitten, S_);
    1162
            }
    1162
            }
    1163
            (*S_).win.as_ref().unwrap().refresh();
    1163
            (*S_).win.as_ref().unwrap().refresh();
    1164
            sleep(1i32 as libc::c_uint);
    1164
            ::std::thread::sleep(::std::time::Duration::from_secs(
    1165
                1i32 as libc::c_uint as u64,
    1166
            ));
    1165
            counter -= 1
    1167
            counter -= 1
    1166
        }
    1168
        }
    1167
        (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
    1169
        (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
    1168
        (*S_).win.as_ref().unwrap().addnstr(
    1170
        (*S_).win.as_ref().unwrap().addnstr(
    1169
            ::std::str::from_utf8(b"You found kitten! Way to go, robot!\x00")
    1171
            ::std::str::from_utf8(b"You found kitten! Way to go, robot!\x00")

    We replace atoi with a call to from_str:

    rewrite_expr 'atoi(__e)'
        '<libc::c_int as ::std::str::FromStr>::from_str(
            ::std::ffi::CStr::from_ptr(__e).to_str().unwrap()).unwrap()' ;
    

    Diff #51

    src/robotfindskitten.rs
    1206
            num_bogus: 0,
    1206
            num_bogus: 0,
    1207
        };
    1207
        };
    1208
        if argc == 1i32 {
    1208
        if argc == 1i32 {
    1209
            S.num_bogus = 20i32
    1209
            S.num_bogus = 20i32
    1210
        } else {
    1210
        } else {
    1211
            S.num_bogus = atoi(*argv.offset(1isize));
    1211
            S.num_bogus = as ::std::str::FromStr>::from_str(
    1212
                ::std::ffi::CStr::from_ptr(*argv.offset(1isize))
    1213
                    .to_str()
    1214
                    .unwrap(),
    1215
            )
    1216
            .unwrap();
    1212
            if S.num_bogus < 0i32
    1217
            if S.num_bogus < 0i32
    1213
                || S.num_bogus as libc::c_ulong
    1218
                || S.num_bogus as libc::c_ulong
    1214
                    > (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    1219
                    > (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    1215
                        .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
    1220
                        .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
    1216
            {
    1221
            {

    In the version of glibc we used for translating robotfindskitten, atoi is actually provided as an inline wrapper function in the libc headers. That means the Rust translation of robotfindskitten actually includes a full definition of fn atoi(...) { ... }. Now that we've replaced the atoi call, we can delete the definition as well:

    select target 'item(atoi);' ;
    delete_items ;
    clear_marks ;
    

    Diff #52

    src/robotfindskitten.rs
    211
        pub color: libc::c_int,
    211
        pub color: libc::c_int,
    212
        pub bold: bool,
    212
        pub bold: bool,
    213
        pub character: libc::c_char,
    213
        pub character: libc::c_char,
    214
    }
    214
    }
    215
    static ver: &'static str = "1.7320508.406\u{0}";
    215
    static ver: &'static str = "1.7320508.406\u{0}";
    216
    #[inline]
    216
    217
    unsafe extern "C" fn atoi(mut __nptr: *const libc::c_char) -> libc::c_int {
    218
        return strtol(
    219
            __nptr,
    220
            0 as *mut libc::c_void as *mut *mut libc::c_char,
    221
            10i32,
    222
        ) as libc::c_int;
    223
    }
    224
    /*Be sure to change MESSAGES when you change the array, or bad things
    217
    /*Be sure to change MESSAGES when you change the array, or bad things
    225
    will happen.*/
    218
    will happen.*/
    226
    /*Also, take note that robotfindskitten.c and configure.in
    219
    /*Also, take note that robotfindskitten.c and configure.in
    227
    currently have the version number hardcoded into them, and they
    220
    currently have the version number hardcoded into them, and they
    228
    should reflect MESSAGES. */
    221
    should reflect MESSAGES. */

    We replace exit with std::process::exit:

    rewrite_expr 'exit(__e)' '::std::process::exit(__e as i32)' ;
    

    Diff #53

    src/robotfindskitten.rs
    714
        ::pancurses::endwin();
    714
        ::pancurses::endwin();
    715
        fmt_printf(format_args!(
    715
        fmt_printf(format_args!(
    716
            "{:}{:}{:}",
    716
            "{:}{:}{:}",
    717
            27i32 as u8 as char, '(' as i32 as u8 as char, 'B' as i32 as u8 as char
    717
            27i32 as u8 as char, '(' as i32 as u8 as char, 'B' as i32 as u8 as char
    718
        ));
    718
        ));
    719
        exit(0i32);
    719
        ::std::process::exit(0i32 as i32);
    720
    }
    720
    }
    721
    #[no_mangle]
    721
    #[no_mangle]
    722
    unsafe extern "C" fn initialize_arrays(S_: &mut State) {
    722
    unsafe extern "C" fn initialize_arrays(S_: &mut State) {
    723
        let mut counter: libc::c_int = 0;
    723
        let mut counter: libc::c_int = 0;
    724
        let mut counter2: libc::c_int = 0;
    724
        let mut counter2: libc::c_int = 0;

    1216
                    "Run-time parameter must be between 0 and {:}.\n",
    1216
                    "Run-time parameter must be between 0 and {:}.\n",
    1217
                    (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    1217
                    (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    1218
                        .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
    1218
                        .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
    1219
                        as libc::c_int
    1219
                        as libc::c_int
    1220
                ));
    1220
                ));
    1221
                exit(0i32);
    1221
                ::std::process::exit(0i32 as i32);
    1222
            }
    1222
            }
    1223
        }
    1223
        }
    1224
        srand(time(0 as *mut time_t) as libc::c_uint);
    1224
        srand(time(0 as *mut time_t) as libc::c_uint);
    1225
        fmt_printf(format_args!(
    1225
        fmt_printf(format_args!(
    1226
            "{:}{:}{:}",
    1226
            "{:}{:}{:}",

    For rand, no equivalent is available in the Rust standard library. Instead, we import the rand crate from crates.io:

    select target 'crate;' ;
    create_item 'extern crate rand;' inside ;
    clear_marks ;
    

    Diff #54

    src/robotfindskitten.rs
    5
        non_snake_case,
    5
        non_snake_case,
    6
        non_upper_case_globals,
    6
        non_upper_case_globals,
    7
        unused_mut
    7
        unused_mut
    8
    )]
    8
    )]
    9
    #![feature(const_raw_ptr_to_usize_cast, extern_types, libc)]
    9
    #![feature(const_raw_ptr_to_usize_cast, extern_types, libc)]
    10
    extern crate rand;
    10
    struct State {
    11
    struct State {
    11
        win: Option<::pancurses::Window>,
    12
        win: Option<::pancurses::Window>,
    12
        bogus: [screen_object; 406],
    13
        bogus: [screen_object; 406],
    13
        bogus_messages: [libc::c_int; 406],
    14
        bogus_messages: [libc::c_int; 406],
    14
        used_messages: [libc::c_int; 406],
    15
        used_messages: [libc::c_int; 406],

    robotfindskitten uses the common srand(time()) pattern to initialize the random number generator, suggesting it does not rely on the ability to control or reuse seeds. That means we can use the thread-local RNG provided by the rand crate, instead of explicitly constructing an RNG with a specific seed. So we replace rand with calls to rand::random:

    rewrite_expr 'rand()'
        '(::rand::random::<libc::c_uint>() >> 1) as libc::c_int' ;
    

    Diff #55

    src/robotfindskitten.rs
    783
    of the first few rows of the screen. But that requires making an
    783
    of the first few rows of the screen. But that requires making an
    784
    offset function and using that everywhere. So not right now. */
    784
    offset function and using that everywhere. So not right now. */
    785
    785
    786
    #[no_mangle]
    786
    #[no_mangle]
    787
    unsafe extern "C" fn initialize_robot(S_: &mut State) {
    787
    unsafe extern "C" fn initialize_robot(S_: &mut State) {
    788
        (*S_).robot.x = (::rand::random::() >> 1) as libc::c_int
    788
        (*S_).robot.x = rand() % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
    789
            % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32)
    790
            + 1i32;
    791
        (*S_).robot.y = (::rand::random::() >> 1) as libc::c_int
    789
        (*S_).robot.y = rand() % ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
    792
            % ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32)
    793
            + 3i32;
    790
        (*S_).robot.character = '#' as i32 as libc::c_char;
    794
        (*S_).robot.character = '#' as i32 as libc::c_char;
    791
        (*S_).robot.color = 0i32;
    795
        (*S_).robot.color = 0i32;
    792
        (*S_).robot.bold = 0 != 0i32;
    796
        (*S_).robot.bold = 0 != 0i32;
    793
        (*S_).screen[(*S_).robot.x as isize as usize][(*S_).robot.y as isize as usize] = 0i32;
    797
        (*S_).screen[(*S_).robot.x as isize as usize][(*S_).robot.y as isize as usize] = 0i32;
    794
    }
    798
    }
    795
    /*Global variables. Bite me, it's fun.*/
    799
    /*Global variables. Bite me, it's fun.*/
    796
    800
    797
    #[no_mangle]
    801
    #[no_mangle]
    798
    unsafe extern "C" fn initialize_kitten(S_: &mut State) {
    802
    unsafe extern "C" fn initialize_kitten(S_: &mut State) {
    799
        loop {
    803
        loop {
    804
            (*S_).kitten.x = (::rand::random::() >> 1) as libc::c_int
    800
            (*S_).kitten.x = rand() % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
    805
                % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32)
    801
            (*S_).kitten.y =
    806
                + 1i32;
    807
            (*S_).kitten.y = (::rand::random::() >> 1) as libc::c_int
    802
                rand() % ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
    808
                % ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32)
    809
                + 3i32;
    803
            if !((*S_).screen[(*S_).kitten.x as isize as usize][(*S_).kitten.y as isize as usize]
    810
            if !((*S_).screen[(*S_).kitten.x as isize as usize][(*S_).kitten.y as isize as usize]
    804
                != -1i32)
    811
                != -1i32)
    805
            {
    812
            {
    806
                break;
    813
                break;
    807
            }
    814
            }
    808
        }
    815
        }
    809
        loop {
    816
        loop {
    810
            (*S_).kitten.character =
    817
            (*S_).kitten.character = ((::rand::random::() >> 1) as libc::c_int
    811
                (rand() % (126i32 - '!' as i32 + 1i32) + '!' as i32) as libc::c_char;
    818
                % (126i32 - '!' as i32 + 1i32)
    819
                + '!' as i32) as libc::c_char;
    812
            if !(0 == validchar((*S_).kitten.character)) {
    820
            if !(0 == validchar((*S_).kitten.character)) {
    813
                break;
    821
                break;
    814
            }
    822
            }
    815
        }
    823
        }
    816
        (*S_).screen[(*S_).kitten.x as isize as usize][(*S_).kitten.y as isize as usize] = 1i32;
    824
        (*S_).screen[(*S_).kitten.x as isize as usize][(*S_).kitten.y as isize as usize] = 1i32;
    817
        (*S_).kitten.color = rand() % 6i32 + 1i32;
    825
        (*S_).kitten.color = (::rand::random::() >> 1) as libc::c_int % 6i32 + 1i32;
    818
        (*S_).kitten.bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
    826
        (*S_).kitten.bold = 0
    827
            != if 0 != (::rand::random::() >> 1) as libc::c_int % 2i32 {
    828
                1i32
    829
            } else {
    830
                0i32
    831
            };
    819
    }
    832
    }
    820
    833
    821
    /*Helper functions*/
    834
    /*Helper functions*/
    822
    #[no_mangle]
    835
    #[no_mangle]
    823
    unsafe extern "C" fn validchar(mut a: libc::c_char) -> libc::c_int {
    836
    unsafe extern "C" fn validchar(mut a: libc::c_char) -> libc::c_int {

    831
    unsafe extern "C" fn initialize_bogus(S_: &mut State) {
    844
    unsafe extern "C" fn initialize_bogus(S_: &mut State) {
    832
        let mut counter: libc::c_int = 0;
    845
        let mut counter: libc::c_int = 0;
    833
        let mut index: libc::c_int = 0;
    846
        let mut index: libc::c_int = 0;
    834
        counter = 0i32;
    847
        counter = 0i32;
    835
        while counter < (*S_).num_bogus {
    848
        while counter < (*S_).num_bogus {
    836
            (*S_).bogus[counter as usize].color = rand() % 6i32 + 1i32;
    849
            (*S_).bogus[counter as usize].color =
    837
            (*S_).bogus[counter as usize].bold = 0 != if 0 != rand() % 2i32 { 1i32 } else { 0i32 };
    850
                (::rand::random::() >> 1) as libc::c_int % 6i32 + 1i32;
    851
            (*S_).bogus[counter as usize].bold = 0
    852
                != if 0 != (::rand::random::() >> 1) as libc::c_int % 2i32 {
    853
                    1i32
    854
                } else {
    855
                    0i32
    856
                };
    838
            loop {
    857
            loop {
    839
                (*S_).bogus[counter as usize].character =
    858
                (*S_).bogus[counter as usize].character = ((::rand::random::() >> 1)
    840
                    (rand() % (126i32 - '!' as i32 + 1i32) + '!' as i32) as libc::c_char;
    859
                    as libc::c_int
    860
                    % (126i32 - '!' as i32 + 1i32)
    861
                    + '!' as i32) as libc::c_char;
    841
                if !(0 == validchar((*S_).bogus[counter as usize].character)) {
    862
                if !(0 == validchar((*S_).bogus[counter as usize].character)) {
    842
                    break;
    863
                    break;
    843
                }
    864
                }
    844
            }
    865
            }
    845
            loop {
    866
            loop {
    846
                (*S_).bogus[counter as usize].x =
    867
                (*S_).bogus[counter as usize].x = (::rand::random::() >> 1)
    868
                    as libc::c_int
    847
                    rand() % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32) + 1i32;
    869
                    % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32)
    848
                (*S_).bogus[counter as usize].y =
    870
                    + 1i32;
    871
                (*S_).bogus[counter as usize].y = (::rand::random::() >> 1)
    872
                    as libc::c_int
    849
                    rand() % ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32) + 3i32;
    873
                    % ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32)
    874
                    + 3i32;
    850
                if !((*S_).screen[(*S_).bogus[counter as usize].x as isize as usize]
    875
                if !((*S_).screen[(*S_).bogus[counter as usize].x as isize as usize]
    851
                    [(*S_).bogus[counter as usize].y as isize as usize]
    876
                    [(*S_).bogus[counter as usize].y as isize as usize]
    852
                    != -1i32)
    877
                    != -1i32)
    853
                {
    878
                {
    854
                    break;
    879
                    break;
    855
                }
    880
                }
    856
            }
    881
            }
    857
            (*S_).screen[(*S_).bogus[counter as usize].x as isize as usize]
    882
            (*S_).screen[(*S_).bogus[counter as usize].x as isize as usize]
    858
                [(*S_).bogus[counter as usize].y as isize as usize] = counter + 2i32;
    883
                [(*S_).bogus[counter as usize].y as isize as usize] = counter + 2i32;
    859
            loop {
    884
            loop {
    860
                index = (rand() as libc::c_ulong).wrapping_rem(
    885
                index = ((::rand::random::() >> 1) as libc::c_int as libc::c_ulong)
    886
                    .wrapping_rem(
    861
                    (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    887
                        (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    862
                        .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong),
    888
                            .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong),
    863
                ) as libc::c_int;
    889
                    ) as libc::c_int;
    864
                if !((*S_).used_messages[index as usize] != 0i32) {
    890
                if !((*S_).used_messages[index as usize] != 0i32) {
    865
                    break;
    891
                    break;
    866
                }
    892
                }
    867
            }
    893
            }
    868
            (*S_).bogus_messages[counter as usize] = index;
    894
            (*S_).bogus_messages[counter as usize] = index;

    And we delete srand calls entirely, relying on the rand crate's automatic initialization of the thread-local RNG:

    rewrite_expr 'srand(__e)' '()' ;
    

    Diff #56

    src/robotfindskitten.rs
    1246
                        as libc::c_int
    1246
                        as libc::c_int
    1247
                ));
    1247
                ));
    1248
                ::std::process::exit(0i32 as i32);
    1248
                ::std::process::exit(0i32 as i32);
    1249
            }
    1249
            }
    1250
        }
    1250
        }
    1251
        srand(time(0 as *mut time_t) as libc::c_uint);
    1251
        ();
    1252
        fmt_printf(format_args!(
    1252
        fmt_printf(format_args!(
    1253
            "{:}{:}{:}",
    1253
            "{:}{:}{:}",
    1254
            27i32 as u8 as char, '(' as i32 as u8 as char, 'U' as i32 as u8 as char
    1254
            27i32 as u8 as char, '(' as i32 as u8 as char, 'U' as i32 as u8 as char
    1255
        ));
    1255
        ));
    1256
        initialize_ncurses(&mut S);
    1256
        initialize_ncurses(&mut S);

    At this point, the only remaining FFI call is to signal. robotfindskitten sets up a SIGINT handler to ensure that ncurses (now pancurses) is shut down properly and the terminal is returned to a normal state when the user terminates the program with ^C. Unfortunately, there is no general way to make signal handling safe: to achieve memory safety, signal handling functions must obey a number of special restrictions above and beyond Rust's normal notions of safety, and these properties cannot be checked by the Rust compiler.

    We therefore leave the call to signal as unsafe code. Since this will be the only unsafe operation in the program once we finish refactoring, we wrap it in its own unsafe block:

    rewrite_expr 'signal(__e, __f)' 'unsafe { signal(__e, __f) }' ;
    

    Diff #57

    src/robotfindskitten.rs
    633
     *Function definitions
    633
     *Function definitions
    634
     */
    634
     */
    635
    /*Initialization and setup functions*/
    635
    /*Initialization and setup functions*/
    636
    #[no_mangle]
    636
    #[no_mangle]
    637
    unsafe extern "C" fn initialize_ncurses(S_: &mut State) {
    637
    unsafe extern "C" fn initialize_ncurses(S_: &mut State) {
    638
        signal(2i32, Some(finish));
    638
        unsafe { signal(2i32, Some(finish)) };
    639
        (*S_).win = Some(::pancurses::initscr());
    639
        (*S_).win = Some(::pancurses::initscr());
    640
        (*S_).win.as_ref().unwrap().keypad(0 != 1i32);
    640
        (*S_).win.as_ref().unwrap().keypad(0 != 1i32);
    641
        ::pancurses::nonl();
    641
        ::pancurses::nonl();
    642
        0;
    642
        0;
    643
        ::pancurses::noecho();
    643
        ::pancurses::noecho();

    We've now covered all of the libc functions used by robotfindskitten, and replaced nearly all of them with safe code.

    commit
    

    Function argument types

    Two functions in robotfindskitten accept raw pointers: message takes a pointer to a string to display on the screen, and main_0 takes an array of string pointers argv containing the program's command line arguments. To make these functions safe, we must replace their raw pointer arguments with safe equivalents.

    message

    We begin with message because it is simpler. This function takes a single argument of type *mut c_char, which we want to replace with &str:

    select target
        'item(message); child(arg); child(match_ty(*mut libc::c_char));' ;
    rewrite_ty 'marked!(*mut libc::c_char)' '&str' ;
    delete_marks target ;
    

    Diff #59

    src/robotfindskitten.rs
    1063
        );
    1063
        );
    1064
        (*S_).win.as_ref().unwrap().refresh();
    1064
        (*S_).win.as_ref().unwrap().refresh();
    1065
        finish(0i32);
    1065
        finish(0i32);
    1066
    }
    1066
    }
    1067
    #[no_mangle]
    1067
    #[no_mangle]
    1068
    unsafe extern "C" fn message(mut message_0: *mut libc::c_char, S_: &mut State) {
    1068
    unsafe extern "C" fn message(mut message_0: &str, S_: &mut State) {
    1069
        (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
    1069
        (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
    1070
        (*S_).win.as_ref().unwrap().clrtoeol();
    1070
        (*S_).win.as_ref().unwrap().clrtoeol();
    1071
        fmt_mvprintw(
    1071
        fmt_mvprintw(
    1072
            1i32,
    1072
            1i32,
    1073
            0i32,
    1073
            0i32,

    Of course, simply changing the type annotation is not sufficient. Like when we retyped the ver and messages constants, this change has introduced two kinds of type errors: callers of message still pass *mut c_char where &str is now expected, and the body of message still uses the message_0: &str argument in contexts that require a *mut c_char. We fix these using type_fix_rules:

    type_fix_rules
        '*, *mut __t, &str =>
            ::std::ffi::CStr::from_ptr(__old).to_str().unwrap()'
        '*, &str, *const __t =>
            ::std::ffi::CString::new(__old.to_owned()).unwrap().as_ptr()'
        ;
    

    Diff #60

    src/robotfindskitten.rs
    1056
                old_y = (*S_).robot.y
    1056
                old_y = (*S_).robot.y
    1057
            }
    1057
            }
    1058
            input = ::encode_input((*S_).win.as_ref().unwrap().getch())
    1058
            input = ::encode_input((*S_).win.as_ref().unwrap().getch())
    1059
        }
    1059
        }
    1060
        message(
    1060
        message(
    1061
            ::std::ffi::CStr::from_ptr(
    1061
            b"Bye!\x00" as *const u8 as *const libc::c_char as *mut libc::c_char,
    1062
                b"Bye!\x00" as *const u8 as *const libc::c_char as *mut libc::c_char,
    1063
            )
    1064
            .to_str()
    1065
            .unwrap(),
    1062
            S_,
    1066
            S_,
    1063
        );
    1067
        );
    1064
        (*S_).win.as_ref().unwrap().refresh();
    1068
        (*S_).win.as_ref().unwrap().refresh();
    1065
        finish(0i32);
    1069
        finish(0i32);
    1066
    }
    1070
    }

    1073
            0i32,
    1077
            0i32,
    1074
            format_args!(
    1078
            format_args!(
    1075
                "{:.*}",
    1079
                "{:.*}",
    1076
                (*S_).win.as_ref().unwrap().get_max_x() as usize,
    1080
                (*S_).win.as_ref().unwrap().get_max_x() as usize,
    1077
                unsafe {
    1081
                unsafe {
    1078
                    ::std::ffi::CStr::from_ptr(message_0 as *const libc::c_char)
    1082
                    ::std::ffi::CStr::from_ptr(
    1079
                        .to_str()
    1083
                        ::std::ffi::CString::new(message_0.to_owned())
    1080
                        .unwrap()
    1084
                            .unwrap()
    1085
                            .as_ptr() as *const libc::c_char,
    1086
                    )
    1087
                    .to_str()
    1088
                    .unwrap()
    1081
                }
    1089
                }
    1082
            ),
    1090
            ),
    1083
            S_,
    1091
            S_,
    1084
        );
    1092
        );
    1085
        (*S_).win.as_ref().unwrap().mv((*S_).robot.y, (*S_).robot.x);
    1093
        (*S_).win.as_ref().unwrap().mv((*S_).robot.y, (*S_).robot.x);

    1114
            260 | 104 | 72 | 2 => check_x -= 1,
    1122
            260 | 104 | 72 | 2 => check_x -= 1,
    1115
            261 | 108 | 76 | 6 => check_x += 1,
    1123
            261 | 108 | 76 | 6 => check_x += 1,
    1116
            0 => {}
    1124
            0 => {}
    1117
            _ => {
    1125
            _ => {
    1118
                message(
    1126
                message(
    1127
                    ::std::ffi::CStr::from_ptr(
    1119
                    b"Invalid input: Use direction keys or Esc.\x00" as *const u8 as *const libc::c_char
    1128
                        b"Invalid input: Use direction keys or Esc.\x00" as *const u8
    1120
                        as *mut libc::c_char,
    1129
                            as *const libc::c_char as *mut libc::c_char,
    1130
                    )
    1131
                    .to_str()
    1132
                    .unwrap(),
    1121
                    S_,
    1133
                    S_,
    1122
                );
    1134
                );
    1123
                return;
    1135
                return;
    1124
            }
    1136
            }
    1125
        }
    1137
        }

    1140
                    (*S_).win.as_ref().unwrap().clrtoeol();
    1152
                    (*S_).win.as_ref().unwrap().clrtoeol();
    1141
                    play_animation(input, S_);
    1153
                    play_animation(input, S_);
    1142
                }
    1154
                }
    1143
                _ => {
    1155
                _ => {
    1144
                    message(
    1156
                    message(
    1157
                        ::std::ffi::CStr::from_ptr(
    1145
                        messages[(*S_).bogus_messages[((*S_).screen[check_x as isize as usize]
    1158
                            messages[(*S_).bogus_messages[((*S_).screen[check_x as isize as usize]
    1146
                            [check_y as isize as usize]
    1159
                                [check_y as isize as usize]
    1147
                            - 2i32) as usize] as usize]
    1160
                                - 2i32) as usize] as usize]
    1148
                            .as_ptr() as *mut i8,
    1161
                                .as_ptr() as *mut i8,
    1162
                        )
    1163
                        .to_str()
    1164
                        .unwrap(),
    1149
                        S_,
    1165
                        S_,
    1150
                    );
    1166
                    );
    1151
                }
    1167
                }
    1152
            }
    1168
            }
    1153
            return;
    1169
            return;

    The first rule handles callers of message, using CStr methods to convert their *mut c_char raw pointers into safe &str references. The second handles errors in the body of message, using CString to convert &strs back into *const c_char. Note we must use CString instead of CStr in the second rule because an allocation is required: a &str is not guaranteed to end with a null terminator, so CString must copy it into a larger buffer and add the null terminator to produce a valid *const c_char string pointer. Since the CString is temporary, it will be deallocated at the end of the containing expression, but this is good enough for the code we encounter inside of message. More complex string manipulation, however, would likely require a different refactoring approach.

    main_0

    The Rust function main_0 is the translation of the C main function of robotfindskitten. The Rust main is a c2rust-generated wrapper that handles the differences between C's main signature and Rust's before invoking main_0.

    As in the message case, we wish to replace the unsafe pointer types in main_0's argument list with safe equivalents. However, in this case our choice of safe reference type is more constrained. main_0 calls argv.offset to access the individual command-line arguments, so we must use CArray (which supports such access patterns) for the outer pointer. For the inner pointer, we use Option<&CStr>: CStr supports the conversions we will need to perform in main and main_0, and Option<&CStr> can be safely zero-initialized, which is required by CArray.

    We begin, as with message, by rewriting the argument type:

    select target
        'item(main_0); child(arg && name("argv")); child(ty);' ;
    rewrite_ty 'marked!(*mut *mut libc::c_char)'
        '::c2rust_runtime::CArray<Option<&::std::ffi::CStr>>' ;
    delete_marks target ;
    

    Diff #61

    src/robotfindskitten.rs
    1210
            -1i32 as usize,
    1210
            -1i32 as usize,
    1211
        );
    1211
        );
    1212
        (*S_).win.as_ref().unwrap().refresh();
    1212
        (*S_).win.as_ref().unwrap().refresh();
    1213
        finish(0i32);
    1213
        finish(0i32);
    1214
    }
    1214
    }
    1215
    unsafe fn main_0(mut argc: libc::c_int, mut argv: *mut *mut libc::c_char) -> libc::c_int {
    1215
    unsafe fn main_0(
    1216
        mut argc: libc::c_int,
    1217
        mut argv: ::c2rust_runtime::CArray<Option<&::std::ffi::CStr>>,
    1218
    ) -> libc::c_int {
    1216
        let mut S: State = State {
    1219
        let mut S: State = State {
    1217
            win: None,
    1220
            win: None,
    1218
            bogus: [screen_object {
    1221
            bogus: [screen_object {
    1219
                x: 0,
    1222
                x: 0,
    1220
                y: 0,
    1223
                y: 0,

    Next, we fix type errors in main, which is the only caller of main_0. Since c2rust always generates the same main wrapper function, rather than refactor it, we can simply replace it entirely with a new version that is compatible with main_0's new signature:

    select target 'item(main);' ;
    create_item '
        fn main() {
            // Collect argv into a vector.
            let mut args_owned: Vec<::std::ffi::CString> = Vec::new();
            for arg in ::std::env::args() {
                args_owned.push(::std::ffi::CString::new(arg).unwrap());
            }
    
            // Now that the length is known, we can build a CArray.
            let mut args: ::c2rust_runtime::CArray<Option<&::std::ffi::CStr>> =
                ::c2rust_runtime::CArray::alloc(args_owned.len() + 1);
            for i in 0 .. args_owned.len() {
                args[i] = Some(&args_owned[i]);
            }
            // The last element of `args` remains `None`.
    
            unsafe {
                ::std::process::exit(main_0(
                    (args.len() - 1) as libc::c_int,
                    args) as i32);
            }
        }
    ' after ;
    delete_items ;
    clear_marks ;
    

    Diff #62

    src/robotfindskitten.rs
    1280
        instructions(&mut S);
    1280
        instructions(&mut S);
    1281
        initialize_screen(&mut S);
    1281
        initialize_screen(&mut S);
    1282
        play_game(&mut S);
    1282
        play_game(&mut S);
    1283
        return 0;
    1283
        return 0;
    1284
    }
    1284
    }
    1285
    pub fn main() {
    1285
    fn main() {
    1286
        let mut args: Vec<*mut libc::c_char> = Vec::new();
    1286
        // Collect argv into a vector.
    1287
        let mut args_owned: Vec<::std::ffi::CString> = Vec::new();
    1287
        for arg in ::std::env::args() {
    1288
        for arg in ::std::env::args() {
    1288
            args.push(
    1289
            args_owned.push(::std::ffi::CString::new(arg).unwrap());
    1289
                ::std::ffi::CString::new(arg)
    1290
                    .expect("Failed to convert argument into CString.")
    1291
                    .into_raw(),
    1292
            );
    1293
        }
    1290
        }
    1294
        args.push(::std::ptr::null_mut());
    1291
    1292
        // Now that the length is known, we can build a CArray.
    1293
        let mut args: ::c2rust_runtime::CArray<Option<&::std::ffi::CStr>> =
    1294
            ::c2rust_runtime::CArray::alloc(args_owned.len() + 1);
    1295
        for i in 0..args_owned.len() {
    1296
            args[i] = Some(&args_owned[i]);
    1297
        }
    1298
        // The last element of `args` remains `None`.
    1299
    1295
        unsafe {
    1300
        unsafe {
    1296
            ::std::process::exit(main_0(
    1301
            ::std::process::exit(main_0((args.len() - 1) as libc::c_int, args) as i32);
    1297
                (args.len() - 1) as libc::c_int,
    1298
                args.as_mut_ptr() as *mut *mut libc::c_char,
    1299
            ) as i32)
    1300
        }
    1302
        }
    1301
    }
    1303
    }

    Now to fix errors in main_0 itself. We changed both the inner and outer pointer types of argv, so there are two kinds of errors to clean up.

    For the outer pointer, where we changed *mut T to CArray<T>, the problem we see is that argv.offset(...) returns &CArrayOffset<T>, not *mut T, and &CArrayOffset<T> requires two derefs to obtain a T (&CArrayOffset<T> derefs to CArrayOffset<T>, which derefs to T) instead of just one. We handle this with type_fix_rules, looking for cases where a single deref resulted in CArrayOffset<T> but some other type was expected, and adding the second deref:

    type_fix_rules
        '*, ::c2rust_runtime::array::CArrayOffset<__t>, __u => *__old'
        ;
    

    Diff #63

    src/robotfindskitten.rs
    1246
        };
    1246
        };
    1247
        if argc == 1i32 {
    1247
        if argc == 1i32 {
    1248
            S.num_bogus = 20i32
    1248
            S.num_bogus = 20i32
    1249
        } else {
    1249
        } else {
    1250
            S.num_bogus = as ::std::str::FromStr>::from_str(
    1250
            S.num_bogus = as ::std::str::FromStr>::from_str(
    1251
                ::std::ffi::CStr::from_ptr(*argv.offset(1isize))
    1251
                ::std::ffi::CStr::from_ptr(**argv.offset(1isize))
    1252
                    .to_str()
    1252
                    .to_str()
    1253
                    .unwrap(),
    1253
                    .unwrap(),
    1254
            )
    1254
            )
    1255
            .unwrap();
    1255
            .unwrap();
    1256
            if S.num_bogus < 0i32
    1256
            if S.num_bogus < 0i32

    For the inner pointer type, which we changed from *mut c_char to Option<&CStr>, we need only insert a simple conversion anywhere the new type is used but *mut c_char is expected:

    type_fix_rules
        '*, ::std::option::Option<&::std::ffi::CStr>, *const i8 =>
            opt_c_str_to_ptr(__old)'
        ;
    

    Diff #64

    src/robotfindskitten.rs
    1246
        };
    1246
        };
    1247
        if argc == 1i32 {
    1247
        if argc == 1i32 {
    1248
            S.num_bogus = 20i32
    1248
            S.num_bogus = 20i32
    1249
        } else {
    1249
        } else {
    1250
            S.num_bogus = as ::std::str::FromStr>::from_str(
    1250
            S.num_bogus = as ::std::str::FromStr>::from_str(
    1251
                ::std::ffi::CStr::from_ptr(**argv.offset(1isize))
    1251
                ::std::ffi::CStr::from_ptr(opt_c_str_to_ptr(**argv.offset(1isize)))
    1252
                    .to_str()
    1252
                    .to_str()
    1253
                    .unwrap(),
    1253
                    .unwrap(),
    1254
            )
    1254
            )
    1255
            .unwrap();
    1255
            .unwrap();
    1256
            if S.num_bogus < 0i32
    1256
            if S.num_bogus < 0i32

    The only quirk here is that we wrap up the conversion in a helper function, making it easier to recognize in the later refactoring step where we clean up redundant string conversions. Of course, now we must define that helper function:

    select target 'item(main);' ;
    create_item '
        fn opt_c_str_to_ptr(x: Option<&::std::ffi::CStr>) -> *const libc::c_char {
            match x {
                None => ::std::ptr::null(),
                Some(x) => x.as_ptr(),
            }
        }
    ' after ;
    clear_marks ;
    

    Diff #65

    src/robotfindskitten.rs
    1299
    1299
    1300
        unsafe {
    1300
        unsafe {
    1301
            ::std::process::exit(main_0((args.len() - 1) as libc::c_int, args) as i32);
    1301
            ::std::process::exit(main_0((args.len() - 1) as libc::c_int, args) as i32);
    1302
        }
    1302
        }
    1303
    }
    1303
    }
    1304
    fn opt_c_str_to_ptr(x: Option<&::std::ffi::CStr>) -> *const libc::c_char {
    1305
        match x {
    1306
            None => ::std::ptr::null(),
    1307
            Some(x) => x.as_ptr(),
    1308
        }
    1309
    }

    And with that, we are done. All raw pointer arguments in robotfindskitten have now been replaced with safe equivalents.

    commit
    

    String conversion cleanup

    A number of the previous refactoring steps involved changing the type of some variable from a raw C string (*const c_char) to a safe Rust string (&str), inserting conversions between the two forms everywhere the variable was initialized or used. But now that we have finished transitioning the entire crate to Rust strings, many of those conversions have become redundant. Essentially, we began with code like this:

    fn f(s1: *const c_char) { ... }
    
    fn g(s2: *const c_char) {
        ... f(s2) ...
    }
    

    By incrementally refactoring C strings into Rust string, we first transitioned to code like this:

    fn f(s1: &str) { ... }
    
    fn g(s2: *const c_char) {
        ... f(c_str_to_rust(s2)) ...
    }
    

    And then to code like this:

    fn f(s1: &str) { ... }
    
    fn g(s2: &str) {
        ... f(rust_str_to_c(c_str_to_rust(s2))) ...
    }
    

    But rust_str_to_c(c_str_to_rust(s2)) is the same as just s2 - the two conversions are redundant and can be removed:

    fn f(s1: &str) { ... }
    
    fn g(s2: &str) {
        ... f(s2) ...
    }
    

    This doesn't merely affect readability - the actual conversion operations represented by c_str_to_rust are unsafe, so we must remove them to complete our refactoring of robotfindskitten.

    The actual refactoring process we apply to robotfindskitten mostly consists of removing specific types of redundant conversions with rewrite_expr. The patterns we use here are general, taking advantage of overlap between different conversion cases rather than hardcoding a rewrite for each distinct conversion in robotfindskitten.

    To begin with, converting CString to *const c_char to CStr can be replaced with a no-op (CString derefs to CStr, so it can be used almost anywhere a CStr is required):

    rewrite_expr
        '::std::ffi::CStr::from_ptr(
            cast!(typed!(__e, ::std::ffi::CString).as_ptr()))'
        '__e' ;
    

    Diff #67

    src/robotfindskitten.rs
    1077
            0i32,
    1077
            0i32,
    1078
            format_args!(
    1078
            format_args!(
    1079
                "{:.*}",
    1079
                "{:.*}",
    1080
                (*S_).win.as_ref().unwrap().get_max_x() as usize,
    1080
                (*S_).win.as_ref().unwrap().get_max_x() as usize,
    1081
                unsafe {
    1081
                unsafe {
    1082
                    ::std::ffi::CStr::from_ptr(
    1083
                        ::std::ffi::CString::new(message_0.to_owned())
    1082
                    ::std::ffi::CString::new(message_0.to_owned())
    1084
                            .unwrap()
    1083
                        .unwrap()
    1085
                            .as_ptr() as *const libc::c_char,
    1086
                    )
    1087
                    .to_str()
    1084
                        .to_str()
    1088
                    .unwrap()
    1085
                        .unwrap()
    1089
                }
    1086
                }
    1090
            ),
    1087
            ),
    1091
            S_,
    1088
            S_,
    1092
        );
    1089
        );
    1093
        (*S_).win.as_ref().unwrap().mv((*S_).robot.y, (*S_).robot.x);
    1090
        (*S_).win.as_ref().unwrap().mv((*S_).robot.y, (*S_).robot.x);

    Converting String to CString to Option<&str> is not strictly a no-op, but can still be simplified:

    rewrite_expr
        '::std::ffi::CString::new(__e).unwrap().to_str()'
        'Some(&__e)' ;
    

    Diff #68

    src/robotfindskitten.rs
    1076
            1i32,
    1076
            1i32,
    1077
            0i32,
    1077
            0i32,
    1078
            format_args!(
    1078
            format_args!(
    1079
                "{:.*}",
    1079
                "{:.*}",
    1080
                (*S_).win.as_ref().unwrap().get_max_x() as usize,
    1080
                (*S_).win.as_ref().unwrap().get_max_x() as usize,
    1081
                unsafe {
    1081
                unsafe { Some(&message_0.to_owned()).unwrap() }
    1082
                    ::std::ffi::CString::new(message_0.to_owned())
    1083
                        .unwrap()
    1084
                        .to_str()
    1085
                        .unwrap()
    1086
                }
    1087
            ),
    1082
            ),
    1088
            S_,
    1083
            S_,
    1089
        );
    1084
        );
    1090
        (*S_).win.as_ref().unwrap().mv((*S_).robot.y, (*S_).robot.x);
    1085
        (*S_).win.as_ref().unwrap().mv((*S_).robot.y, (*S_).robot.x);
    1091
        (*S_).win.as_ref().unwrap().refresh();
    1086
        (*S_).win.as_ref().unwrap().refresh();

    In some places, the code actually converts &str to *const c_char directly, rather than using CString, and then converts *const c_char to CStr to &str. This is memory-safe only when the &str already includes a null terminator, and the CStr to str conversion will trim it off. We rewrite the code to simply trim off the null terminator directly, avoiding these complex (and unsafe) conversions:

    rewrite_expr
        '::std::ffi::CStr::from_ptr(
            cast!(typed!(__e, &str).as_ptr())).to_str()'
        "Some(__e.trim_end_matches('\0'))" ;
    

    Diff #69

    src/robotfindskitten.rs
    902
        let mut counter: libc::c_int = 0;
    902
        let mut counter: libc::c_int = 0;
    903
        fmt_mvprintw(
    903
        fmt_mvprintw(
    904
            0i32,
    904
            0i32,
    905
            0i32,
    905
            0i32,
    906
            format_args!("robotfindskitten v{:}\n\n", unsafe {
    906
            format_args!("robotfindskitten v{:}\n\n", unsafe {
    907
                ::std::ffi::CStr::from_ptr(ver.as_ptr() as *const libc::c_char)
    907
                Some(ver.trim_end_matches('\u{0}')).unwrap()
    908
                    .to_str()
    909
                    .unwrap()
    910
            }),
    908
            }),
    911
            S_,
    909
            S_,
    912
        );
    910
        );
    913
        counter = 0i32;
    911
        counter = 0i32;
    914
        while counter <= (*S_).win.as_ref().unwrap().get_max_x() - 1i32 {
    912
        while counter <= (*S_).win.as_ref().unwrap().get_max_x() - 1i32 {

    985
        let mut dummy: libc::c_char = 0;
    983
        let mut dummy: libc::c_char = 0;
    986
        fmt_mvprintw(
    984
        fmt_mvprintw(
    987
            0i32,
    985
            0i32,
    988
            0i32,
    986
            0i32,
    989
            format_args!("robotfindskitten v{:}\n", unsafe {
    987
            format_args!("robotfindskitten v{:}\n", unsafe {
    990
                ::std::ffi::CStr::from_ptr(ver.as_ptr() as *const libc::c_char)
    988
                Some(ver.trim_end_matches('\u{0}')).unwrap()
    991
                    .to_str()
    992
                    .unwrap()
    993
            }),
    989
            }),
    994
            S_,
    990
            S_,
    995
        );
    991
        );
    996
        fmt_printw(
    992
        fmt_printw(
    997
            format_args!("By the illustrious Leonard Richardson (C) 1997, 2000\n"),
    993
            format_args!("By the illustrious Leonard Richardson (C) 1997, 2000\n"),

    1144
                    (*S_).win.as_ref().unwrap().clrtoeol();
    1140
                    (*S_).win.as_ref().unwrap().clrtoeol();
    1145
                    play_animation(input, S_);
    1141
                    play_animation(input, S_);
    1146
                }
    1142
                }
    1147
                _ => {
    1143
                _ => {
    1148
                    message(
    1144
                    message(
    1149
                        ::std::ffi::CStr::from_ptr(
    1145
                        Some(
    1150
                            messages[(*S_).bogus_messages[((*S_).screen[check_x as isize as usize]
    1146
                            messages[(*S_).bogus_messages[((*S_).screen[check_x as isize as usize]
    1151
                                [check_y as isize as usize]
    1147
                                [check_y as isize as usize]
    1152
                                - 2i32) as usize] as usize]
    1148
                                - 2i32) as usize] as usize]
    1153
                                .as_ptr() as *mut i8,
    1149
                                .trim_end_matches('\u{0}'),
    1154
                        )
    1150
                        )
    1155
                        .to_str()
    1156
                        .unwrap(),
    1151
                        .unwrap(),
    1157
                        S_,
    1152
                        S_,
    1158
                    );
    1153
                    );
    1159
                }
    1154
                }
    1160
            }
    1155
            }

    For code in main_0 using the opt_c_str_to_ptr helper function we introduced earlier, the Option<&CStr> to &CStr conversion can be replaced with a simple unwrap():

    rewrite_expr
        '::std::ffi::CStr::from_ptr(cast!(opt_c_str_to_ptr(__e)))'
        '__e.unwrap()' ;
    

    Diff #70

    src/robotfindskitten.rs
    1233
        };
    1233
        };
    1234
        if argc == 1i32 {
    1234
        if argc == 1i32 {
    1235
            S.num_bogus = 20i32
    1235
            S.num_bogus = 20i32
    1236
        } else {
    1236
        } else {
    1237
            S.num_bogus = as ::std::str::FromStr>::from_str(
    1237
            S.num_bogus = as ::std::str::FromStr>::from_str(
    1238
                ::std::ffi::CStr::from_ptr(opt_c_str_to_ptr(**argv.offset(1isize)))
    1238
                (**argv.offset(1isize)).unwrap().to_str().unwrap(),
    1239
                    .to_str()
    1240
                    .unwrap(),
    1241
            )
    1239
            )
    1242
            .unwrap();
    1240
            .unwrap();
    1243
            if S.num_bogus < 0i32
    1241
            if S.num_bogus < 0i32
    1244
                || S.num_bogus as libc::c_ulong
    1242
                || S.num_bogus as libc::c_ulong
    1245
                    > (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    1243
                    > (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)

    Conversions of bytestring literals (b"...", whose type is &[u8; _]) to *const c_char to CStr to str can be simplified down to a direct conversion from &[u8; _] to &str, plus removal of the null terminator:

    rewrite_expr
        '::std::ffi::CStr::from_ptr(
            cast!(typed!(__e, &[u8; __f]))).to_str()'
        "Some(::std::str::from_utf8(__e).unwrap().trim_end_matches('\0'))" ;
    

    Diff #71

    src/robotfindskitten.rs
    1052
                old_y = (*S_).robot.y
    1052
                old_y = (*S_).robot.y
    1053
            }
    1053
            }
    1054
            input = ::encode_input((*S_).win.as_ref().unwrap().getch())
    1054
            input = ::encode_input((*S_).win.as_ref().unwrap().getch())
    1055
        }
    1055
        }
    1056
        message(
    1056
        message(
    1057
            ::std::ffi::CStr::from_ptr(
    1057
            Some(
    1058
                b"Bye!\x00" as *const u8 as *const libc::c_char as *mut libc::c_char,
    1058
                ::std::str::from_utf8(b"Bye!\x00")
    1059
                    .unwrap()
    1060
                    .trim_end_matches('\u{0}'),
    1059
            )
    1061
            )
    1060
            .to_str()
    1061
            .unwrap(),
    1062
            .unwrap(),
    1062
            S_,
    1063
            S_,
    1063
        );
    1064
        );
    1064
        (*S_).win.as_ref().unwrap().refresh();
    1065
        (*S_).win.as_ref().unwrap().refresh();
    1065
        finish(0i32);
    1066
        finish(0i32);

    1110
            260 | 104 | 72 | 2 => check_x -= 1,
    1111
            260 | 104 | 72 | 2 => check_x -= 1,
    1111
            261 | 108 | 76 | 6 => check_x += 1,
    1112
            261 | 108 | 76 | 6 => check_x += 1,
    1112
            0 => {}
    1113
            0 => {}
    1113
            _ => {
    1114
            _ => {
    1114
                message(
    1115
                message(
    1115
                    ::std::ffi::CStr::from_ptr(
    1116
                    Some(
    1116
                        b"Invalid input: Use direction keys or Esc.\x00" as *const u8
    1117
                        ::std::str::from_utf8(b"Invalid input: Use direction keys or Esc.\x00")
    1117
                            as *const libc::c_char as *mut libc::c_char,
    1118
                            .unwrap()
    1119
                            .trim_end_matches('\u{0}'),
    1118
                    )
    1120
                    )
    1119
                    .to_str()
    1120
                    .unwrap(),
    1121
                    .unwrap(),
    1121
                    S_,
    1122
                    S_,
    1122
                );
    1123
                );
    1123
                return;
    1124
                return;
    1124
            }
    1125
            }

    This removes the unsafety, but with a little more work, we can further improve readability. First, we convert the byte strings to ordinary string literals (b"..." to "..."):

    select target
        'crate; desc(match_expr(::std::str::from_utf8(__e))); desc(expr);' ;
    bytestr_to_str ;
    clear_marks ;
    

    Diff #72

    src/robotfindskitten.rs
    1053
            }
    1053
            }
    1054
            input = ::encode_input((*S_).win.as_ref().unwrap().getch())
    1054
            input = ::encode_input((*S_).win.as_ref().unwrap().getch())
    1055
        }
    1055
        }
    1056
        message(
    1056
        message(
    1057
            Some(
    1057
            Some(
    1058
                ::std::str::from_utf8(b"Bye!\x00")
    1058
                ::std::str::from_utf8("Bye!\u{0}")
    1059
                    .unwrap()
    1059
                    .unwrap()
    1060
                    .trim_end_matches('\u{0}'),
    1060
                    .trim_end_matches('\u{0}'),
    1061
            )
    1061
            )
    1062
            .unwrap(),
    1062
            .unwrap(),
    1063
            S_,
    1063
            S_,

    1112
            261 | 108 | 76 | 6 => check_x += 1,
    1112
            261 | 108 | 76 | 6 => check_x += 1,
    1113
            0 => {}
    1113
            0 => {}
    1114
            _ => {
    1114
            _ => {
    1115
                message(
    1115
                message(
    1116
                    Some(
    1116
                    Some(
    1117
                        ::std::str::from_utf8(b"Invalid input: Use direction keys or Esc.\x00")
    1117
                        ::std::str::from_utf8("Invalid input: Use direction keys or Esc.\u{0}")
    1118
                            .unwrap()
    1118
                            .unwrap()
    1119
                            .trim_end_matches('\u{0}'),
    1119
                            .trim_end_matches('\u{0}'),
    1120
                    )
    1120
                    )
    1121
                    .unwrap(),
    1121
                    .unwrap(),
    1122
                    S_,
    1122
                    S_,

    1190
            ));
    1190
            ));
    1191
            counter -= 1
    1191
            counter -= 1
    1192
        }
    1192
        }
    1193
        (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
    1193
        (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
    1194
        (*S_).win.as_ref().unwrap().addnstr(
    1194
        (*S_).win.as_ref().unwrap().addnstr(
    1195
            ::std::str::from_utf8(b"You found kitten! Way to go, robot!\x00")
    1195
            ::std::str::from_utf8("You found kitten! Way to go, robot!\u{0}")
    1196
                .unwrap()
    1196
                .unwrap()
    1197
                .trim_end_matches('\u{0}'),
    1197
                .trim_end_matches('\u{0}'),
    1198
            -1i32 as usize,
    1198
            -1i32 as usize,
    1199
        );
    1199
        );
    1200
        (*S_).win.as_ref().unwrap().refresh();
    1200
        (*S_).win.as_ref().unwrap().refresh();

    This introduces type errors, as the type of the literal has changed from &str to &[u8]. We fix these by inserting calls to str::as_bytes:

    type_fix_rules '*, &str, &[u8] => __old.as_bytes()' ;
    

    Diff #73

    src/robotfindskitten.rs
    1053
            }
    1053
            }
    1054
            input = ::encode_input((*S_).win.as_ref().unwrap().getch())
    1054
            input = ::encode_input((*S_).win.as_ref().unwrap().getch())
    1055
        }
    1055
        }
    1056
        message(
    1056
        message(
    1057
            Some(
    1057
            Some(
    1058
                ::std::str::from_utf8("Bye!\u{0}")
    1058
                ::std::str::from_utf8("Bye!\u{0}".as_bytes())
    1059
                    .unwrap()
    1059
                    .unwrap()
    1060
                    .trim_end_matches('\u{0}'),
    1060
                    .trim_end_matches('\u{0}'),
    1061
            )
    1061
            )
    1062
            .unwrap(),
    1062
            .unwrap(),
    1063
            S_,
    1063
            S_,

    1112
            261 | 108 | 76 | 6 => check_x += 1,
    1112
            261 | 108 | 76 | 6 => check_x += 1,
    1113
            0 => {}
    1113
            0 => {}
    1114
            _ => {
    1114
            _ => {
    1115
                message(
    1115
                message(
    1116
                    Some(
    1116
                    Some(
    1117
                        ::std::str::from_utf8(
    1117
                        ::std::str::from_utf8("Invalid input: Use direction keys or Esc.\u{0}")
    1118
                            "Invalid input: Use direction keys or Esc.\u{0}".as_bytes(),
    1119
                        )
    1118
                            .unwrap()
    1120
                        .unwrap()
    1119
                            .trim_end_matches('\u{0}'),
    1121
                        .trim_end_matches('\u{0}'),
    1120
                    )
    1122
                    )
    1121
                    .unwrap(),
    1123
                    .unwrap(),
    1122
                    S_,
    1124
                    S_,
    1123
                );
    1125
                );
    1124
                return;
    1126
                return;

    1190
            ));
    1192
            ));
    1191
            counter -= 1
    1193
            counter -= 1
    1192
        }
    1194
        }
    1193
        (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
    1195
        (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
    1194
        (*S_).win.as_ref().unwrap().addnstr(
    1196
        (*S_).win.as_ref().unwrap().addnstr(
    1195
            ::std::str::from_utf8("You found kitten! Way to go, robot!\u{0}")
    1197
            ::std::str::from_utf8("You found kitten! Way to go, robot!\u{0}".as_bytes())
    1196
                .unwrap()
    1198
                .unwrap()
    1197
                .trim_end_matches('\u{0}'),
    1199
                .trim_end_matches('\u{0}'),
    1198
            -1i32 as usize,
    1200
            -1i32 as usize,
    1199
        );
    1201
        );
    1200
        (*S_).win.as_ref().unwrap().refresh();
    1202
        (*S_).win.as_ref().unwrap().refresh();

    Finally, we remove the redundant conversion from &str to &[u8] to &str:

    rewrite_expr
        '::std::str::from_utf8(__e.as_bytes())'
        'Some(__e)' ;
    

    Diff #74

    src/robotfindskitten.rs
    1052
                old_y = (*S_).robot.y
    1052
                old_y = (*S_).robot.y
    1053
            }
    1053
            }
    1054
            input = ::encode_input((*S_).win.as_ref().unwrap().getch())
    1054
            input = ::encode_input((*S_).win.as_ref().unwrap().getch())
    1055
        }
    1055
        }
    1056
        message(
    1056
        message(
    1057
            Some(
    1057
            Some(Some("Bye!\u{0}").unwrap().trim_end_matches('\u{0}')).unwrap(),
    1058
                ::std::str::from_utf8("Bye!\u{0}".as_bytes())
    1059
                    .unwrap()
    1060
                    .trim_end_matches('\u{0}'),
    1061
            )
    1062
            .unwrap(),
    1063
            S_,
    1058
            S_,
    1064
        );
    1059
        );
    1065
        (*S_).win.as_ref().unwrap().refresh();
    1060
        (*S_).win.as_ref().unwrap().refresh();
    1066
        finish(0i32);
    1061
        finish(0i32);
    1067
    }
    1062
    }

    1112
            261 | 108 | 76 | 6 => check_x += 1,
    1107
            261 | 108 | 76 | 6 => check_x += 1,
    1113
            0 => {}
    1108
            0 => {}
    1114
            _ => {
    1109
            _ => {
    1115
                message(
    1110
                message(
    1116
                    Some(
    1111
                    Some(
    1117
                        ::std::str::from_utf8(
    1118
                            "Invalid input: Use direction keys or Esc.\u{0}".as_bytes(),
    1112
                        Some("Invalid input: Use direction keys or Esc.\u{0}")
    1119
                        )
    1120
                        .unwrap()
    1113
                            .unwrap()
    1121
                        .trim_end_matches('\u{0}'),
    1114
                            .trim_end_matches('\u{0}'),
    1122
                    )
    1115
                    )
    1123
                    .unwrap(),
    1116
                    .unwrap(),
    1124
                    S_,
    1117
                    S_,
    1125
                );
    1118
                );
    1126
                return;
    1119
                return;

    1192
            ));
    1185
            ));
    1193
            counter -= 1
    1186
            counter -= 1
    1194
        }
    1187
        }
    1195
        (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
    1188
        (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
    1196
        (*S_).win.as_ref().unwrap().addnstr(
    1189
        (*S_).win.as_ref().unwrap().addnstr(
    1197
            ::std::str::from_utf8("You found kitten! Way to go, robot!\u{0}".as_bytes())
    1190
            Some("You found kitten! Way to go, robot!\u{0}")
    1198
                .unwrap()
    1191
                .unwrap()
    1199
                .trim_end_matches('\u{0}'),
    1192
                .trim_end_matches('\u{0}'),
    1200
            -1i32 as usize,
    1193
            -1i32 as usize,
    1201
        );
    1194
        );
    1202
        (*S_).win.as_ref().unwrap().refresh();
    1195
        (*S_).win.as_ref().unwrap().refresh();

    With the replacements above, we have removed all redundant string conversions from the crate. This was the last major source of unnecessary unsafety in robotfindskitten.

    The last few changes we make are purely cosmetic - they do not affect safety. First, Some(x).unwrap() is the same as just x:

    rewrite_expr
        'Some(__x).unwrap()'
        '__x' ;
    

    Diff #75

    src/robotfindskitten.rs
    902
        let mut counter: libc::c_int = 0;
    902
        let mut counter: libc::c_int = 0;
    903
        fmt_mvprintw(
    903
        fmt_mvprintw(
    904
            0i32,
    904
            0i32,
    905
            0i32,
    905
            0i32,
    906
            format_args!("robotfindskitten v{:}\n\n", unsafe {
    906
            format_args!("robotfindskitten v{:}\n\n", unsafe {
    907
                Some(ver.trim_end_matches('\u{0}')).unwrap()
    907
                ver.trim_end_matches('\u{0}')
    908
            }),
    908
            }),
    909
            S_,
    909
            S_,
    910
        );
    910
        );
    911
        counter = 0i32;
    911
        counter = 0i32;
    912
        while counter <= (*S_).win.as_ref().unwrap().get_max_x() - 1i32 {
    912
        while counter <= (*S_).win.as_ref().unwrap().get_max_x() - 1i32 {

    983
        let mut dummy: libc::c_char = 0;
    983
        let mut dummy: libc::c_char = 0;
    984
        fmt_mvprintw(
    984
        fmt_mvprintw(
    985
            0i32,
    985
            0i32,
    986
            0i32,
    986
            0i32,
    987
            format_args!("robotfindskitten v{:}\n", unsafe {
    987
            format_args!("robotfindskitten v{:}\n", unsafe {
    988
                Some(ver.trim_end_matches('\u{0}')).unwrap()
    988
                ver.trim_end_matches('\u{0}')
    989
            }),
    989
            }),
    990
            S_,
    990
            S_,
    991
        );
    991
        );
    992
        fmt_printw(
    992
        fmt_printw(
    993
            format_args!("By the illustrious Leonard Richardson (C) 1997, 2000\n"),
    993
            format_args!("By the illustrious Leonard Richardson (C) 1997, 2000\n"),

    1051
                old_x = (*S_).robot.x;
    1051
                old_x = (*S_).robot.x;
    1052
                old_y = (*S_).robot.y
    1052
                old_y = (*S_).robot.y
    1053
            }
    1053
            }
    1054
            input = ::encode_input((*S_).win.as_ref().unwrap().getch())
    1054
            input = ::encode_input((*S_).win.as_ref().unwrap().getch())
    1055
        }
    1055
        }
    1056
        message(
    1056
        message("Bye!\u{0}".trim_end_matches('\u{0}'), S_);
    1057
            Some(Some("Bye!\u{0}").unwrap().trim_end_matches('\u{0}')).unwrap(),
    1058
            S_,
    1059
        );
    1060
        (*S_).win.as_ref().unwrap().refresh();
    1057
        (*S_).win.as_ref().unwrap().refresh();
    1061
        finish(0i32);
    1058
        finish(0i32);
    1062
    }
    1059
    }
    1063
    #[no_mangle]
    1060
    #[no_mangle]
    1064
    unsafe extern "C" fn message(mut message_0: &str, S_: &mut State) {
    1061
    unsafe extern "C" fn message(mut message_0: &str, S_: &mut State) {

    1068
            1i32,
    1065
            1i32,
    1069
            0i32,
    1066
            0i32,
    1070
            format_args!(
    1067
            format_args!(
    1071
                "{:.*}",
    1068
                "{:.*}",
    1072
                (*S_).win.as_ref().unwrap().get_max_x() as usize,
    1069
                (*S_).win.as_ref().unwrap().get_max_x() as usize,
    1073
                unsafe { Some(&message_0.to_owned()).unwrap() }
    1070
                unsafe { &message_0.to_owned() }
    1074
            ),
    1071
            ),
    1075
            S_,
    1072
            S_,
    1076
        );
    1073
        );
    1077
        (*S_).win.as_ref().unwrap().mv((*S_).robot.y, (*S_).robot.x);
    1074
        (*S_).win.as_ref().unwrap().mv((*S_).robot.y, (*S_).robot.x);
    1078
        (*S_).win.as_ref().unwrap().refresh();
    1075
        (*S_).win.as_ref().unwrap().refresh();

    1106
            260 | 104 | 72 | 2 => check_x -= 1,
    1103
            260 | 104 | 72 | 2 => check_x -= 1,
    1107
            261 | 108 | 76 | 6 => check_x += 1,
    1104
            261 | 108 | 76 | 6 => check_x += 1,
    1108
            0 => {}
    1105
            0 => {}
    1109
            _ => {
    1106
            _ => {
    1110
                message(
    1107
                message(
    1111
                    Some(
    1112
                        Some("Invalid input: Use direction keys or Esc.\u{0}")
    1108
                    "Invalid input: Use direction keys or Esc.\u{0}".trim_end_matches('\u{0}'),
    1113
                            .unwrap()
    1114
                            .trim_end_matches('\u{0}'),
    1115
                    )
    1116
                    .unwrap(),
    1117
                    S_,
    1109
                    S_,
    1118
                );
    1110
                );
    1119
                return;
    1111
                return;
    1120
            }
    1112
            }
    1121
        }
    1113
        }

    1136
                    (*S_).win.as_ref().unwrap().clrtoeol();
    1128
                    (*S_).win.as_ref().unwrap().clrtoeol();
    1137
                    play_animation(input, S_);
    1129
                    play_animation(input, S_);
    1138
                }
    1130
                }
    1139
                _ => {
    1131
                _ => {
    1140
                    message(
    1132
                    message(
    1141
                        Some(
    1142
                            messages[(*S_).bogus_messages[((*S_).screen[check_x as isize as usize]
    1133
                        messages[(*S_).bogus_messages[((*S_).screen[check_x as isize as usize]
    1143
                                [check_y as isize as usize]
    1134
                            [check_y as isize as usize]
    1144
                                - 2i32) as usize] as usize]
    1135
                            - 2i32) as usize] as usize]
    1145
                                .trim_end_matches('\u{0}'),
    1136
                            .trim_end_matches('\u{0}'),
    1146
                        )
    1147
                        .unwrap(),
    1148
                        S_,
    1137
                        S_,
    1149
                    );
    1138
                    );
    1150
                }
    1139
                }
    1151
            }
    1140
            }
    1152
            return;
    1141
            return;

    1185
            ));
    1174
            ));
    1186
            counter -= 1
    1175
            counter -= 1
    1187
        }
    1176
        }
    1188
        (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
    1177
        (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
    1189
        (*S_).win.as_ref().unwrap().addnstr(
    1178
        (*S_).win.as_ref().unwrap().addnstr(
    1190
            Some("You found kitten! Way to go, robot!\u{0}")
    1179
            "You found kitten! Way to go, robot!\u{0}".trim_end_matches('\u{0}'),
    1191
                .unwrap()
    1192
                .trim_end_matches('\u{0}'),
    1193
            -1i32 as usize,
    1180
            -1i32 as usize,
    1194
        );
    1181
        );
    1195
        (*S_).win.as_ref().unwrap().refresh();
    1182
        (*S_).win.as_ref().unwrap().refresh();
    1196
        finish(0i32);
    1183
        finish(0i32);
    1197
    }
    1184
    }

    And second, "foo\0".trim_end_matches('\0') is the same as just "foo". This one is a little more complicated to rewrite. We first remove null terminators throughout the crate, then remove the calls to trim_end_matches:

    select target 'crate; desc(expr);' ;
    remove_null_terminator ;
    clear_marks ;
    
    rewrite_expr "__e.trim_end_matches('\0')" '__e' ;
    

    Diff #76

    src/robotfindskitten.rs
    211
        pub y: libc::c_int,
    211
        pub y: libc::c_int,
    212
        pub color: libc::c_int,
    212
        pub color: libc::c_int,
    213
        pub bold: bool,
    213
        pub bold: bool,
    214
        pub character: libc::c_char,
    214
        pub character: libc::c_char,
    215
    }
    215
    }
    216
    static ver: &'static str = "1.7320508.406\u{0}";
    216
    static ver: &'static str = "1.7320508.406";
    217
    217
    218
    /*Be sure to change MESSAGES when you change the array, or bad things
    218
    /*Be sure to change MESSAGES when you change the array, or bad things
    219
    will happen.*/
    219
    will happen.*/
    220
    /*Also, take note that robotfindskitten.c and configure.in
    220
    /*Also, take note that robotfindskitten.c and configure.in
    221
    currently have the version number hardcoded into them, and they
    221
    currently have the version number hardcoded into them, and they
    222
    should reflect MESSAGES. */
    222
    should reflect MESSAGES. */
    223
    /* Watch out for fenceposts.*/
    223
    /* Watch out for fenceposts.*/
    224
    static messages: [&'static str; 406] = [
    224
    static messages: [&'static str; 406] = [
    225
        "\"I pity the fool who mistakes me for kitten!\", sez Mr. T.\u{0}",
    225
        "\"I pity the fool who mistakes me for kitten!\", sez Mr. T.",
    226
        "That\'s just an old tin can.\u{0}",
    226
        "That\'s just an old tin can.",
    227
        "It\'s an altar to the horse god.\u{0}",
    227
        "It\'s an altar to the horse god.",
    228
        "A box of dancing mechanical pencils. They dance! They sing!\u{0}",
    228
        "A box of dancing mechanical pencils. They dance! They sing!",
    229
        "It\'s an old Duke Ellington record.\u{0}",
    229
        "It\'s an old Duke Ellington record.",

    626
        "It\'s your favorite game -- robotfindscatan!\u{0}",
    626
        "It\'s your favorite game -- robotfindscatan!",
    627
        "Just a man selling an albatross.\u{0}",
    627
        "Just a man selling an albatross.",
    628
        "The intermission from a 1930s silent movie.\u{0}",
    628
        "The intermission from a 1930s silent movie.",
    629
        "It\'s an inverted billiard ball!\u{0}",
    629
        "It\'s an inverted billiard ball!",
    630
        "The spectre of Sherlock Holmes wills you onwards.\u{0}",
    630
        "The spectre of Sherlock Holmes wills you onwards.",
    631
    ];
    631
    ];
    632
    /*
    632
    /*
    633
     *Function definitions
    633
     *Function definitions
    634
     */
    634
     */
    635
    /*Initialization and setup functions*/
    635
    /*Initialization and setup functions*/

    901
    unsafe extern "C" fn initialize_screen(S_: &mut State) {
    901
    unsafe extern "C" fn initialize_screen(S_: &mut State) {
    902
        let mut counter: libc::c_int = 0;
    902
        let mut counter: libc::c_int = 0;
    903
        fmt_mvprintw(
    903
        fmt_mvprintw(
    904
            0i32,
    904
            0i32,
    905
            0i32,
    905
            0i32,
    906
            format_args!("robotfindskitten v{:}\n\n", unsafe {
    906
            format_args!("robotfindskitten v{:}\n\n", unsafe { ver }),
    907
                ver.trim_end_matches('\u{0}')
    908
            }),
    909
            S_,
    907
            S_,
    910
        );
    908
        );
    911
        counter = 0i32;
    909
        counter = 0i32;
    912
        while counter <= (*S_).win.as_ref().unwrap().get_max_x() - 1i32 {
    910
        while counter <= (*S_).win.as_ref().unwrap().get_max_x() - 1i32 {
    913
            fmt_printw(format_args!("{:}", 95i32 as u8 as char), S_);
    911
            fmt_printw(format_args!("{:}", 95i32 as u8 as char), S_);

    982
    unsafe extern "C" fn instructions(S_: &mut State) {
    980
    unsafe extern "C" fn instructions(S_: &mut State) {
    983
        let mut dummy: libc::c_char = 0;
    981
        let mut dummy: libc::c_char = 0;
    984
        fmt_mvprintw(
    982
        fmt_mvprintw(
    985
            0i32,
    983
            0i32,
    986
            0i32,
    984
            0i32,
    987
            format_args!("robotfindskitten v{:}\n", unsafe {
    985
            format_args!("robotfindskitten v{:}\n", unsafe { ver }),
    988
                ver.trim_end_matches('\u{0}')
    989
            }),
    990
            S_,
    986
            S_,
    991
        );
    987
        );
    992
        fmt_printw(
    988
        fmt_printw(
    993
            format_args!("By the illustrious Leonard Richardson (C) 1997, 2000\n"),
    989
            format_args!("By the illustrious Leonard Richardson (C) 1997, 2000\n"),
    994
            S_,
    990
            S_,

    1051
                old_x = (*S_).robot.x;
    1047
                old_x = (*S_).robot.x;
    1052
                old_y = (*S_).robot.y
    1048
                old_y = (*S_).robot.y
    1053
            }
    1049
            }
    1054
            input = ::encode_input((*S_).win.as_ref().unwrap().getch())
    1050
            input = ::encode_input((*S_).win.as_ref().unwrap().getch())
    1055
        }
    1051
        }
    1056
        message("Bye!\u{0}".trim_end_matches('\u{0}'), S_);
    1052
        message("Bye!", S_);
    1057
        (*S_).win.as_ref().unwrap().refresh();
    1053
        (*S_).win.as_ref().unwrap().refresh();
    1058
        finish(0i32);
    1054
        finish(0i32);
    1059
    }
    1055
    }
    1060
    #[no_mangle]
    1056
    #[no_mangle]
    1061
    unsafe extern "C" fn message(mut message_0: &str, S_: &mut State) {
    1057
    unsafe extern "C" fn message(mut message_0: &str, S_: &mut State) {

    1102
            }
    1098
            }
    1103
            260 | 104 | 72 | 2 => check_x -= 1,
    1099
            260 | 104 | 72 | 2 => check_x -= 1,
    1104
            261 | 108 | 76 | 6 => check_x += 1,
    1100
            261 | 108 | 76 | 6 => check_x += 1,
    1105
            0 => {}
    1101
            0 => {}
    1106
            _ => {
    1102
            _ => {
    1107
                message(
    1103
                message("Invalid input: Use direction keys or Esc.", S_);
    1108
                    "Invalid input: Use direction keys or Esc.\u{0}".trim_end_matches('\u{0}'),
    1109
                    S_,
    1110
                );
    1111
                return;
    1104
                return;
    1112
            }
    1105
            }
    1113
        }
    1106
        }
    1114
        if check_y < 3i32
    1107
        if check_y < 3i32
    1115
            || check_y > (*S_).win.as_ref().unwrap().get_max_y() - 1i32
    1108
            || check_y > (*S_).win.as_ref().unwrap().get_max_y() - 1i32

    1130
                }
    1123
                }
    1131
                _ => {
    1124
                _ => {
    1132
                    message(
    1125
                    message(
    1133
                        messages[(*S_).bogus_messages[((*S_).screen[check_x as isize as usize]
    1126
                        messages[(*S_).bogus_messages[((*S_).screen[check_x as isize as usize]
    1134
                            [check_y as isize as usize]
    1127
                            [check_y as isize as usize]
    1135
                            - 2i32) as usize] as usize]
    1128
                            - 2i32) as usize] as usize],
    1136
                            .trim_end_matches('\u{0}'),
    1137
                        S_,
    1129
                        S_,
    1138
                    );
    1130
                    );
    1139
                }
    1131
                }
    1140
            }
    1132
            }
    1141
            return;
    1133
            return;

    1173
                1i32 as libc::c_uint as u64,
    1165
                1i32 as libc::c_uint as u64,
    1174
            ));
    1166
            ));
    1175
            counter -= 1
    1167
            counter -= 1
    1176
        }
    1168
        }
    1177
        (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
    1169
        (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
    1178
        (*S_).win.as_ref().unwrap().addnstr(
    1170
        (*S_)
    1179
            "You found kitten! Way to go, robot!\u{0}".trim_end_matches('\u{0}'),
    1171
            .win
    1180
            -1i32 as usize,
    1172
            .as_ref()
    1181
        );
    1173
            .unwrap()
    1174
            .addnstr("You found kitten! Way to go, robot!", -1i32 as usize);
    1182
        (*S_).win.as_ref().unwrap().refresh();
    1175
        (*S_).win.as_ref().unwrap().refresh();
    1183
        finish(0i32);
    1176
        finish(0i32);
    1184
    }
    1177
    }
    1185
    unsafe fn main_0(
    1178
    unsafe fn main_0(
    1186
        mut argc: libc::c_int,
    1179
        mut argc: libc::c_int,

    This indiscriminate use of remove_null_terminator could introduce bugs (including memory unsafety) if the program still contained code that relies on the presence of the null terminator, such as calls to CStr::from_ptr or libc string functions. But previous refactoring steps have already removed all uses of those functions from robotfindskitten, so this transformation is safe.

    commit
    

    Final cleanup

    At this point, we have removed all the major sources of unsafety from robotfindskitten. We finish the refactoring with an assortment of minor cleanup steps.

    We want to remove unnecessary unsafe blocks, but right now every unsafe block is considered unused because they all occur inside unsafe fns. None of these functions actually need to be unsafe at this point, so we mark them safe:

    select target 'crate; desc(item && fn);' ;
    set_unsafety safe ;
    clear_marks ;
    

    Diff #78

    src/robotfindskitten.rs
    632
    /*
    632
    /*
    633
     *Function definitions
    633
     *Function definitions
    634
     */
    634
     */
    635
    /*Initialization and setup functions*/
    635
    /*Initialization and setup functions*/
    636
    #[no_mangle]
    636
    #[no_mangle]
    637
    unsafe extern "C" fn initialize_ncurses(S_: &mut State) {
    637
    extern "C" fn initialize_ncurses(S_: &mut State) {
    638
        unsafe { signal(2i32, Some(finish)) };
    638
        unsafe { signal(2i32, Some(finish)) };
    639
        (*S_).win = Some(::pancurses::initscr());
    639
        (*S_).win = Some(::pancurses::initscr());
    640
        (*S_).win.as_ref().unwrap().keypad(0 != 1i32);
    640
        (*S_).win.as_ref().unwrap().keypad(0 != 1i32);
    641
        ::pancurses::nonl();
    641
        ::pancurses::nonl();
    642
        0;
    642
        0;

    709
                    code
    709
                    code
    710
                }
    710
                }
    711
            }
    711
            }
    712
        }
    712
        }
    713
    }
    713
    }
    714
    unsafe extern "C" fn finish(mut sig: libc::c_int) {
    714
    extern "C" fn finish(mut sig: libc::c_int) {
    715
        ::pancurses::endwin();
    715
        ::pancurses::endwin();
    716
        fmt_printf(format_args!(
    716
        fmt_printf(format_args!(
    717
            "{:}{:}{:}",
    717
            "{:}{:}{:}",
    718
            27i32 as u8 as char, '(' as i32 as u8 as char, 'B' as i32 as u8 as char
    718
            27i32 as u8 as char, '(' as i32 as u8 as char, 'B' as i32 as u8 as char
    719
        ));
    719
        ));
    720
        ::std::process::exit(0i32 as i32);
    720
        ::std::process::exit(0i32 as i32);
    721
    }
    721
    }
    722
    #[no_mangle]
    722
    #[no_mangle]
    723
    unsafe extern "C" fn initialize_arrays(S_: &mut State) {
    723
    extern "C" fn initialize_arrays(S_: &mut State) {
    724
        let mut counter: libc::c_int = 0;
    724
        let mut counter: libc::c_int = 0;
    725
        let mut counter2: libc::c_int = 0;
    725
        let mut counter2: libc::c_int = 0;
    726
        let mut empty: screen_object = screen_object {
    726
        let mut empty: screen_object = screen_object {
    727
            x: 0,
    727
            x: 0,
    728
            y: 0,
    728
            y: 0,

    782
    array is bigger than it needs to be, as we don't need to keep track
    782
    array is bigger than it needs to be, as we don't need to keep track
    783
    of the first few rows of the screen. But that requires making an
    783
    of the first few rows of the screen. But that requires making an
    784
    offset function and using that everywhere. So not right now. */
    784
    offset function and using that everywhere. So not right now. */
    785
    785
    786
    #[no_mangle]
    786
    #[no_mangle]
    787
    unsafe extern "C" fn initialize_robot(S_: &mut State) {
    787
    extern "C" fn initialize_robot(S_: &mut State) {
    788
        (*S_).robot.x = (::rand::random::() >> 1) as libc::c_int
    788
        (*S_).robot.x = (::rand::random::() >> 1) as libc::c_int
    789
            % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32)
    789
            % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32)
    790
            + 1i32;
    790
            + 1i32;
    791
        (*S_).robot.y = (::rand::random::() >> 1) as libc::c_int
    791
        (*S_).robot.y = (::rand::random::() >> 1) as libc::c_int
    792
            % ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32)
    792
            % ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32)

    797
        (*S_).screen[(*S_).robot.x as isize as usize][(*S_).robot.y as isize as usize] = 0i32;
    797
        (*S_).screen[(*S_).robot.x as isize as usize][(*S_).robot.y as isize as usize] = 0i32;
    798
    }
    798
    }
    799
    /*Global variables. Bite me, it's fun.*/
    799
    /*Global variables. Bite me, it's fun.*/
    800
    800
    801
    #[no_mangle]
    801
    #[no_mangle]
    802
    unsafe extern "C" fn initialize_kitten(S_: &mut State) {
    802
    extern "C" fn initialize_kitten(S_: &mut State) {
    803
        loop {
    803
        loop {
    804
            (*S_).kitten.x = (::rand::random::() >> 1) as libc::c_int
    804
            (*S_).kitten.x = (::rand::random::() >> 1) as libc::c_int
    805
                % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32)
    805
                % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32)
    806
                + 1i32;
    806
                + 1i32;
    807
            (*S_).kitten.y = (::rand::random::() >> 1) as libc::c_int
    807
            (*S_).kitten.y = (::rand::random::() >> 1) as libc::c_int

    831
            };
    831
            };
    832
    }
    832
    }
    833
    833
    834
    /*Helper functions*/
    834
    /*Helper functions*/
    835
    #[no_mangle]
    835
    #[no_mangle]
    836
    unsafe extern "C" fn validchar(mut a: libc::c_char) -> libc::c_int {
    836
    extern "C" fn validchar(mut a: libc::c_char) -> libc::c_int {
    837
        match a as libc::c_int {
    837
        match a as libc::c_int {
    838
            35 | 32 | 127 => return 0i32,
    838
            35 | 32 | 127 => return 0i32,
    839
            _ => {}
    839
            _ => {}
    840
        }
    840
        }
    841
        return 1i32;
    841
        return 1i32;
    842
    }
    842
    }
    843
    #[no_mangle]
    843
    #[no_mangle]
    844
    unsafe extern "C" fn initialize_bogus(S_: &mut State) {
    844
    extern "C" fn initialize_bogus(S_: &mut State) {
    845
        let mut counter: libc::c_int = 0;
    845
        let mut counter: libc::c_int = 0;
    846
        let mut index: libc::c_int = 0;
    846
        let mut index: libc::c_int = 0;
    847
        counter = 0i32;
    847
        counter = 0i32;
    848
        while counter < (*S_).num_bogus {
    848
        while counter < (*S_).num_bogus {
    849
            (*S_).bogus[counter as usize].color =
    849
            (*S_).bogus[counter as usize].color =

    896
            counter += 1
    896
            counter += 1
    897
        }
    897
        }
    898
    }
    898
    }
    899
    899
    900
    #[no_mangle]
    900
    #[no_mangle]
    901
    unsafe extern "C" fn initialize_screen(S_: &mut State) {
    901
    extern "C" fn initialize_screen(S_: &mut State) {
    902
        let mut counter: libc::c_int = 0;
    902
        let mut counter: libc::c_int = 0;
    903
        fmt_mvprintw(
    903
        fmt_mvprintw(
    904
            0i32,
    904
            0i32,
    905
            0i32,
    905
            0i32,
    906
            format_args!("robotfindskitten v{:}\n\n", unsafe { ver }),
    906
            format_args!("robotfindskitten v{:}\n\n", unsafe { ver }),

    919
        draw((*S_).kitten, S_);
    919
        draw((*S_).kitten, S_);
    920
        draw((*S_).robot, S_);
    920
        draw((*S_).robot, S_);
    921
        (*S_).win.as_ref().unwrap().refresh();
    921
        (*S_).win.as_ref().unwrap().refresh();
    922
    }
    922
    }
    923
    #[no_mangle]
    923
    #[no_mangle]
    924
    unsafe extern "C" fn draw(mut o: screen_object, S_: &mut State) {
    924
    extern "C" fn draw(mut o: screen_object, S_: &mut State) {
    925
        full_draw(o, 0 != 0i32, S_);
    925
        full_draw(o, 0 != 0i32, S_);
    926
    }
    926
    }
    927
    #[no_mangle]
    927
    #[no_mangle]
    928
    unsafe extern "C" fn full_draw(mut o: screen_object, mut in_place: bool, S_: &mut State) {
    928
    extern "C" fn full_draw(mut o: screen_object, mut in_place: bool, S_: &mut State) {
    929
        let mut old: attr_t = 0;
    929
        let mut old: attr_t = 0;
    930
        let mut dummy: libc::c_short = 0;
    930
        let mut dummy: libc::c_short = 0;
    931
        let mut new: attr_t = 0;
    931
        let mut new: attr_t = 0;
    932
        if !(stdscr as *const libc::c_void).is_null() {
    932
        if !(stdscr as *const libc::c_void).is_null() {
    933
            if !(&mut old as *mut attr_t as *const libc::c_void).is_null() {
    933
            if !(&mut old as *mut attr_t as *const libc::c_void).is_null() {

    975
            .as_ref()
    975
            .as_ref()
    976
            .unwrap()
    976
            .unwrap()
    977
            .attrset(old as libc::c_int as ::pancurses::chtype);
    977
            .attrset(old as libc::c_int as ::pancurses::chtype);
    978
    }
    978
    }
    979
    #[no_mangle]
    979
    #[no_mangle]
    980
    unsafe extern "C" fn instructions(S_: &mut State) {
    980
    extern "C" fn instructions(S_: &mut State) {
    981
        let mut dummy: libc::c_char = 0;
    981
        let mut dummy: libc::c_char = 0;
    982
        fmt_mvprintw(
    982
        fmt_mvprintw(
    983
            0i32,
    983
            0i32,
    984
            0i32,
    984
            0i32,
    985
            format_args!("robotfindskitten v{:}\n", unsafe { ver }),
    985
            format_args!("robotfindskitten v{:}\n", unsafe { ver }),

    1021
        (*S_).win.as_ref().unwrap().refresh();
    1021
        (*S_).win.as_ref().unwrap().refresh();
    1022
        dummy = ::encode_input((*S_).win.as_ref().unwrap().getch()) as libc::c_char;
    1022
        dummy = ::encode_input((*S_).win.as_ref().unwrap().getch()) as libc::c_char;
    1023
        (*S_).win.as_ref().unwrap().clear();
    1023
        (*S_).win.as_ref().unwrap().clear();
    1024
    }
    1024
    }
    1025
    #[no_mangle]
    1025
    #[no_mangle]
    1026
    unsafe extern "C" fn draw_in_place(mut o: screen_object, S_: &mut State) {
    1026
    extern "C" fn draw_in_place(mut o: screen_object, S_: &mut State) {
    1027
        full_draw(o, 0 != 1i32, S_);
    1027
        full_draw(o, 0 != 1i32, S_);
    1028
    }
    1028
    }
    1029
    /*Game functions*/
    1029
    /*Game functions*/
    1030
    #[no_mangle]
    1030
    #[no_mangle]
    1031
    unsafe extern "C" fn play_game(S_: &mut State) {
    1031
    extern "C" fn play_game(S_: &mut State) {
    1032
        let mut old_x: libc::c_int = (*S_).robot.x;
    1032
        let mut old_x: libc::c_int = (*S_).robot.x;
    1033
        let mut old_y: libc::c_int = (*S_).robot.y;
    1033
        let mut old_y: libc::c_int = (*S_).robot.y;
    1034
        let mut input: libc::c_int = 0;
    1034
        let mut input: libc::c_int = 0;
    1035
        input = ::encode_input((*S_).win.as_ref().unwrap().getch());
    1035
        input = ::encode_input((*S_).win.as_ref().unwrap().getch());
    1036
        while input != 27i32 && input != 'q' as i32 && input != 'Q' as i32 {
    1036
        while input != 27i32 && input != 'q' as i32 && input != 'Q' as i32 {

    1052
        message("Bye!", S_);
    1052
        message("Bye!", S_);
    1053
        (*S_).win.as_ref().unwrap().refresh();
    1053
        (*S_).win.as_ref().unwrap().refresh();
    1054
        finish(0i32);
    1054
        finish(0i32);
    1055
    }
    1055
    }
    1056
    #[no_mangle]
    1056
    #[no_mangle]
    1057
    unsafe extern "C" fn message(mut message_0: &str, S_: &mut State) {
    1057
    extern "C" fn message(mut message_0: &str, S_: &mut State) {
    1058
        (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
    1058
        (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
    1059
        (*S_).win.as_ref().unwrap().clrtoeol();
    1059
        (*S_).win.as_ref().unwrap().clrtoeol();
    1060
        fmt_mvprintw(
    1060
        fmt_mvprintw(
    1061
            1i32,
    1061
            1i32,
    1062
            0i32,
    1062
            0i32,

    1069
        );
    1069
        );
    1070
        (*S_).win.as_ref().unwrap().mv((*S_).robot.y, (*S_).robot.x);
    1070
        (*S_).win.as_ref().unwrap().mv((*S_).robot.y, (*S_).robot.x);
    1071
        (*S_).win.as_ref().unwrap().refresh();
    1071
        (*S_).win.as_ref().unwrap().refresh();
    1072
    }
    1072
    }
    1073
    #[no_mangle]
    1073
    #[no_mangle]
    1074
    unsafe extern "C" fn process_input(mut input: libc::c_int, S_: &mut State) {
    1074
    extern "C" fn process_input(mut input: libc::c_int, S_: &mut State) {
    1075
        let mut check_x: libc::c_int = (*S_).robot.x;
    1075
        let mut check_x: libc::c_int = (*S_).robot.x;
    1076
        let mut check_y: libc::c_int = (*S_).robot.y;
    1076
        let mut check_y: libc::c_int = (*S_).robot.y;
    1077
        match input {
    1077
        match input {
    1078
            12 => {
    1078
            12 => {
    1079
                (*S_).win.as_ref().unwrap().refresh();
    1079
                (*S_).win.as_ref().unwrap().refresh();

    1134
        }
    1134
        }
    1135
        (*S_).robot.x = check_x;
    1135
        (*S_).robot.x = check_x;
    1136
        (*S_).robot.y = check_y;
    1136
        (*S_).robot.y = check_y;
    1137
    }
    1137
    }
    1138
    #[no_mangle]
    1138
    #[no_mangle]
    1139
    unsafe extern "C" fn play_animation(mut input: libc::c_int, S_: &mut State) {
    1139
    extern "C" fn play_animation(mut input: libc::c_int, S_: &mut State) {
    1140
        let mut counter: libc::c_int = 0;
    1140
        let mut counter: libc::c_int = 0;
    1141
        counter = 4i32;
    1141
        counter = 4i32;
    1142
        while counter > 0i32 {
    1142
        while counter > 0i32 {
    1143
            if (*S_).win.as_ref().unwrap().mv(1i32, 50i32 + counter + 1i32) == -1i32 {
    1143
            if (*S_).win.as_ref().unwrap().mv(1i32, 50i32 + counter + 1i32) == -1i32 {
    1144
            } else {
    1144
            } else {

    1173
            .unwrap()
    1173
            .unwrap()
    1174
            .addnstr("You found kitten! Way to go, robot!", -1i32 as usize);
    1174
            .addnstr("You found kitten! Way to go, robot!", -1i32 as usize);
    1175
        (*S_).win.as_ref().unwrap().refresh();
    1175
        (*S_).win.as_ref().unwrap().refresh();
    1176
        finish(0i32);
    1176
        finish(0i32);
    1177
    }
    1177
    }
    1178
    unsafe fn main_0(
    1178
    fn main_0(
    1179
        mut argc: libc::c_int,
    1179
        mut argc: libc::c_int,
    1180
        mut argv: ::c2rust_runtime::CArray<Option<&::std::ffi::CStr>>,
    1180
        mut argv: ::c2rust_runtime::CArray<Option<&::std::ffi::CStr>>,
    1181
    ) -> libc::c_int {
    1181
    ) -> libc::c_int {
    1182
        let mut S: State = State {
    1182
        let mut S: State = State {
    1183
            win: None,
    1183
            win: None,

    This part can't be fully automated. In general, there is no easy way to tell whether the safety of a given function relies on unchecked assumptions about its input, or whether it might break invariants that other functions rely on. In the case of robotfindskitten, every function really is safe, but for other applications or libraries, it might be necessary to be more selective when removing the unsafe qualifier.

    Now that all functions are safe, fix_unused_unsafe will remove any unsafe blocks that contain no unsafe operations:

    fix_unused_unsafe
    

    Diff #79

    src/robotfindskitten.rs
    105
        y: libc::c_int,
    105
        y: libc::c_int,
    106
        x: libc::c_int,
    106
        x: libc::c_int,
    107
        args: ::std::fmt::Arguments,
    107
        args: ::std::fmt::Arguments,
    108
        S_: &mut State,
    108
        S_: &mut State,
    109
    ) -> libc::c_int {
    109
    ) -> libc::c_int {
    110
        unsafe {
    110
        {
    111
            (*S_)
    111
            (*S_)
    112
                .win
    112
                .win
    113
                .as_ref()
    113
                .as_ref()
    114
                .unwrap()
    114
                .unwrap()
    115
                .mvprintw(y, x, &format!("{}", args))
    115
                .mvprintw(y, x, &format!("{}", args))
    116
        }
    116
        }
    117
    }
    117
    }
    118
    fn fmt_printw(args: ::std::fmt::Arguments, S_: &mut State) -> libc::c_int {
    118
    fn fmt_printw(args: ::std::fmt::Arguments, S_: &mut State) -> libc::c_int {
    119
        {
    119
        unsafe { (*S_).win.as_ref().unwrap().printw(&format!("{}", args)) }
    120
            (*S_).win.as_ref().unwrap().printw(&format!("{}", args))
    121
        }
    120
    }
    122
    }
    121
    fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
    123
    fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
    122
        print!("{}", args);
    124
        print!("{}", args);
    123
        0
    125
        0
    124
    }
    126
    }

    729
            color: 0,
    731
            color: 0,
    730
            bold: false,
    732
            bold: false,
    731
            character: 0,
    733
            character: 0,
    732
        };
    734
        };
    733
        let mut i: libc::c_int = 0i32;
    735
        let mut i: libc::c_int = 0i32;
    734
        (*S_).screen = unsafe {
    736
        (*S_).screen = {
    735
            ::c2rust_runtime::CArray::alloc(
    737
            ::c2rust_runtime::CArray::alloc(
    736
                (::std::mem::size_of::<*mut libc::c_int>() as libc::c_ulong).wrapping_mul(
    738
                (::std::mem::size_of::<*mut libc::c_int>() as libc::c_ulong).wrapping_mul(
    737
                    ((*S_).win.as_ref().unwrap().get_max_x() - 1i32 + 1i32) as libc::c_ulong,
    739
                    ((*S_).win.as_ref().unwrap().get_max_x() - 1i32 + 1i32) as libc::c_ulong,
    738
                ) as usize
    740
                ) as usize
    739
                    / ::std::mem::size_of::<::c2rust_runtime::CArray<i32>>(),
    741
                    / ::std::mem::size_of::<::c2rust_runtime::CArray<i32>>(),
    740
            )
    742
            )
    741
        };
    743
        };
    742
        i = 0i32;
    744
        i = 0i32;
    743
        while i < (*S_).win.as_ref().unwrap().get_max_x() - 1i32 + 1i32 {
    745
        while i < (*S_).win.as_ref().unwrap().get_max_x() - 1i32 + 1i32 {
    744
            let ref mut fresh0 = (*S_).screen[i as isize as usize];
    746
            let ref mut fresh0 = (*S_).screen[i as isize as usize];
    745
            *fresh0 = unsafe {
    747
            *fresh0 = {
    746
                ::c2rust_runtime::CArray::alloc(
    748
                ::c2rust_runtime::CArray::alloc(
    747
                    (::std::mem::size_of::() as libc::c_ulong).wrapping_mul(
    749
                    (::std::mem::size_of::() as libc::c_ulong).wrapping_mul(
    748
                        ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 + 1i32) as libc::c_ulong,
    750
                        ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 + 1i32) as libc::c_ulong,
    749
                    ) as usize
    751
                    ) as usize
    750
                        / ::std::mem::size_of::(),
    752
                        / ::std::mem::size_of::(),

    901
    extern "C" fn initialize_screen(S_: &mut State) {
    903
    extern "C" fn initialize_screen(S_: &mut State) {
    902
        let mut counter: libc::c_int = 0;
    904
        let mut counter: libc::c_int = 0;
    903
        fmt_mvprintw(
    905
        fmt_mvprintw(
    904
            0i32,
    906
            0i32,
    905
            0i32,
    907
            0i32,
    906
            format_args!("robotfindskitten v{:}\n\n", unsafe { ver }),
    908
            format_args!("robotfindskitten v{:}\n\n", { ver }),
    907
            S_,
    909
            S_,
    908
        );
    910
        );
    909
        counter = 0i32;
    911
        counter = 0i32;
    910
        while counter <= (*S_).win.as_ref().unwrap().get_max_x() - 1i32 {
    912
        while counter <= (*S_).win.as_ref().unwrap().get_max_x() - 1i32 {
    911
            fmt_printw(format_args!("{:}", 95i32 as u8 as char), S_);
    913
            fmt_printw(format_args!("{:}", 95i32 as u8 as char), S_);

    980
    extern "C" fn instructions(S_: &mut State) {
    982
    extern "C" fn instructions(S_: &mut State) {
    981
        let mut dummy: libc::c_char = 0;
    983
        let mut dummy: libc::c_char = 0;
    982
        fmt_mvprintw(
    984
        fmt_mvprintw(
    983
            0i32,
    985
            0i32,
    984
            0i32,
    986
            0i32,
    985
            format_args!("robotfindskitten v{:}\n", unsafe { ver }),
    987
            format_args!("robotfindskitten v{:}\n", { ver }),
    986
            S_,
    988
            S_,
    987
        );
    989
        );
    988
        fmt_printw(
    990
        fmt_printw(
    989
            format_args!("By the illustrious Leonard Richardson (C) 1997, 2000\n"),
    991
            format_args!("By the illustrious Leonard Richardson (C) 1997, 2000\n"),
    990
            S_,
    992
            S_,

    1058
        (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
    1060
        (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
    1059
        (*S_).win.as_ref().unwrap().clrtoeol();
    1061
        (*S_).win.as_ref().unwrap().clrtoeol();
    1060
        fmt_mvprintw(
    1062
        fmt_mvprintw(
    1061
            1i32,
    1063
            1i32,
    1062
            0i32,
    1064
            0i32,
    1063
            format_args!(
    1064
                "{:.*}",
    1065
                (*S_).win.as_ref().unwrap().get_max_x() as usize,
    1065
            format_args!("{:.*}", (*S_).win.as_ref().unwrap().get_max_x() as usize, {
    1066
                unsafe { &message_0.to_owned() }
    1066
                &message_0.to_owned()
    1067
            ),
    1067
            }),
    1068
            S_,
    1068
            S_,
    1069
        );
    1069
        );
    1070
        (*S_).win.as_ref().unwrap().mv((*S_).robot.y, (*S_).robot.x);
    1070
        (*S_).win.as_ref().unwrap().mv((*S_).robot.y, (*S_).robot.x);
    1071
        (*S_).win.as_ref().unwrap().refresh();
    1071
        (*S_).win.as_ref().unwrap().refresh();
    1072
    }
    1072
    }

    1188
                bold: false,
    1188
                bold: false,
    1189
                character: 0,
    1189
                character: 0,
    1190
            }; 406],
    1190
            }; 406],
    1191
            bogus_messages: [0; 406],
    1191
            bogus_messages: [0; 406],
    1192
            used_messages: [0; 406],
    1192
            used_messages: [0; 406],
    1193
            screen: unsafe { ::c2rust_runtime::CArray::empty() },
    1193
            screen: { ::c2rust_runtime::CArray::empty() },
    1194
            robot: screen_object {
    1194
            robot: screen_object {
    1195
                x: 0,
    1195
                x: 0,
    1196
                y: 0,
    1196
                y: 0,
    1197
                color: 0,
    1197
                color: 0,
    1198
                bold: false,
    1198
                bold: false,

    1256
        for i in 0..args_owned.len() {
    1256
        for i in 0..args_owned.len() {
    1257
            args[i] = Some(&args_owned[i]);
    1257
            args[i] = Some(&args_owned[i]);
    1258
        }
    1258
        }
    1259
        // The last element of `args` remains `None`.
    1259
        // The last element of `args` remains `None`.
    1260
    1260
    1261
        unsafe {
    1261
        {
    1262
            ::std::process::exit(main_0((args.len() - 1) as libc::c_int, args) as i32);
    1262
            ::std::process::exit(main_0((args.len() - 1) as libc::c_int, args) as i32);
    1263
        }
    1263
        }
    1264
    }
    1264
    }
    1265
    fn opt_c_str_to_ptr(x: Option<&::std::ffi::CStr>) -> *const libc::c_char {
    1265
    fn opt_c_str_to_ptr(x: Option<&::std::ffi::CStr>) -> *const libc::c_char {
    1266
        match x {
    1266
        match x {

    Next, we remove a number of unused items from the crate. We have replaced all uses of the FFI declarations generated by c2rust with alternatives, except for one call to signal. We generate a new extern "C" block containing only the declaration of signal, then delete the old unused extern "C" blocks. remove the declarations now:

    select target 'crate; desc(foreign_mod); last;' ;
    create_item '
        extern "C" {
            fn signal(sig: libc::c_int, handler: __sighandler_t) -> __sighandler_t;
        }
    ' after ;
    
    select target 'crate; desc(foreign_mod && !marked(new));' ;
    delete_items ;
    

    Diff #80

    src/robotfindskitten.rs
    20
    }
    20
    }
    21
    extern crate c2rust_runtime;
    21
    extern crate c2rust_runtime;
    22
    extern crate libc;
    22
    extern crate libc;
    23
    extern crate pancurses;
    23
    extern crate pancurses;
    24
    extern "C" {
    24
    extern "C" {
    25
        pub type ldat;
    26
        #[no_mangle]
    27
        fn printf(_: *const libc::c_char, ...) -> libc::c_int;
    28
        #[no_mangle]
    29
        fn cbreak() -> libc::c_int;
    30
        #[no_mangle]
    31
        fn endwin() -> libc::c_int;
    32
        #[no_mangle]
    33
        fn has_colors() -> bool;
    34
        #[no_mangle]
    35
        fn initscr() -> *mut WINDOW;
    36
        #[no_mangle]
    37
        fn init_pair(_: libc::c_short, _: libc::c_short, _: libc::c_short) -> libc::c_int;
    38
        #[no_mangle]
    39
        fn intrflush(_: *mut WINDOW, _: bool) -> libc::c_int;
    40
        #[no_mangle]
    41
        fn keypad(_: *mut WINDOW, _: bool) -> libc::c_int;
    42
        #[no_mangle]
    43
        fn mvprintw(_: libc::c_int, _: libc::c_int, _: *const libc::c_char, ...) -> libc::c_int;
    44
        #[no_mangle]
    45
        fn noecho() -> libc::c_int;
    46
        #[no_mangle]
    47
        fn nonl() -> libc::c_int;
    48
        #[no_mangle]
    49
        fn printw(_: *const libc::c_char, ...) -> libc::c_int;
    50
        #[no_mangle]
    51
        fn start_color() -> libc::c_int;
    52
        #[no_mangle]
    53
        fn waddch(_: *mut WINDOW, _: chtype) -> libc::c_int;
    54
        #[no_mangle]
    55
        fn waddnstr(_: *mut WINDOW, _: *const libc::c_char, _: libc::c_int) -> libc::c_int;
    56
        #[no_mangle]
    57
        fn wclear(_: *mut WINDOW) -> libc::c_int;
    58
        #[no_mangle]
    59
        fn wclrtoeol(_: *mut WINDOW) -> libc::c_int;
    60
        #[no_mangle]
    61
        fn wgetch(_: *mut WINDOW) -> libc::c_int;
    62
        #[no_mangle]
    63
        fn wmove(_: *mut WINDOW, _: libc::c_int, _: libc::c_int) -> libc::c_int;
    64
        #[no_mangle]
    65
        fn wrefresh(_: *mut WINDOW) -> libc::c_int;
    66
        #[no_mangle]
    67
        static mut curscr: *mut WINDOW;
    68
        #[no_mangle]
    69
        static mut stdscr: *mut WINDOW;
    70
        #[no_mangle]
    71
        static mut COLS: libc::c_int;
    72
        #[no_mangle]
    73
        static mut LINES: libc::c_int;
    74
        #[no_mangle]
    75
        fn signal(__sig: libc::c_int, __handler: __sighandler_t) -> __sighandler_t;
    25
        fn signal(sig: libc::c_int, handler: __sighandler_t) -> __sighandler_t;
    76
        #[no_mangle]
    77
        fn strtol(
    78
            __nptr: *const libc::c_char,
    79
            __endptr: *mut *mut libc::c_char,
    80
            __base: libc::c_int,
    81
        ) -> libc::c_long;
    82
        #[no_mangle]
    83
        fn rand() -> libc::c_int;
    84
        #[no_mangle]
    85
        fn srand(__seed: libc::c_uint);
    86
        #[no_mangle]
    87
        fn malloc(_: libc::c_ulong) -> *mut libc::c_void;
    88
        #[no_mangle]
    89
        fn exit(_: libc::c_int) -> !;
    90
        #[no_mangle]
    91
        fn time(__timer: *mut time_t) -> time_t;
    92
        #[no_mangle]
    93
        fn sleep(__seconds: libc::c_uint) -> libc::c_uint;
    94
    }
    26
    }
    95
    extern "C" {
    96
        fn wattr_get(
    97
            win: *mut WINDOW,
    98
            attrs: *mut attr_t,
    99
            pair: *mut libc::c_short,
    100
            opts: *mut libc::c_void,
    101
        ) -> libc::c_int;
    102
        fn wattrset(win: *mut WINDOW, attrs: libc::c_int) -> libc::c_int;
    103
    }
    104
    fn fmt_mvprintw(
    27
    fn fmt_mvprintw(
    105
        y: libc::c_int,
    28
        y: libc::c_int,
    106
        x: libc::c_int,
    29
        x: libc::c_int,
    107
        args: ::std::fmt::Arguments,
    30
        args: ::std::fmt::Arguments,
    108
        S_: &mut State,
    31
        S_: &mut State,

    Furthermore, we can delete a number of type declarations that were previously used only in foreign functions:

    select target '
        item(__time_t);
        item(time_t);
        item(pdat);
        item(_win_st);
        item(WINDOW);
    ' ;
    delete_items ;
    

    Diff #81

    src/robotfindskitten.rs
    45
    }
    45
    }
    46
    fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
    46
    fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
    47
        print!("{}", args);
    47
        print!("{}", args);
    48
        0
    48
        0
    49
    }
    49
    }
    50
    pub type __time_t = libc::c_long;
    50
    51
    pub type chtype = libc::c_ulong;
    51
    pub type chtype = libc::c_ulong;
    52
    52
    53
    #[repr(C)]
    54
    #[derive(Copy, Clone)]
    55
    pub struct _win_st {
    56
        pub _cury: libc::c_short,
    57
        pub _curx: libc::c_short,
    58
        pub _maxy: libc::c_short,
    59
        pub _maxx: libc::c_short,
    60
        pub _begy: libc::c_short,
    61
        pub _begx: libc::c_short,
    62
        pub _flags: libc::c_short,
    63
        pub _attrs: attr_t,
    64
        pub _bkgd: chtype,
    65
        pub _notimeout: bool,
    66
        pub _clear: bool,
    67
        pub _leaveok: bool,
    68
        pub _scroll: bool,
    69
        pub _idlok: bool,
    70
        pub _idcok: bool,
    71
        pub _immed: bool,
    72
        pub _sync: bool,
    73
        pub _use_keypad: bool,
    74
        pub _delay: libc::c_int,
    75
        pub _line: *mut ldat,
    76
        pub _regtop: libc::c_short,
    77
        pub _regbottom: libc::c_short,
    78
        pub _parx: libc::c_int,
    79
        pub _pary: libc::c_int,
    80
        pub _parent: *mut WINDOW,
    81
        pub _pad: pdat,
    82
        pub _yoffset: libc::c_short,
    83
    }
    84
    85
    #[repr(C)]
    86
    #[derive(Copy, Clone)]
    87
    pub struct pdat {
    88
        pub _pad_y: libc::c_short,
    89
        pub _pad_x: libc::c_short,
    90
        pub _pad_top: libc::c_short,
    91
        pub _pad_left: libc::c_short,
    92
        pub _pad_bottom: libc::c_short,
    93
        pub _pad_right: libc::c_short,
    94
    }
    95
    pub type WINDOW = _win_st;
    96
    pub type attr_t = chtype;
    53
    pub type attr_t = chtype;
    97
    pub type __sighandler_t = Option<unsafe extern "C" fn(_: libc::c_int) -> ()>;
    54
    pub type __sighandler_t = Option<unsafe extern "C" fn(_: libc::c_int) -> ()>;
    98
    pub type time_t = __time_t;
    55
    99
    /*
    56
    /*
    100
     *robotfindskitten: A Zen simulation
    57
     *robotfindskitten: A Zen simulation
    101
     *
    58
     *
    102
     *Copyright (C) 1997,2000 Leonard Richardson
    59
     *Copyright (C) 1997,2000 Leonard Richardson
    103
     *                        leonardr@segfault.org
    60
     *                        leonardr@segfault.org

    Similarly, we can delete the opt_c_str_to_ptr helper function, which we used only temporarily while cleaning up string-pointer function arguments:

    select target '
        item(opt_c_str_to_ptr);
    ' ;
    delete_items ;
    

    Diff #82

    src/robotfindskitten.rs
    1140
    1140
    1141
        {
    1141
        {
    1142
            ::std::process::exit(main_0((args.len() - 1) as libc::c_int, args) as i32);
    1142
            ::std::process::exit(main_0((args.len() - 1) as libc::c_int, args) as i32);
    1143
        }
    1143
        }
    1144
    }
    1144
    }
    1145
    fn opt_c_str_to_ptr(x: Option<&::std::ffi::CStr>) -> *const libc::c_char {
    1146
        match x {
    1147
            None => ::std::ptr::null(),
    1148
            Some(x) => x.as_ptr(),
    1149
        }
    1150
    }

    Now we are done refactoring robotfindskitten. We have preserved the functionality of the original C program, but all unsafe code has been removed, with the exception of a single signal call that cannot be made safe. The refactored Rust version of robotfindskitten is still unidiomatic and somewhat difficult to read, but by removing nearly all of the unsafe code, we have established a solid foundation for future improvements.

    Final output

    Here is the final refactored version of robotfindskitten:

    Diff #83

    src/robotfindskitten.rs
    1
    #![allow(
    2
        dead_code,
    3
        mutable_transmutes,
    4
        non_camel_case_types,
    5
        non_snake_case,
    6
        non_upper_case_globals,
    7
        unused_mut
    8
    )]
    9
    #![feature(const_raw_ptr_to_usize_cast, extern_types, libc)]
    10
    extern crate rand;
    11
    struct State {
    12
        win: Option<::pancurses::Window>,
    13
        bogus: [screen_object; 406],
    14
        bogus_messages: [libc::c_int; 406],
    15
        used_messages: [libc::c_int; 406],
    16
        screen: ::c2rust_runtime::CArray<::c2rust_runtime::CArray>,
    17
        robot: screen_object,
    18
        kitten: screen_object,
    19
        num_bogus: libc::c_int,
    20
    }
    21
    extern crate c2rust_runtime;
    22
    extern crate libc;
    23
    extern crate pancurses;
    24
    extern "C" {
    25
        fn signal(sig: libc::c_int, handler: __sighandler_t) -> __sighandler_t;
    26
    }
    27
    fn fmt_mvprintw(
    28
        y: libc::c_int,
    29
        x: libc::c_int,
    30
        args: ::std::fmt::Arguments,
    31
        S_: &mut State,
    32
    ) -> libc::c_int {
    33
        {
    34
            (*S_)
    35
                .win
    36
                .as_ref()
    37
                .unwrap()
    38
                .mvprintw(y, x, &format!("{}", args))
    39
        }
    40
    }
    41
    fn fmt_printw(args: ::std::fmt::Arguments, S_: &mut State) -> libc::c_int {
    42
        {
    43
            (*S_).win.as_ref().unwrap().printw(&format!("{}", args))
    44
        }
    45
    }
    46
    fn fmt_printf(args: ::std::fmt::Arguments) -> libc::c_int {
    47
        print!("{}", args);
    48
        0
    49
    }
    50
    51
    pub type chtype = libc::c_ulong;
    52
    53
    pub type attr_t = chtype;
    54
    pub type __sighandler_t = Option<unsafe extern "C" fn(_: libc::c_int) -> ()>;
    55
    56
    /*
    57
     *robotfindskitten: A Zen simulation
    58
     *
    59
     *Copyright (C) 1997,2000 Leonard Richardson
    60
     *                        leonardr@segfault.org
    61
     *                        http://www.crummy.com/devel/
    62
     *
    63
     *   This program is free software; you can redistribute it and/or
    64
     *   modify it under the terms of the GNU General Public License as
    65
     *   published by the Free Software Foundation; either version 2 of
    66
     *   the License, or (at your option) any later version.
    67
     *
    68
     *   This program is distributed in the hope that it will be useful,
    69
     *   but WITHOUT ANY WARRANTY; without even the implied warranty of
    70
     *   MERCHANTABILITY or EXISTANCE OF KITTEN.  See the GNU General
    71
     *   Public License for more details.
    72
     *
    73
     *   http://www.gnu.org/copyleft/gpl.html
    74
     *
    75
     */
    76
    /*The messages go in a separate file because they are collectively
    77
    huge, and you might want to modify them. It would be nice to load
    78
    the messages from a text file at run time.*/
    79
    /*Constants for our internal representation of the screen.*/
    80
    /*Keycode constants*/
    81
    /*Nethack keycodes*/
    82
    /*EMACS keycodes - Subtracting 64 makes it a control command*/
    83
    /*Screen dimensions.*/
    84
    /*Macros for generating numbers in different ranges*/
    85
    /*Row constants for the animation*/
    86
    /*This struct contains all the information we need to display an object
    87
    on the screen*/
    88
    89
    #[repr(C)]
    90
    #[derive(Copy, Clone)]
    91
    pub struct screen_object {
    92
        pub x: libc::c_int,
    93
        pub y: libc::c_int,
    94
        pub color: libc::c_int,
    95
        pub bold: bool,
    96
        pub character: libc::c_char,
    97
    }
    98
    static ver: &'static str = "1.7320508.406";
    99
    100
    /*Be sure to change MESSAGES when you change the array, or bad things
    101
    will happen.*/
    102
    /*Also, take note that robotfindskitten.c and configure.in
    103
    currently have the version number hardcoded into them, and they
    104
    should reflect MESSAGES. */
    105
    /* Watch out for fenceposts.*/
    106
    static messages: [&'static str; 406] = [
    107
        "\"I pity the fool who mistakes me for kitten!\", sez Mr. T.",
    108
        "That\'s just an old tin can.",
    109
        "It\'s an altar to the horse god.",
    110
        "A box of dancing mechanical pencils. They dance! They sing!",
    111
        "It\'s an old Duke Ellington record.",

    508
        "It\'s your favorite game -- robotfindscatan!",
    509
        "Just a man selling an albatross.",
    510
        "The intermission from a 1930s silent movie.",
    511
        "It\'s an inverted billiard ball!",
    512
        "The spectre of Sherlock Holmes wills you onwards.",
    513
    ];
    514
    /*
    515
     *Function definitions
    516
     */
    517
    /*Initialization and setup functions*/
    518
    #[no_mangle]
    519
    extern "C" fn initialize_ncurses(S_: &mut State) {
    520
        unsafe { signal(2i32, Some(finish)) };
    521
        (*S_).win = Some(::pancurses::initscr());
    522
        (*S_).win.as_ref().unwrap().keypad(0 != 1i32);
    523
        ::pancurses::nonl();
    524
        0;
    525
        ::pancurses::noecho();
    526
        ::pancurses::cbreak();
    527
        if ::pancurses::has_colors() {
    528
            ::pancurses::start_color();
    529
            ::pancurses::init_pair(
    530
                0i32 as libc::c_short,
    531
                0i32 as libc::c_short,
    532
                0i32 as libc::c_short,
    533
            );
    534
            ::pancurses::init_pair(
    535
                2i32 as libc::c_short,
    536
                2i32 as libc::c_short,
    537
                0i32 as libc::c_short,
    538
            );
    539
            ::pancurses::init_pair(
    540
                1i32 as libc::c_short,
    541
                1i32 as libc::c_short,
    542
                0i32 as libc::c_short,
    543
            );
    544
            ::pancurses::init_pair(
    545
                6i32 as libc::c_short,
    546
                6i32 as libc::c_short,
    547
                0i32 as libc::c_short,
    548
            );
    549
            ::pancurses::init_pair(
    550
                7i32 as libc::c_short,
    551
                7i32 as libc::c_short,
    552
                0i32 as libc::c_short,
    553
            );
    554
            ::pancurses::init_pair(
    555
                5i32 as libc::c_short,
    556
                5i32 as libc::c_short,
    557
                0i32 as libc::c_short,
    558
            );
    559
            ::pancurses::init_pair(
    560
                4i32 as libc::c_short,
    561
                4i32 as libc::c_short,
    562
                0i32 as libc::c_short,
    563
            );
    564
            ::pancurses::init_pair(
    565
                3i32 as libc::c_short,
    566
                3i32 as libc::c_short,
    567
                0i32 as libc::c_short,
    568
            );
    569
        };
    570
    }
    571
    fn encode_input(inp: Option<::pancurses::Input>) -> libc::c_int {
    572
        use pancurses::Input::*;
    573
        let inp = match inp {
    574
            Some(x) => x,
    575
            None => return -1,
    576
        };
    577
        match inp {
    578
            // TODO: unicode inputs in the range 256 .. 512 can
    579
            // collide with ncurses special keycodes
    580
            Character(c) => c as u32 as libc::c_int,
    581
            Unknown(i) => i,
    582
            special => {
    583
                let idx = ::pancurses::SPECIAL_KEY_CODES
    584
                    .iter()
    585
                    .position(|&k| k == special)
    586
                    .unwrap();
    587
                let code = idx as i32 + ::pancurses::KEY_OFFSET;
    588
                if code > ::pancurses::KEY_F15 {
    589
                    code + 48
    590
                } else {
    591
                    code
    592
                }
    593
            }
    594
        }
    595
    }
    596
    extern "C" fn finish(mut sig: libc::c_int) {
    597
        ::pancurses::endwin();
    598
        fmt_printf(format_args!(
    599
            "{:}{:}{:}",
    600
            27i32 as u8 as char, '(' as i32 as u8 as char, 'B' as i32 as u8 as char
    601
        ));
    602
        ::std::process::exit(0i32 as i32);
    603
    }
    604
    #[no_mangle]
    605
    extern "C" fn initialize_arrays(S_: &mut State) {
    606
        let mut counter: libc::c_int = 0;
    607
        let mut counter2: libc::c_int = 0;
    608
        let mut empty: screen_object = screen_object {
    609
            x: 0,
    610
            y: 0,
    611
            color: 0,
    612
            bold: false,
    613
            character: 0,
    614
        };
    615
        let mut i: libc::c_int = 0i32;
    616
        (*S_).screen = {
    617
            ::c2rust_runtime::CArray::alloc(
    618
                (::std::mem::size_of::<*mut libc::c_int>() as libc::c_ulong).wrapping_mul(
    619
                    ((*S_).win.as_ref().unwrap().get_max_x() - 1i32 + 1i32) as libc::c_ulong,
    620
                ) as usize
    621
                    / ::std::mem::size_of::<::c2rust_runtime::CArray<i32>>(),
    622
            )
    623
        };
    624
        i = 0i32;
    625
        while i < (*S_).win.as_ref().unwrap().get_max_x() - 1i32 + 1i32 {
    626
            let ref mut fresh0 = (*S_).screen[i as isize as usize];
    627
            *fresh0 = {
    628
                ::c2rust_runtime::CArray::alloc(
    629
                    (::std::mem::size_of::() as libc::c_ulong).wrapping_mul(
    630
                        ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 + 1i32) as libc::c_ulong,
    631
                    ) as usize
    632
                        / ::std::mem::size_of::(),
    633
                )
    634
            };
    635
            i += 1
    636
        }
    637
        empty.x = -1i32;
    638
        empty.y = -1i32;
    639
        empty.color = 0i32;
    640
        empty.bold = 0 != 0i32;
    641
        empty.character = ' ' as i32 as libc::c_char;
    642
        counter = 0i32;
    643
        while counter <= (*S_).win.as_ref().unwrap().get_max_x() - 1i32 {
    644
            counter2 = 0i32;
    645
            while counter2 <= (*S_).win.as_ref().unwrap().get_max_y() - 1i32 {
    646
                (*S_).screen[counter as isize as usize][counter2 as isize as usize] = -1i32;
    647
                counter2 += 1
    648
            }
    649
            counter += 1
    650
        }
    651
        counter = 0i32;
    652
        while (counter as libc::c_ulong)
    653
            < (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    654
                .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
    655
        {
    656
            (*S_).used_messages[counter as usize] = 0i32;
    657
            (*S_).bogus_messages[counter as usize] = 0i32;
    658
            (*S_).bogus[counter as usize] = empty;
    659
            counter += 1
    660
        }
    661
    }
    662
    663
    /* This array contains our internal representation of the screen. The
    664
    array is bigger than it needs to be, as we don't need to keep track
    665
    of the first few rows of the screen. But that requires making an
    666
    offset function and using that everywhere. So not right now. */
    667
    668
    #[no_mangle]
    669
    extern "C" fn initialize_robot(S_: &mut State) {
    670
        (*S_).robot.x = (::rand::random::() >> 1) as libc::c_int
    671
            % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32)
    672
            + 1i32;
    673
        (*S_).robot.y = (::rand::random::() >> 1) as libc::c_int
    674
            % ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32)
    675
            + 3i32;
    676
        (*S_).robot.character = '#' as i32 as libc::c_char;
    677
        (*S_).robot.color = 0i32;
    678
        (*S_).robot.bold = 0 != 0i32;
    679
        (*S_).screen[(*S_).robot.x as isize as usize][(*S_).robot.y as isize as usize] = 0i32;
    680
    }
    681
    /*Global variables. Bite me, it's fun.*/
    682
    683
    #[no_mangle]
    684
    extern "C" fn initialize_kitten(S_: &mut State) {
    685
        loop {
    686
            (*S_).kitten.x = (::rand::random::() >> 1) as libc::c_int
    687
                % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32)
    688
                + 1i32;
    689
            (*S_).kitten.y = (::rand::random::() >> 1) as libc::c_int
    690
                % ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32)
    691
                + 3i32;
    692
            if !((*S_).screen[(*S_).kitten.x as isize as usize][(*S_).kitten.y as isize as usize]
    693
                != -1i32)
    694
            {
    695
                break;
    696
            }
    697
        }
    698
        loop {
    699
            (*S_).kitten.character = ((::rand::random::() >> 1) as libc::c_int
    700
                % (126i32 - '!' as i32 + 1i32)
    701
                + '!' as i32) as libc::c_char;
    702
            if !(0 == validchar((*S_).kitten.character)) {
    703
                break;
    704
            }
    705
        }
    706
        (*S_).screen[(*S_).kitten.x as isize as usize][(*S_).kitten.y as isize as usize] = 1i32;
    707
        (*S_).kitten.color = (::rand::random::() >> 1) as libc::c_int % 6i32 + 1i32;
    708
        (*S_).kitten.bold = 0
    709
            != if 0 != (::rand::random::() >> 1) as libc::c_int % 2i32 {
    710
                1i32
    711
            } else {
    712
                0i32
    713
            };
    714
    }
    715
    716
    /*Helper functions*/
    717
    #[no_mangle]
    718
    extern "C" fn validchar(mut a: libc::c_char) -> libc::c_int {
    719
        match a as libc::c_int {
    720
            35 | 32 | 127 => return 0i32,
    721
            _ => {}
    722
        }
    723
        return 1i32;
    724
    }
    725
    #[no_mangle]
    726
    extern "C" fn initialize_bogus(S_: &mut State) {
    727
        let mut counter: libc::c_int = 0;
    728
        let mut index: libc::c_int = 0;
    729
        counter = 0i32;
    730
        while counter < (*S_).num_bogus {
    731
            (*S_).bogus[counter as usize].color =
    732
                (::rand::random::() >> 1) as libc::c_int % 6i32 + 1i32;
    733
            (*S_).bogus[counter as usize].bold = 0
    734
                != if 0 != (::rand::random::() >> 1) as libc::c_int % 2i32 {
    735
                    1i32
    736
                } else {
    737
                    0i32
    738
                };
    739
            loop {
    740
                (*S_).bogus[counter as usize].character = ((::rand::random::() >> 1)
    741
                    as libc::c_int
    742
                    % (126i32 - '!' as i32 + 1i32)
    743
                    + '!' as i32) as libc::c_char;
    744
                if !(0 == validchar((*S_).bogus[counter as usize].character)) {
    745
                    break;
    746
                }
    747
            }
    748
            loop {
    749
                (*S_).bogus[counter as usize].x = (::rand::random::() >> 1)
    750
                    as libc::c_int
    751
                    % ((*S_).win.as_ref().unwrap().get_max_x() - 1i32)
    752
                    + 1i32;
    753
                (*S_).bogus[counter as usize].y = (::rand::random::() >> 1)
    754
                    as libc::c_int
    755
                    % ((*S_).win.as_ref().unwrap().get_max_y() - 1i32 - 3i32 + 1i32)
    756
                    + 3i32;
    757
                if !((*S_).screen[(*S_).bogus[counter as usize].x as isize as usize]
    758
                    [(*S_).bogus[counter as usize].y as isize as usize]
    759
                    != -1i32)
    760
                {
    761
                    break;
    762
                }
    763
            }
    764
            (*S_).screen[(*S_).bogus[counter as usize].x as isize as usize]
    765
                [(*S_).bogus[counter as usize].y as isize as usize] = counter + 2i32;
    766
            loop {
    767
                index = ((::rand::random::() >> 1) as libc::c_int as libc::c_ulong)
    768
                    .wrapping_rem(
    769
                        (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    770
                            .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong),
    771
                    ) as libc::c_int;
    772
                if !((*S_).used_messages[index as usize] != 0i32) {
    773
                    break;
    774
                }
    775
            }
    776
            (*S_).bogus_messages[counter as usize] = index;
    777
            (*S_).used_messages[index as usize] = 1i32;
    778
            counter += 1
    779
        }
    780
    }
    781
    782
    #[no_mangle]
    783
    extern "C" fn initialize_screen(S_: &mut State) {
    784
        let mut counter: libc::c_int = 0;
    785
        fmt_mvprintw(
    786
            0i32,
    787
            0i32,
    788
            format_args!("robotfindskitten v{:}\n\n", { ver }),
    789
            S_,
    790
        );
    791
        counter = 0i32;
    792
        while counter <= (*S_).win.as_ref().unwrap().get_max_x() - 1i32 {
    793
            fmt_printw(format_args!("{:}", 95i32 as u8 as char), S_);
    794
            counter += 1
    795
        }
    796
        counter = 0i32;
    797
        while counter < (*S_).num_bogus {
    798
            draw((*S_).bogus[counter as usize], S_);
    799
            counter += 1
    800
        }
    801
        draw((*S_).kitten, S_);
    802
        draw((*S_).robot, S_);
    803
        (*S_).win.as_ref().unwrap().refresh();
    804
    }
    805
    #[no_mangle]
    806
    extern "C" fn draw(mut o: screen_object, S_: &mut State) {
    807
        full_draw(o, 0 != 0i32, S_);
    808
    }
    809
    #[no_mangle]
    810
    extern "C" fn full_draw(mut o: screen_object, mut in_place: bool, S_: &mut State) {
    811
        let mut old: attr_t = 0;
    812
        let mut dummy: libc::c_short = 0;
    813
        let mut new: attr_t = 0;
    814
        if !(stdscr as *const libc::c_void).is_null() {
    815
            if !(&mut old as *mut attr_t as *const libc::c_void).is_null() {
    816
                old = (*stdscr)._attrs
    817
            } else {
    818
            };
    819
            if !(&mut dummy as *mut libc::c_short as *const libc::c_void).is_null() {
    820
                dummy = (((*stdscr)._attrs & (1u64 << 8i32).wrapping_sub(1u64) << 0i32 + 8i32) >> 8i32)
    821
                    as libc::c_int as libc::c_short
    822
            } else {
    823
            };
    824
        } else {
    825
        };
    826
        new = (o.color as chtype) << 0i32 + 8i32 & (1u64 << 8i32).wrapping_sub(1u64) << 0i32 + 8i32;
    827
        if o.character as libc::c_int == '#' as i32 {
    828
            new |= 1u64 << 12i32 + 8i32
    829
        }
    830
        if o.character as libc::c_int <= '\u{1a}' as i32 {
    831
            new |= 1u64 << 14i32 + 8i32
    832
        }
    833
        if o.bold {
    834
            new |= 1u64 << 13i32 + 8i32
    835
        }
    836
        (*S_)
    837
            .win
    838
            .as_ref()
    839
            .unwrap()
    840
            .attrset(new as libc::c_int as ::pancurses::chtype);
    841
        if in_place {
    842
            fmt_printw(
    843
                format_args!("{:}", o.character as libc::c_int as u8 as char),
    844
                S_,
    845
            );
    846
        } else {
    847
            fmt_mvprintw(
    848
                o.y,
    849
                o.x,
    850
                format_args!("{:}", o.character as libc::c_int as u8 as char),
    851
                S_,
    852
            );
    853
            (*S_).win.as_ref().unwrap().mv(o.y, o.x);
    854
        }
    855
        (*S_)
    856
            .win
    857
            .as_ref()
    858
            .unwrap()
    859
            .attrset(old as libc::c_int as ::pancurses::chtype);
    860
    }
    861
    #[no_mangle]
    862
    extern "C" fn instructions(S_: &mut State) {
    863
        let mut dummy: libc::c_char = 0;
    864
        fmt_mvprintw(
    865
            0i32,
    866
            0i32,
    867
            format_args!("robotfindskitten v{:}\n", { ver }),
    868
            S_,
    869
        );
    870
        fmt_printw(
    871
            format_args!("By the illustrious Leonard Richardson (C) 1997, 2000\n"),
    872
            S_,
    873
        );
    874
        fmt_printw(
    875
            format_args!("Written originally for the Nerth Pork robotfindskitten contest\n\n"),
    876
            S_,
    877
        );
    878
        fmt_printw(format_args!("In this game, you are robot ("), S_);
    879
        draw_in_place((*S_).robot, S_);
    880
        fmt_printw(
    881
            format_args!("). Your job is to find kitten. This task\n"),
    882
            S_,
    883
        );
    884
        fmt_printw(
    885
            format_args!("is complicated by the existence of various things which are not kitten.\n"),
    886
            S_,
    887
        );
    888
        fmt_printw(
    889
            format_args!("Robot must touch items to determine if they are kitten or not. The game\n"),
    890
            S_,
    891
        );
    892
        fmt_printw(
    893
            format_args!(
    894
                "ends when robotfindskitten. Alternatively, you may end the game by hitting\n"
    895
            ),
    896
            S_,
    897
        );
    898
        fmt_printw(
    899
            format_args!("the Esc key. See the documentation for more information.\n\n"),
    900
            S_,
    901
        );
    902
        fmt_printw(format_args!("Press any key to start.\n"), S_);
    903
        (*S_).win.as_ref().unwrap().refresh();
    904
        dummy = ::encode_input((*S_).win.as_ref().unwrap().getch()) as libc::c_char;
    905
        (*S_).win.as_ref().unwrap().clear();
    906
    }
    907
    #[no_mangle]
    908
    extern "C" fn draw_in_place(mut o: screen_object, S_: &mut State) {
    909
        full_draw(o, 0 != 1i32, S_);
    910
    }
    911
    /*Game functions*/
    912
    #[no_mangle]
    913
    extern "C" fn play_game(S_: &mut State) {
    914
        let mut old_x: libc::c_int = (*S_).robot.x;
    915
        let mut old_y: libc::c_int = (*S_).robot.y;
    916
        let mut input: libc::c_int = 0;
    917
        input = ::encode_input((*S_).win.as_ref().unwrap().getch());
    918
        while input != 27i32 && input != 'q' as i32 && input != 'Q' as i32 {
    919
            process_input(input, S_);
    920
            if !(old_x == (*S_).robot.x && old_y == (*S_).robot.y) {
    921
                if (*S_).win.as_ref().unwrap().mv(old_y, old_x) == -1i32 {
    922
                } else {
    923
                    (*S_).win.as_ref().unwrap().addch(' ' as i32 as chtype);
    924
                };
    925
                (*S_).screen[old_x as isize as usize][old_y as isize as usize] = -1i32;
    926
                draw((*S_).robot, S_);
    927
                (*S_).win.as_ref().unwrap().refresh();
    928
                (*S_).screen[(*S_).robot.x as isize as usize][(*S_).robot.y as isize as usize] = 0i32;
    929
                old_x = (*S_).robot.x;
    930
                old_y = (*S_).robot.y
    931
            }
    932
            input = ::encode_input((*S_).win.as_ref().unwrap().getch())
    933
        }
    934
        message("Bye!", S_);
    935
        (*S_).win.as_ref().unwrap().refresh();
    936
        finish(0i32);
    937
    }
    938
    #[no_mangle]
    939
    extern "C" fn message(mut message_0: &str, S_: &mut State) {
    940
        (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
    941
        (*S_).win.as_ref().unwrap().clrtoeol();
    942
        fmt_mvprintw(
    943
            1i32,
    944
            0i32,
    945
            format_args!("{:.*}", (*S_).win.as_ref().unwrap().get_max_x() as usize, {
    946
                &message_0.to_owned()
    947
            }),
    948
            S_,
    949
        );
    950
        (*S_).win.as_ref().unwrap().mv((*S_).robot.y, (*S_).robot.x);
    951
        (*S_).win.as_ref().unwrap().refresh();
    952
    }
    953
    #[no_mangle]
    954
    extern "C" fn process_input(mut input: libc::c_int, S_: &mut State) {
    955
        let mut check_x: libc::c_int = (*S_).robot.x;
    956
        let mut check_y: libc::c_int = (*S_).robot.y;
    957
        match input {
    958
            12 => {
    959
                (*S_).win.as_ref().unwrap().refresh();
    960
            }
    961
            259 | 107 | 75 | 16 => check_y -= 1,
    962
            262 | 121 | 89 => {
    963
                check_x -= 1;
    964
                check_y -= 1
    965
            }
    966
            339 | 117 | 85 => {
    967
                check_x += 1;
    968
                check_y -= 1
    969
            }
    970
            258 | 106 | 74 | 14 => check_y += 1,
    971
            360 | 98 | 66 => {
    972
                check_x -= 1;
    973
                check_y += 1
    974
            }
    975
            338 | 110 | 78 => {
    976
                check_x += 1;
    977
                check_y += 1
    978
            }
    979
            260 | 104 | 72 | 2 => check_x -= 1,
    980
            261 | 108 | 76 | 6 => check_x += 1,
    981
            0 => {}
    982
            _ => {
    983
                message("Invalid input: Use direction keys or Esc.", S_);
    984
                return;
    985
            }
    986
        }
    987
        if check_y < 3i32
    988
            || check_y > (*S_).win.as_ref().unwrap().get_max_y() - 1i32
    989
            || check_x < 0i32
    990
            || check_x > (*S_).win.as_ref().unwrap().get_max_x() - 1i32
    991
        {
    992
            return;
    993
        }
    994
        if (*S_).screen[check_x as isize as usize][check_y as isize as usize] != -1i32 {
    995
            match (*S_).screen[check_x as isize as usize][check_y as isize as usize] {
    996
                0 => {}
    997
                1 => {
    998
                    /*We didn't move, or we're stuck in a
    999
                    time warp or something.*/
    1000
                    (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
    1001
                    (*S_).win.as_ref().unwrap().clrtoeol();
    1002
                    play_animation(input, S_);
    1003
                }
    1004
                _ => {
    1005
                    message(
    1006
                        messages[(*S_).bogus_messages[((*S_).screen[check_x as isize as usize]
    1007
                            [check_y as isize as usize]
    1008
                            - 2i32) as usize] as usize],
    1009
                        S_,
    1010
                    );
    1011
                }
    1012
            }
    1013
            return;
    1014
        }
    1015
        (*S_).robot.x = check_x;
    1016
        (*S_).robot.y = check_y;
    1017
    }
    1018
    #[no_mangle]
    1019
    extern "C" fn play_animation(mut input: libc::c_int, S_: &mut State) {
    1020
        let mut counter: libc::c_int = 0;
    1021
        counter = 4i32;
    1022
        while counter > 0i32 {
    1023
            if (*S_).win.as_ref().unwrap().mv(1i32, 50i32 + counter + 1i32) == -1i32 {
    1024
            } else {
    1025
                (*S_).win.as_ref().unwrap().addch(' ' as i32 as chtype);
    1026
            };
    1027
            (*S_).win.as_ref().unwrap().mv(1i32, 50i32 + counter);
    1028
            if input == 0o405i32 || input == 0o402i32 || input == 0o540i32 || input == 0o535i32 {
    1029
                draw_in_place((*S_).kitten, S_);
    1030
            } else {
    1031
                draw_in_place((*S_).robot, S_);
    1032
            }
    1033
            if (*S_).win.as_ref().unwrap().mv(1i32, 50i32 - counter) == -1i32 {
    1034
            } else {
    1035
                (*S_).win.as_ref().unwrap().addch(' ' as i32 as chtype);
    1036
            };
    1037
            (*S_).win.as_ref().unwrap().mv(1i32, 50i32 - counter + 1i32);
    1038
            if input == 0o405i32 || input == 0o402i32 || input == 0o540i32 || input == 0o535i32 {
    1039
                draw_in_place((*S_).robot, S_);
    1040
            } else {
    1041
                draw_in_place((*S_).kitten, S_);
    1042
            }
    1043
            (*S_).win.as_ref().unwrap().refresh();
    1044
            ::std::thread::sleep(::std::time::Duration::from_secs(
    1045
                1i32 as libc::c_uint as u64,
    1046
            ));
    1047
            counter -= 1
    1048
        }
    1049
        (*S_).win.as_ref().unwrap().mv(1i32, 0i32);
    1050
        (*S_)
    1051
            .win
    1052
            .as_ref()
    1053
            .unwrap()
    1054
            .addnstr("You found kitten! Way to go, robot!", -1i32 as usize);
    1055
        (*S_).win.as_ref().unwrap().refresh();
    1056
        finish(0i32);
    1057
    }
    1058
    fn main_0(
    1059
        mut argc: libc::c_int,
    1060
        mut argv: ::c2rust_runtime::CArray<Option<&::std::ffi::CStr>>,
    1061
    ) -> libc::c_int {
    1062
        let mut S: State = State {
    1063
            win: None,
    1064
            bogus: [screen_object {
    1065
                x: 0,
    1066
                y: 0,
    1067
                color: 0,
    1068
                bold: false,
    1069
                character: 0,
    1070
            }; 406],
    1071
            bogus_messages: [0; 406],
    1072
            used_messages: [0; 406],
    1073
            screen: { ::c2rust_runtime::CArray::empty() },
    1074
            robot: screen_object {
    1075
                x: 0,
    1076
                y: 0,
    1077
                color: 0,
    1078
                bold: false,
    1079
                character: 0,
    1080
            },
    1081
            kitten: screen_object {
    1082
                x: 0,
    1083
                y: 0,
    1084
                color: 0,
    1085
                bold: false,
    1086
                character: 0,
    1087
            },
    1088
            num_bogus: 0,
    1089
        };
    1090
        if argc == 1i32 {
    1091
            S.num_bogus = 20i32
    1092
        } else {
    1093
            S.num_bogus = as ::std::str::FromStr>::from_str(
    1094
                (**argv.offset(1isize)).unwrap().to_str().unwrap(),
    1095
            )
    1096
            .unwrap();
    1097
            if S.num_bogus < 0i32
    1098
                || S.num_bogus as libc::c_ulong
    1099
                    > (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    1100
                        .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
    1101
            {
    1102
                fmt_printf(format_args!(
    1103
                    "Run-time parameter must be between 0 and {:}.\n",
    1104
                    (::std::mem::size_of::<[*mut libc::c_char; 406]>() as libc::c_ulong)
    1105
                        .wrapping_div(::std::mem::size_of::<*mut libc::c_char>() as libc::c_ulong)
    1106
                        as libc::c_int
    1107
                ));
    1108
                ::std::process::exit(0i32 as i32);
    1109
            }
    1110
        }
    1111
        ();
    1112
        fmt_printf(format_args!(
    1113
            "{:}{:}{:}",
    1114
            27i32 as u8 as char, '(' as i32 as u8 as char, 'U' as i32 as u8 as char
    1115
        ));
    1116
        initialize_ncurses(&mut S);
    1117
        initialize_arrays(&mut S);
    1118
        initialize_robot(&mut S);
    1119
        initialize_kitten(&mut S);
    1120
        initialize_bogus(&mut S);
    1121
        instructions(&mut S);
    1122
        initialize_screen(&mut S);
    1123
        play_game(&mut S);
    1124
        return 0;
    1125
    }
    1126
    fn main() {
    1127
        // Collect argv into a vector.
    1128
        let mut args_owned: Vec<::std::ffi::CString> = Vec::new();
    1129
        for arg in ::std::env::args() {
    1130
            args_owned.push(::std::ffi::CString::new(arg).unwrap());
    1131
        }
    1132
    1133
        // Now that the length is known, we can build a CArray.
    1134
        let mut args: ::c2rust_runtime::CArray<Option<&::std::ffi::CStr>> =
    1135
            ::c2rust_runtime::CArray::alloc(args_owned.len() + 1);
    1136
        for i in 0..args_owned.len() {
    1137
            args[i] = Some(&args_owned[i]);
    1138
        }
    1139
        // The last element of `args` remains `None`.
    1140
    1141
        {
    1142
            ::std::process::exit(main_0((args.len() - 1) as libc::c_int, args) as i32);
    1143
        }
    1144
    }

    Setting up a development environment

    There are three ways to build the C2Rust project:

    The previous two options automatically install all prerequisites during provisioning. You can also provision a macOS or Linux system manually.

    • If you are on a Debian-based OS, you can run scripts/provision_deb.sh to do so.

    • If you are on macOS, install the Xcode command-line tools (e.g., xcode-select --install) and homebrew first. Then run scripts/provision_mac.sh.

    • If you prefer to install dependencies yourself, or are using a non Debian-based Linux OS, our dependencies are as follows:

      • cmake >= 3.9.1
      • dirmngr
      • curl
      • git
      • gnupg2
      • gperf
      • ninja
      • unzip
      • clang 5.0+
      • intercept-build or bear - see why here
      • python-dev
      • python 3.6+
      • python dependencies
      • rustc version
      • rustfmt-preview component for the above rustc version
      • libssl (development library, dependency of the refactoring tool)

    Building with system LLVM libraries

    The quickest way to build the C2Rust transpiler is with LLVM and clang system libraries (LLVM 6 and 7 are currently supported). If you have libLLVM.so and the libclang libraries (libclangAST.a, libclangTooling.a, etc. or their shared variants) installed, you can build the transpiler with:

    $ cd c2rust-transpile
    $ cargo build
    

    You can customize the location where the build system will look for LLVM using the following environment variables at compile time:

    • LLVM_CONFIG_PATH = Path to the llvm-config tool of the LLVM installation
    • LLVM_LIB_DIR = Path to the lib directory of the LLVM installation (not necessary if you use LLVM_CONFIG_PATH)
    • LLVM_SYSTEM_LIBS = Additional system libraries LLVM needs to link against (e.g. -lz -lrt -ldl). Not necessary with llvm-config.
    • CLANG_PATH = Path to a clang that is the same version as your libclang.so. If this is necessary the build system will return an error message explaining as much.

    C2Rust (indirectly) uses the clang-sys crate which can be configured with its own environment variables.

    Building dependencies from source

    To develop on components that interact with LLVM, we recommend building against a local copy of LLVM. This will ensure that you have debug symbols and IDE integration for both LLVM and C2Rust. However, building C2Rust from source with LLVM takes a while. For a shorter build that links against prebuilt LLVM and clang system libraries, you should be able to cargo build in the c2rust-transpile directory (see the general README).

    The following from source full build script has been tested on recent versions of macOS and Ubuntu:

    $ ./scripts/build_translator.py
    

    This downloads and builds LLVM under a new top-level folder named build. Use the C2RUST_BUILD_SUFFIX variable to do multiple side-by-side builds against a local copy of LLVM like this:

    $ C2RUST_BUILD_SUFFIX=.debug ./scripts/build_translator.py --debug
    

    NOTE: Set C2RUST_BUILD_SUFFIX if building inside and outside of the provided Docker or Vagrant environments from a single C2Rust checkout.

    Testing (Optional)

    Tests are found in the tests folder. If you build the translator successfully, you should be able to run the tests with:

    $ ./scripts/test_translator.py tests
    

    This basically tests that the original C file and translated Rust file produce the same output when compiled and run. More details about tests can be found in the tests folder.

    C2Rust Vagrant environment

    • Ubuntu 18.04 (GNU/Linux 4.15.0-12-generic x86_64)
    • CMake 3.10.2
    • Ninja 1.8.2

    Installing Prerequisites

    Download a copy of vagrant from https://www.vagrantup.com/downloads.html. Vagrant supports a range of virtualization engines. We recommend you use either VirtualBox or on the VMWare editions, e.g., VMWare Workstation Player.

    On Windows, you may need to run with administrative privileges.

    Running with Virtualbox

    vagrant up

    Running with VMWare Fusion

    Requires paid plug-in. See https://www.vagrantup.com/vmware/index.html

    1. install plugin vagrant plugin install vagrant-vmware-fusion

    2. install license vagrant plugin license vagrant-vmware-fusion /path/to/license.lic

    3. start vagrant vagrant up --provider vmware_fusion

    C2Rust Docker environment

    Tested with Docker Community Edition 18.03. The version distributed with your host OS may be too old. Follow the installation instructions to get the latest version.

    Building the docker image:

    $ cd /path/to/c2rust/docker
    $ ../scripts/docker_build.sh
    

    The docker_build.sh script takes two optional arguments:

    1. the name of the base image (ubuntu:bionic by default)
    2. the name of the provisioning script (provision_deb.sh by default)

    Creating a container:

    $ ./docker_run.sh
    

    The docker_run.sh scripts takes the image name as an optional argument:

    $ ./docker_run.sh immunant/c2rust:ubuntu-xenial-20190131
    

    Stopping and starting containers:

    $ docker start c2rust
    $ docker stop c2rust
    

    Connect to a running container:

    $ ./docker_exec.sh
    

    Delete c2rust container (force stop if running)

    $ docker rm -f c2rust
    

    Warning: the following commands delete data

    removing all containers:

    docker rm `docker ps -aq`
    

    pruning all images:

    docker system prune
    # remove *all* images, not just unused ones
    docker system prune -a
    

    Adding a test case

    To add a new test case, simply create a new .c file. For example:

    void example(unsigned buffer_size, int buffer[]) {
        /* your code here */
    }
    

    Then create a new .rs file with the following skeleton (does not need to be a buffer, can check return values as well):

    
    # #![allow(unused_variables)]
    #fn main() {
    extern crate libc;
    
    use c_file::rust_example;
    
    use self::libc::c_int;
    
    #[link(name = "test")]
    extern "C" {
        #[no_mangle]
        fn example(_: c_uint, _: *mut c_int);
    }
    
    // The length can be any value
    const BUFFER_SIZE: usize = 1024;
    
    pub fn test_example() {
        let mut buffer = [0; BUFFER_SIZE];
        let mut rust_buffer = [0; BUFFER_SIZE];
        let expected_buffer = [/* this can be used as another measure of correctness */];
    
        unsafe {
            example(BUFFER_SIZE as u32, buffer.as_mut_ptr());
            rust_example(BUFFER_SIZE as u32, rust_buffer.as_mut_ptr());
        }
    
        assert_eq!(buffer, rust_buffer);
        assert_eq!(buffer, expected_buffer);
    }
    #}

    The C code can do one of two things: modify some sort of buffer or return a value.

    To completely skip the translation of a C file, you must add the comment //! skip_translation at the top of the file. That will prevent the case from showing up as red in the console output.

    You can also mark a Rust file as unexpected to compile, by adding //! xfail to the top of the file, or just expect an individual test function to fail to run by adding // xfail prior to the function definition.

    Adding //! extern_crate_X to the top of a test file will ensure extern crate X; gets added to the main binary driver. Be sure to also add the X crate to the test directory's Cargo.toml.

    Similarly, //! feature_X adds #![feature(X)] to the top of the main driver file.

    Running the tests

    From the project root, run ./scripts/test_translator.py tests to run all of the tests in the tests folder. Here are a couple other handy options:

    # run a subset of the tests
    $ ./scripts/test_translator.py --only-directories="loops" tests
    # show output of failed tests
    $ ./scripts/test_translator.py --log ERROR                tests
    # keep all of the files generated during testing
    $ ./scripts/test_translator.py --keep=all                 tests
    # get help with the command line options
    $ ./scripts/test_translator.py --help
    

    What happens under the hood

    This tests directory contains regression, feature, and unit tests. A test directory goes through the following set of steps:

    1. A compile_commands.json file is created for the Clang plugin in c2rust-ast-exporter to recognize its C source input

    2. This JSON and the C source file are fed to the c2rust-ast-exporter to produce CBOR data of the Clang type-annotated abstract syntax tree.

    3. This CBOR data is fed to the c2rust-transpile to produce a Rust source file supposedly preserving the semantics of the initial C source file.

    4. Rust test files (test_xyz.rs) are compiled into a single main wrapper and main test binary and are automatically linked against other Rust and C files thanks to cargo.

    5. The executable from the previous step is run one or more times parameterized to a specific test function.

    Building the manual

    1. cargo install --git https://github.com/immunant/mdBook.git --branch installable (May require --force if you already have mdbook installed. Requires custom changes to resolve symlinks, hopefully will be merged into upstream soon)
    2. mdbook build in the root source directory
    3. The manual should now be available in the book subdirectory.

    Adding to the manual

    1. Add a new Markdown file somewhere in the repository.
    2. Edit manual/SUMMARY.md and add a link to the new file. Use a path relative to the repository root.
    3. Add the new Markdown file to the git index (git add ...)
    4. Run scripts/link_manual.py from the root directory. This will create a symlink for the new file in the manual/ directory. This symlink should be added to git as well.

    Generated docs

    The manual/generator_dispatch.py script runs as an mdbook preprocessor and replaces {{#generate GEN ARGS}} anywhere in the book with the output of running generator GEN on ARGS. The set of available generators is defined in generator_dispatch.py.

    As one example, this is used in manual/c2rust-refactor/commands.md to replace the {{#generate refactor_commands}} placeholder with auto-generated docs for refactoring commands, by running c2rust-refactor/doc/gen_command_docs.py.

    API documentation for the Lua scripting interface is generated by calling ldoc . in the c2rust-refactor directory. This updates the c2rust-refactor/doc/scripting_api.html file which we keep checked into source control and linked into the manual.

    C2Rust Source Walkthrough

    This guide provides insight into the program structure of the c2rust translator and should be helpful to anyone wanting contribute to its development.

    This project provides tooling for translating C programs into Rust, refactoring Rust programs, and cross-checking the execution of C and Rust programs.

    Project crate structure

    The c2rust project is divided into 6 different crates. The purposes of each crate is described below.

    c2rust

    The c2rust crate provides a unified command-line interface to the translator and to the refactorer. This is intended to the be the top-level crate that a user would install and interact with.

    This crate contains logic for dispatching command-line arguments to the correct sub crate. It should not contain any logic for translating or refactoring code itself.

    c2rust-ast-builder

    The c2rust-ast-builder crate provides an AST building abstraction on top of rustc's libsyntax. This is used for code generation both in translation and refactoring.

    The builder implemented in this package provides a more stable interface to AST generation that we'd get depending directly on libsyntax. Libsyntax itself is consided unstable and subject to change dramatically at each nightly release. Libsyntax provides is own AST building functionality, but it doesn't have many of the conveniences that we use in our own implementation.

    c2rust-ast-exporter

    The c2rust project uses clang as a library in order to get reliable pre-processing, parsing, and type-checking of C code. The c2rust-ast-exporter crate provides a mix of C++ and Rust in order to provide a dump of the clang-generated AST. The exporter exports the AST using CBOR.

    c2rust-transpile

    This crate implements all of the translation logic for getting from C to Rust. It consumes input from the c2rust-ast-exporter crate and generates Rust using c2rust-ast-builder. It is invoked by the c2rust crate.

    c2rust-refactor

    This crate implements various rewrites and analyses for refactoring our generated Rust code into more idiomatic code.

    cross-checks

    This crate provides tools for instrumenting Rust executables to be suitable for running in the multi-variant execution engine.

    Crate Walkthrough: c2rust-ast-builder

    Builder type

    The Builder type allows for short-cuts when building up AST elements by providing a place to store default values for attributes that typically are not changed. This effectively allows us to simulate optional arguments to methods.

    New Builder values can be constructed using mk().

    For example the default behavior is for patterns to be immutable. If we want to emit a mutable pattern we can store the mutability flag on the Builder and then generate a pattern.

    
    # #![allow(unused_variables)]
    #fn main() {
    let mut_x_pat = mk().mutbl().ident_pat("x"); // generates: mut x
    let y_pat = mk().ident_pat("y"); // generates: y
    #}

    Make trait

    The Make trait allows for convenient, implicit coercions when using the Builder. Many methods will be parameterized over an arbitrary Make implementation to avoid needing manual conversions. It's quite common to see methods requiring Make<Ident> arguments instead of Ident so that we can accept a number of types. Any new methods implemented for Builder should look to see if there are useful Make implementations for its argument types.

    
    # #![allow(unused_variables)]
    #fn main() {
    pub trait Make<T> { fn make(self, mk: &Builder) -> T; }
    #}

    P type

    The P type comes from the libsyntax crate and provides functionality similar to Box for immutable, shared values. Many components of the Rust AST will store P<T> instead of T when there are potential savings to be had from shared references.

    Spans and Node IDs

    The Rust AST types are designed to be able to be cross-referenced to source-file locations and various type-information metadata maps. These references are tracked through span and node IDs scattered throughout the AST type definitions. In the case of generating new syntax we don't have any corresponding metadata maps to align with. Instead we fill all of these ID fields with various dummy values: DUMMY_SP and DUMMY_NODE_ID.

    Naming convention

    Builder methods are named using the pattern kind_type. For example to make a P<Ty> that is a pointer to another Ty use the ptr_ty method because internally you're making a TyKind::Ptr.

    Crate Walkthrough: c2rust-transpile

    The c2rust-transpile crate is broken up into 4 major pieces. First the c_ast modules are used to import and handle the C AST representation of the program to be translated. The cfg modules implement Relooper logic to compile away control-flow constructs that exist in C but not in Rust. In particular we use this to get rid of goto and switch statements. The translator modules do the bulk of the actual translation for declarations, statements, and expressions. The rust_ast modules provide helpers for generating the final Rust code.

    In order to preserve comments across translations we instruct clang to parse and export all comments. The mechanism provided by libsyntax for emitting comments relies on matching up comments with particular span IDs, so we have to carefully track the span IDs of the Rust AST that we generate when we want to associate a comment. Additionally libsyntax requires that span IDs are found in order, so before we emit the final code we renumber all span IDs to be in order.

    Module structure

    The c_ast module provides the Rust types that mirror the AST from Clang along with methods for deserializing from CBOR into these types. There are 4 kinds of AST element that we distinguish between: Types, Expressions, Declarations, and Statements. Each of these has a corresponding enum: CTypeKind, CExprKind, CDeclKind, and CStmtKind. All of these IDs can be dereferenced using the indexing operator on a TypedAstContext. The typed part of that name is to distinguish it from the conversion time ConversionContext where raw CBOR nodes are processed before we know which IDs correspond to one of the 4 categories above.

    The c_ast.iterator module provides a depth-first iterator of the four C AST types. This is used in the code to query different kinds of properties of the input code when translating. For example it can be used to check if a section of code uses a goto statement.

    The rust_ast module provides functionality for working with the Rust AST and not for building it. Build functionality can be found in the c2rust-ast-builder crate.

    The rust_ast.traverse module provides a depth-first visitor pattern for Rust ASTs. Transformations can be written and made to be instances of the Traversal trait to specify the desired behavior at each AST element. The default implementations of the trait's methods will simply recursively apply the traversal to the child nodes. This can be used both to transform ASTs as well as to query them.

    The rust.comment_store module handles the logic for tracking comments that are going to be reinserted back into the final generated Rust.

    Translation type

    This is a substantial type that carries the running state of a translation. This struct carries keeps track of all of the items generated so far, the language features used, the association between declarations and their Rust identifiers, and the configuration values set for this translation.

    There are various methods for translating elements of C syntax defined on this struct. When these are used any supporting items, imports, or features will be tracked in addition to returning the translated item as the method result.

    WithStmts type

    The WithStmts type is a convenient way to keep track of all of the supporting statements that go along with a value after it has been translated. When translating an expression that will not be used for anything other than its side effects, the val component will be set to a panic macro to make it easy to detect the mistake in the generated code.

    ExprContext type

    The ExprContext struct tracks information about how to translate expressions. This value is updated as translation progresses through the AST keeping track of the different contexts that expression translation can be in.

    The used attribute has one of the biggest effects on translation. This indicates when the val field of the WithStmts result is going to be emitted or discarded. This can be modified in the current context with the .used() and .unused() methods.

    The is_static attribute indicates that an expression is being used in the initializer for a static variable. Rust has many extra restrictions on the expressions that can be used for a static initializer. In some cases we can still generate valid code at the cost of readability. This fallback is enabled by this attribute.

    The decay_ref attribute keeps track of whether or not we're in a context in which Rust will infer that a reference can decay in to a pointer. This can happen at method calls, variable initializers, and possibly more locations. This allows the translation to omit some otherwise superfluous casts.

    The va_decl attribute indicates which, if any, declaration corresponds to the variable-argument list for the current variadic function. This enables us to drop the associated declaration, va_start, and va_end for that variable during translation.

    Handling Comments

    Comments are tricky to translate. Part of the issue is that comments are typically removed from C source code as part of the pre-processing phase. To handle this we extract comments from the original source code and track their source position information. In addition we track source position information for the various syntactic elements of the C translation unit.

    During translation of a statement or a declaration we look into the set of comments to see if we're as close as we're going to get to the home location of this comment. If we are we allocated a unique, temporary span idea to associated between the comment and the syntax element that should carry the comment.

    Once all of the translation is complete we revisit all of the synthetic span IDs to reassign them so that they are in ascending order, as required by libsyntax. Once we've done this renumbering, libsyntax is able to emit the comments in the correct location during the final rendering of the Rust AST.

    Named References

    The translator.named_references provides support for naming expressions that need to be able to be read or written two multiple times without reevaluating the expression. This module helps by identifying when a temporary variable will be needed to hold onto a reference so that it can support read and write operations.