diff mbox series

[117/125] gccrs: borrowck: Polonius FFI

Message ID 20240801145809.366388-119-arthur.cohen@embecosm.com
State New
Headers show
Series [001/125] Rust: Make 'tree'-level 'MAIN_NAME_P' work | expand

Commit Message

Arthur Cohen Aug. 1, 2024, 2:57 p.m. UTC
From: Jakub Dupak <dev@jakubdupak.com>

Rust part is not build and not invoked at this point.

gcc/rust/ChangeLog:

	* checks/errors/borrowck/ffi-polonius/Cargo.toml: New file.
	* checks/errors/borrowck/ffi-polonius/src/gccrs_ffi.rs: New file.
	* checks/errors/borrowck/ffi-polonius/src/gccrs_ffi_generated.rs: New file.
	* checks/errors/borrowck/ffi-polonius/src/lib.rs: New file.
	* checks/errors/borrowck/polonius/rust-polonius-ffi.h: New file.
	* checks/errors/borrowck/polonius/rust-polonius.h: New file.

Signed-off-by: Jakub Dupak <dev@jakubdupak.com>
---
 .../errors/borrowck/ffi-polonius/Cargo.toml   |  11 +
 .../borrowck/ffi-polonius/src/gccrs_ffi.rs    |  61 +++++
 .../ffi-polonius/src/gccrs_ffi_generated.rs   |  50 ++++
 .../errors/borrowck/ffi-polonius/src/lib.rs   | 133 ++++++++++
 .../borrowck/polonius/rust-polonius-ffi.h     | 109 ++++++++
 .../errors/borrowck/polonius/rust-polonius.h  | 232 ++++++++++++++++++
 6 files changed, 596 insertions(+)
 create mode 100644 gcc/rust/checks/errors/borrowck/ffi-polonius/Cargo.toml
 create mode 100644 gcc/rust/checks/errors/borrowck/ffi-polonius/src/gccrs_ffi.rs
 create mode 100644 gcc/rust/checks/errors/borrowck/ffi-polonius/src/gccrs_ffi_generated.rs
 create mode 100644 gcc/rust/checks/errors/borrowck/ffi-polonius/src/lib.rs
 create mode 100644 gcc/rust/checks/errors/borrowck/polonius/rust-polonius-ffi.h
 create mode 100644 gcc/rust/checks/errors/borrowck/polonius/rust-polonius.h
diff mbox series

Patch

diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/Cargo.toml b/gcc/rust/checks/errors/borrowck/ffi-polonius/Cargo.toml
new file mode 100644
index 00000000000..71315c3b635
--- /dev/null
+++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/Cargo.toml
@@ -0,0 +1,11 @@ 
+[package]
+name = "ffi-polonius"
+version = "0.1.0"
+edition = "2021"
+license = "GPL-3"
+
+[lib]
+crate-type = ["staticlib"]
+
+[dependencies]
+polonius-engine = "0.13.0"
\ No newline at end of file
diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/src/gccrs_ffi.rs b/gcc/rust/checks/errors/borrowck/ffi-polonius/src/gccrs_ffi.rs
new file mode 100644
index 00000000000..3a849444bbc
--- /dev/null
+++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/src/gccrs_ffi.rs
@@ -0,0 +1,61 @@ 
+// Copyright (C) 2020-2023 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// This is an FFI interface to gccrs (c++).
+// It is a counterpart to `rust-polonius-ffi.h`.
+
+// Generated by rust-bindgen, remove unsafe phantoms and add Into impls.
+// ```shell
+// bindgen \
+//  --generate types \
+//  --allowlist-file rust-polonius-facts-ffi.h
+//  --no-layout-tests
+//  rust-polonius-ffi.h
+//  -- -x c++
+// ```
+include!("gccrs_ffi_generated.rs");
+
+use crate::GccrsAtom;
+
+impl<T1, T2> Into<(GccrsAtom, GccrsAtom)> for Pair<T1, T2>
+where
+    GccrsAtom: From<T1> + From<T2>,
+{
+    fn into(self) -> (GccrsAtom, GccrsAtom) {
+        (self.first.into(), self.second.into())
+    }
+}
+
+impl<T1, T2, T3> Into<(GccrsAtom, GccrsAtom, GccrsAtom)> for Triple<T1, T2, T3>
+where
+    GccrsAtom: From<T1> + From<T2> + From<T3>,
+{
+    fn into(self) -> (GccrsAtom, GccrsAtom, GccrsAtom) {
+        (self.first.into(), self.second.into(), self.third.into())
+    }
+}
+
+impl<OUT, IN> Into<Vec<OUT>> for Slice<IN>
+where
+    IN: Into<OUT> + Copy,
+{
+    fn into(self) -> Vec<OUT> {
+        let slice = unsafe { std::slice::from_raw_parts(self.data, self.len as usize) };
+        slice.iter().map(|&e| e.into()).collect()
+    }
+}
diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/src/gccrs_ffi_generated.rs b/gcc/rust/checks/errors/borrowck/ffi-polonius/src/gccrs_ffi_generated.rs
new file mode 100644
index 00000000000..20908179528
--- /dev/null
+++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/src/gccrs_ffi_generated.rs
@@ -0,0 +1,50 @@ 
+pub type Origin = usize;
+pub type Loan = usize;
+pub type Point = usize;
+pub type Variable = usize;
+pub type Path = usize;
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct Pair<T1, T2> {
+    pub first: T1,
+    pub second: T2,
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct Triple<T1, T2, T3> {
+    pub first: T1,
+    pub second: T2,
+    pub third: T3,
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct Slice<T> {
+    pub len: usize,
+    pub data: *const T,
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct FactsView {
+    pub loan_issued_at: Slice<Triple<Origin, Loan, Point>>,
+    pub universal_region: Slice<Origin>,
+    pub cfg_edge: Slice<Pair<Point, Point>>,
+    pub loan_killed_at: Slice<Pair<Loan, Point>>,
+    pub subset_base: Slice<Triple<Origin, Origin, Point>>,
+    pub loan_invalidated_at: Slice<Pair<Point, Loan>>,
+    pub var_used_at: Slice<Pair<Variable, Point>>,
+    pub var_defined_at: Slice<Pair<Variable, Point>>,
+    pub var_dropped_at: Slice<Pair<Variable, Point>>,
+    pub use_of_var_derefs_origin: Slice<Pair<Variable, Origin>>,
+    pub drop_of_var_derefs_origin: Slice<Pair<Variable, Origin>>,
+    pub child_path: Slice<Pair<Path, Path>>,
+    pub path_is_var: Slice<Pair<Path, Variable>>,
+    pub path_assigned_at_base: Slice<Pair<Path, Point>>,
+    pub path_moved_at_base: Slice<Pair<Path, Point>>,
+    pub path_accessed_at_base: Slice<Pair<Path, Point>>,
+    pub known_placeholder_subset: Slice<Pair<Origin, Origin>>,
+    pub placeholder: Slice<Pair<Origin, Loan>>,
+}
diff --git a/gcc/rust/checks/errors/borrowck/ffi-polonius/src/lib.rs b/gcc/rust/checks/errors/borrowck/ffi-polonius/src/lib.rs
new file mode 100644
index 00000000000..a9aa8106b69
--- /dev/null
+++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/src/lib.rs
@@ -0,0 +1,133 @@ 
+// Copyright (C) 2020-2023 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+mod gccrs_ffi;
+mod gccrs_ffi_generated;
+
+use polonius_engine::{AllFacts, Atom, FactTypes, Output};
+use std::fmt::Debug;
+use std::hash::Hash;
+
+/// A single fact value.
+/// For simplicity we use one type for all facts.
+#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+struct GccrsAtom(usize);
+
+impl Atom for GccrsAtom {
+    fn index(self) -> usize {
+        self.0
+    }
+}
+
+impl From<usize> for GccrsAtom {
+    fn from(inner: usize) -> GccrsAtom {
+        GccrsAtom(inner)
+    }
+}
+
+impl From<GccrsAtom> for usize {
+    fn from(atom: GccrsAtom) -> Self {
+        atom.index()
+    }
+}
+
+#[derive(Debug, Clone, Copy, Default)]
+struct GccrsFacts;
+
+impl FactTypes for GccrsFacts {
+    type Origin = GccrsAtom;
+    type Loan = GccrsAtom;
+    type Point = GccrsAtom;
+    type Variable = GccrsAtom;
+    type Path = GccrsAtom;
+}
+
+impl From<gccrs_ffi::FactsView> for AllFacts<GccrsFacts> {
+    fn from(input: gccrs_ffi::FactsView) -> Self {
+        AllFacts::<GccrsFacts> {
+            loan_issued_at: input.loan_issued_at.into(),
+            universal_region: input.universal_region.into(),
+            cfg_edge: input.cfg_edge.into(),
+            loan_killed_at: input.loan_killed_at.into(),
+            subset_base: input.subset_base.into(),
+            loan_invalidated_at: input.loan_invalidated_at.into(),
+            var_used_at: input.var_used_at.into(),
+            var_defined_at: input.var_defined_at.into(),
+            var_dropped_at: input.var_dropped_at.into(),
+            use_of_var_derefs_origin: input.use_of_var_derefs_origin.into(),
+            drop_of_var_derefs_origin: input.drop_of_var_derefs_origin.into(),
+            child_path: input.child_path.into(),
+            path_is_var: input.path_is_var.into(),
+            path_assigned_at_base: input.path_assigned_at_base.into(),
+            path_moved_at_base: input.path_moved_at_base.into(),
+            path_accessed_at_base: input.path_accessed_at_base.into(),
+            known_placeholder_subset: input.known_placeholder_subset.into(),
+            placeholder: input.placeholder.into(),
+        }
+    }
+}
+
+fn print_point(point: GccrsAtom) {
+    let val: usize = point.into();
+    let mid = val % 2 == 1;
+    let bb = val >> 16;
+    let stmt = (val >> 1) & 0xFFFF;
+    print!("{}(bb{}[{}])", if mid { "Mid" } else { "Start" }, bb, stmt);
+}
+
+/// Run the polonius analysis on the given facts (for a single function).
+/// Right now, results are only printed and not propagated back to the gccrs.
+#[no_mangle]
+pub unsafe extern "C" fn polonius_run(input: gccrs_ffi::FactsView, dump_enabled: bool) {
+    let facts = AllFacts::<GccrsFacts>::from(input);
+    let output = Output::compute(&facts, polonius_engine::Algorithm::Naive, dump_enabled);
+
+    // FIXME: Temporary output
+    println!("Polonius analysis completed. Results:");
+    println!("Errors: {:#?}", output.errors);
+    println!("Subset error: {:#?}", output.subset_errors);
+    println!("Move error: {:#?}", output.move_errors);
+
+    println!("Subsets:");
+    let mut subset_vec: Vec<_> = output.subset.iter().collect();
+    subset_vec.sort_by_key(|&(point, _)| point);
+    for (point, subsets) in subset_vec {
+        print_point(*point);
+        println!(": {{");
+        for (&lhs, rhss) in subsets {
+            for &rhs in rhss {
+                println!("    {} <= {}", usize::from(lhs), usize::from(rhs));
+            }
+        }
+        println!("}}");
+    }
+    println!("Subset anywhere: {:#?}", output.subset_anywhere);
+
+    // Print origin live on entry
+    println!("Origin live on entry:");
+    let mut origin_vec: Vec<_> = output.origin_live_on_entry.iter().collect();
+    origin_vec.sort_by_key(|&(point, _)| point);
+    for (point, origins) in origin_vec {
+        print_point(*point);
+        println!(": {{");
+        for &origin in origins {
+            println!("    {}", usize::from(origin));
+        }
+        println!("}}");
+    }
+}
diff --git a/gcc/rust/checks/errors/borrowck/polonius/rust-polonius-ffi.h b/gcc/rust/checks/errors/borrowck/polonius/rust-polonius-ffi.h
new file mode 100644
index 00000000000..500d25a3222
--- /dev/null
+++ b/gcc/rust/checks/errors/borrowck/polonius/rust-polonius-ffi.h
@@ -0,0 +1,109 @@ 
+// Copyright (C) 2020-2023 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_POLONIUS_FACTS_FFI_H
+#define RUST_POLONIUS_FACTS_FFI_H
+
+#include "rust-system.h"
+
+// This file defines the C++ side of the FFI interface to Polonius.
+// The corresponding Rust side is in `gccrs-ffi.rs`.
+
+// IMPORTANT:
+// This file intentionally does not include any C++ headers
+// to allow seamless binding generation on the Rust side.
+
+namespace Rust {
+namespace Polonius {
+
+using Origin = size_t;
+using Loan = size_t;
+/**
+ * Compressed CFG point
+ * Encoding:
+ *  - (bit size - 16) bits: basic block index
+ *  - 15 bits: stmt index inside basic block
+ *  - 1bit: start or mid
+ *
+ * Note: Polonius requires the holding type to be `size_t`/`usize`.
+ */
+using Point = size_t;
+using Variable = size_t;
+using Path = size_t;
+
+namespace FFI {
+
+// NOTE: std::pair and std::tuple are complicating the bindings' generation.
+template <typename T1, typename T2> struct Pair
+{
+  T1 first;
+  T2 second;
+
+  Pair (T1 first, T2 second) : first (first), second (second) {}
+};
+
+template <typename T1, typename T2, typename T3> struct Triple
+{
+  T1 first;
+  T2 second;
+  T3 third;
+
+  Triple (T1 first, T2 second, T3 third)
+    : first (first), second (second), third (third)
+  {}
+};
+
+/** Frozen variant to vector for FFI */
+template <typename T> struct Slice
+{
+  size_t len;
+  const T *const data;
+
+  template <typename vector>
+  Slice (const vector &v) : len (v.size ()), data (v.data ())
+  {}
+};
+
+/** Frozen variant of the facts with C ABI and no methods. */
+struct FactsView
+{
+  Slice<Triple<Origin, Loan, Point>> loan_issued_at;
+  Slice<Origin> universal_region;
+  Slice<Pair<Point, Point>> cfg_edge;
+  Slice<Pair<Loan, Point>> loan_killed_at;
+  Slice<Triple<Origin, Origin, Point>> subset_base;
+  Slice<Pair<Point, Loan>> loan_invalidated_at;
+  Slice<Pair<Variable, Point>> var_used_at;
+  Slice<Pair<Variable, Point>> var_defined_at;
+  Slice<Pair<Variable, Point>> var_dropped_at;
+  Slice<Pair<Variable, Origin>> use_of_var_derefs_origin;
+  Slice<Pair<Variable, Origin>> drop_of_var_derefs_origin;
+  Slice<Pair<Path, Path>> child_path;
+  Slice<Pair<Path, Variable>> path_is_var;
+  Slice<Pair<Path, Point>> path_assigned_at_base;
+  Slice<Pair<Path, Point>> path_moved_at_base;
+  Slice<Pair<Path, Point>> path_accessed_at_base;
+  Slice<Pair<Origin, Origin>> known_placeholder_subset;
+  Slice<Pair<Origin, Loan>> placeholder;
+};
+
+} // namespace FFI
+} // namespace Polonius
+} // namespace Rust
+
+#endif // RUST_POLONIUS_FACTS_FFI_H
diff --git a/gcc/rust/checks/errors/borrowck/polonius/rust-polonius.h b/gcc/rust/checks/errors/borrowck/polonius/rust-polonius.h
new file mode 100644
index 00000000000..df746dd0c76
--- /dev/null
+++ b/gcc/rust/checks/errors/borrowck/polonius/rust-polonius.h
@@ -0,0 +1,232 @@ 
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_POLONIUS_H
+#define RUST_POLONIUS_H
+
+// Interface to the Polonius borrow checker engine.
+// See (https://github.com/rust-lang/polonius/blob/master/polonius-engine/)
+
+#include <ostream>
+#include "rust-polonius-ffi.h"
+
+namespace Rust {
+namespace Polonius {
+
+/** A point in the control flow graph. */
+struct FullPoint
+{
+  uint32_t bb;
+  uint32_t stmt;
+  bool mid;
+
+  /** Expands a compressed `Point` into its components.
+   * See `Point` docs for encoding details.
+   */
+  explicit FullPoint (Point point)
+    : bb (extract_bb (point)), stmt (extract_stmt (point)),
+      mid (extract_mid (point))
+  {}
+
+  static uint32_t extract_bb (Point point) { return point >> 16; }
+  static uint32_t extract_stmt (Point point)
+  {
+    return (point & ~(1 << 16)) >> 1;
+  }
+  static bool extract_mid (Point point) { return point & 1; }
+
+  friend std::ostream &operator<< (std::ostream &os, const FullPoint &point)
+  {
+    return os << ((point.mid) ? "Mid" : "Start") << "(bb" << point.bb << "["
+	      << point.stmt << "])";
+  }
+};
+
+struct Facts
+{
+  // See (https://rust-lang.github.io/polonius/rules/relations.html)
+  std::vector<FFI::Triple<Origin, Loan, Point>> loan_issued_at;
+  std::vector<Origin> universal_region;
+  std::vector<FFI::Pair<Point, Point>> cfg_edge;
+  std::vector<FFI::Pair<Loan, Point>> loan_killed_at;
+  std::vector<FFI::Triple<Origin, Origin, Point>> subset_base;
+  std::vector<FFI::Pair<Point, Loan>> loan_invalidated_at;
+  std::vector<FFI::Pair<Variable, Point>> var_used_at;
+  std::vector<FFI::Pair<Variable, Point>> var_defined_at;
+  std::vector<FFI::Pair<Variable, Point>> var_dropped_at;
+  std::vector<FFI::Pair<Variable, Origin>> use_of_var_derefs_origin;
+  std::vector<FFI::Pair<Variable, Origin>> drop_of_var_derefs_origin;
+  std::vector<FFI::Pair<Path, Path>> child_path;
+  std::vector<FFI::Pair<Path, Variable>> path_is_var;
+  std::vector<FFI::Pair<Path, Point>> path_assigned_at_base;
+  std::vector<FFI::Pair<Path, Point>> path_moved_at_base;
+  std::vector<FFI::Pair<Path, Point>> path_accessed_at_base;
+  std::vector<FFI::Pair<Origin, Origin>> known_placeholder_subset;
+  std::vector<FFI::Pair<Origin, Loan>> placeholder;
+
+  /**
+   * Create a const view for the struct for FFI.
+   *
+   * This view uses the original vector storage.
+   * Therefore any resizing operation of Facts member may invalidate the view.
+   */
+  FFI::FactsView freeze ()
+  {
+    return FFI::FactsView{loan_issued_at,
+			  universal_region,
+			  cfg_edge,
+			  loan_killed_at,
+			  subset_base,
+			  loan_invalidated_at,
+			  var_used_at,
+			  var_defined_at,
+			  var_dropped_at,
+			  use_of_var_derefs_origin,
+			  drop_of_var_derefs_origin,
+			  child_path,
+			  path_is_var,
+			  path_assigned_at_base,
+			  path_moved_at_base,
+			  path_accessed_at_base,
+			  known_placeholder_subset,
+			  placeholder};
+  }
+
+  void dump_loan_issued_at (std::ostream &os) const
+  {
+    for (auto &e : loan_issued_at)
+      os << "\"'?" << e.first << "\"\t\"bw" << e.second << "\"\t\""
+	 << FullPoint (e.third) << "\"\n";
+  }
+
+  void dump_universal_region (std::ostream &os) const
+  {
+    for (auto &e : universal_region)
+      os << e << "\n";
+  }
+
+  void dump_cfg_edge (std::ostream &os) const
+  {
+    for (auto &e : cfg_edge)
+      os << FullPoint (e.first) << " " << FullPoint (e.second) << "\n";
+  }
+
+  void dump_loan_killed_at (std::ostream &os) const
+  {
+    for (auto &e : loan_killed_at)
+      os << e.first << " " << FullPoint (e.second) << "\n";
+  }
+
+  void dump_subset_base (std::ostream &os) const
+  {
+    for (auto &e : subset_base)
+      os << "\"'?" << e.first << "\"\t\"'?" << e.second << "\"\t\""
+	 << FullPoint (e.third) << "\"\n";
+  }
+
+  void dump_loan_invalidated_at (std::ostream &os) const
+  {
+    for (auto &e : loan_invalidated_at)
+      os << FullPoint (e.first) << " " << e.second << "\n";
+  }
+
+  void dump_var_used_at (std::ostream &os) const
+  {
+    for (auto &e : var_used_at)
+      os << e.first - 1 << " " << FullPoint (e.second) << "\n";
+  }
+
+  void dump_var_defined_at (std::ostream &os) const
+  {
+    for (auto &e : var_defined_at)
+      os << e.first << " " << FullPoint (e.second) << "\n";
+  }
+
+  void dump_var_dropped_at (std::ostream &os) const
+  {
+    for (auto &e : var_dropped_at)
+      os << e.first << " " << FullPoint (e.second) << "\n";
+  }
+
+  void dump_use_of_var_derefs_origin (std::ostream &os) const
+  {
+    for (auto &e : use_of_var_derefs_origin)
+      os << e.first << " " << e.second << "\n";
+  }
+
+  void dump_drop_of_var_derefs_origin (std::ostream &os) const
+  {
+    for (auto &e : drop_of_var_derefs_origin)
+      os << e.first << " " << e.second << "\n";
+  }
+
+  void dump_child_path (std::ostream &os) const
+  {
+    for (auto &e : child_path)
+      os << e.first << " " << e.second << "\n";
+  }
+
+  void dump_path_is_var (std::ostream &os) const
+  {
+    for (auto &e : path_is_var)
+      os << e.first << " " << e.second << "\n";
+  }
+
+  void dump_path_assigned_at_base (std::ostream &os) const
+  {
+    for (auto &e : path_assigned_at_base)
+      os << e.first << " " << FullPoint (e.second) << "\n";
+  }
+
+  void dump_path_moved_at_base (std::ostream &os) const
+  {
+    for (auto &e : path_moved_at_base)
+      os << e.first << " " << FullPoint (e.second) << "\n";
+  }
+
+  void dump_path_accessed_at_base (std::ostream &os) const
+  {
+    for (auto &e : path_accessed_at_base)
+      os << e.first << " " << FullPoint (e.second) << "\n";
+  }
+
+  void dump_known_placeholder_subset (std::ostream &os) const
+  {
+    for (auto &e : known_placeholder_subset)
+      os << e.first << " " << e.second << "\n";
+  }
+
+  void dump_placeholder (std::ostream &os) const
+  {
+    for (auto &e : placeholder)
+      os << e.first << " " << e.second << "\n";
+  }
+};
+
+/**
+ * Check a single function for borrow errors.
+ *
+ * Output is not yet implemented and is only dumped to stdout.
+ */
+extern "C" void
+polonius_run (FFI::FactsView input, bool dump_enabled);
+
+} // namespace Polonius
+} // namespace Rust
+
+#endif /* !RUST_POLONIUS_H */