From b87f6211f0db3e43f9a8109ac565215d6fb930ce Mon Sep 17 00:00:00 2001
From: Jakub Dupak <dev@jakubdupak.com>
Date: Sun, 29 Oct 2023 17:34:05 +0100
Subject: [PATCH] borrowck: Polonius FFI

gcc/rust/ChangeLog:

	* Make-lang.in: Build FFI-Polonius.
	* checks/errors/borrowck/rust-borrow-checker.cc (BorrowChecker::go): Invoke Polonius.
	* 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/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>
---
 gcc/rust/Make-lang.in                         |  19 ++-
 .../errors/borrowck/ffi-polonius/Cargo.toml   |  11 ++
 .../borrowck/ffi-polonius/src/gccrs_ffi.rs    | 115 ++++++++++++++++++
 .../errors/borrowck/ffi-polonius/src/lib.rs   |  96 +++++++++++++++
 .../borrowck/polonius/rust-polonius-ffi.h     |  98 +++++++++++++++
 .../errors/borrowck/polonius/rust-polonius.h  |  92 ++++++++++++++
 .../errors/borrowck/rust-borrow-checker.cc    |   4 +
 7 files changed, 432 insertions(+), 3 deletions(-)
 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/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 --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in
index facdd43fd518..8ccab85eb2f1 100644
--- a/gcc/rust/Make-lang.in
+++ b/gcc/rust/Make-lang.in
@@ -20,6 +20,7 @@
 
 # This file provides the language dependent support in the main Makefile.
 
+
 #RUST_EXES = rust
 
 # Use strict warnings for this front end.
@@ -199,14 +200,19 @@ RUST_ALL_OBJS = $(GRS_OBJS) $(RUST_TARGET_OBJS)
 
 rust_OBJS = $(RUST_ALL_OBJS) rust/rustspec.o
 
-RUST_LDFLAGS = $(LDFLAGS) -L./../libgrust/libproc_macro_internal
-RUST_LIBDEPS = $(LIBDEPS) ../libgrust/libproc_macro_internal/libproc_macro_internal.a
+# FIXME: Experiment - move to configure
+CLANG_TARGET := $(shell echo "$(target)" | sed 's/-pc-/-unknown-/')
+POLONIUS_LDFLAGS = $(shell cargo rustc --manifest-path $(srcdir)/rust/checks/errors/borrowck/ffi-polonius/Cargo.toml -q --message-format short -- --target $(CLANG_TARGET) --print=native-static-libs 2>&1 | grep "note: native-static-libs:" | sed 's/note: native-static-libs: //g')
+RUST_LDFLAGS = $(LDFLAGS) -L./../libgrust/libproc_macro_internal $(POLONIUS_LDFLAGS)
+RUST_LIBDEPS = $(LIBDEPS) ../libgrust/libproc_macro_internal/libproc_macro_internal.a rust/libffi_polonius.a
 
 # The compiler itself is called crab1
 crab1$(exeext): $(RUST_ALL_OBJS) attribs.o $(BACKEND) $(RUST_LIBDEPS) $(rust.prev)
 	@$(call LINK_PROGRESS,$(INDEX.rust),start)
 	+$(LLINKER) $(ALL_LINKERFLAGS) $(RUST_LDFLAGS) -o $@ \
-	      $(RUST_ALL_OBJS) attribs.o $(BACKEND) $(LIBS) ../libgrust/libproc_macro_internal/libproc_macro_internal.a $(BACKENDLIBS)
+	      $(RUST_ALL_OBJS) attribs.o $(BACKEND) \
+	      $(LIBS) ../libgrust/libproc_macro_internal/libproc_macro_internal.a rust/libffi_polonius.a \
+ 		  $(BACKENDLIBS)
 	@$(call LINK_PROGRESS,$(INDEX.rust),end)
 
 # Build hooks.
@@ -240,6 +246,7 @@ rust.srcman:
 # Clean hooks.
 
 rust.mostlyclean:
+	rm -rf rust/ffi-polonius/release libffi_polonius.a
 #	cd $(srcdir)/rust; rm -f *.o y.tab.h y.tab.c lex.yy.c
 
 rust.clean: rust.mostlyclean
@@ -461,3 +468,9 @@ rust/%.o: rust/checks/errors/borrowck/%.cc
 rust/%.o: rust/metadata/%.cc
 	$(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $<
 	$(POSTCOMPILE)
+
+rust/libffi_polonius.a: \
+	rust/checks/errors/borrowck/ffi-polonius/Cargo.toml \
+	$(wildcard $(srcdir)/rust/checks/errors/borrowck/ffi-polonius/src/*)
+	cargo build --manifest-path $(srcdir)/rust/checks/errors/borrowck/ffi-polonius/Cargo.toml --release --target-dir rust/ffi-polonius
+	cp rust/ffi-polonius/release/libffi_polonius.a rust/libffi_polonius.a
\ No newline at end of file
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 000000000000..71315c3b635e
--- /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 000000000000..4720150f8b03
--- /dev/null
+++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/src/gccrs_ffi.rs
@@ -0,0 +1,115 @@
+// Copyright (C) 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++
+// ```
+
+// GENERATED START ============================================================
+
+use crate::GccrsAtom;
+
+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: u64,
+    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>>,
+}
+
+// GENERATED END ==============================================================
+
+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/lib.rs b/gcc/rust/checks/errors/borrowck/ffi-polonius/src/lib.rs
new file mode 100644
index 000000000000..4d3f6b6d75b1
--- /dev/null
+++ b/gcc/rust/checks/errors/borrowck/ffi-polonius/src/lib.rs
@@ -0,0 +1,96 @@
+// Copyright (C) 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;
+
+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(),
+        }
+    }
+}
+
+/// 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);
+}
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 000000000000..75635804ccb8
--- /dev/null
+++ b/gcc/rust/checks/errors/borrowck/polonius/rust-polonius-ffi.h
@@ -0,0 +1,98 @@
+// Copyright (C) 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
+
+// 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.
+
+#include <cstdint>
+
+namespace Rust {
+namespace Polonius {
+
+using Origin = size_t;
+using Loan = size_t;
+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)
+  {}
+};
+
+template <typename T> struct Slice
+{
+  uint64_t len;
+  const T *const data;
+
+  template <typename vector>
+  Slice (const vector &v) : len (v.size ()), data (v.data ())
+  {}
+};
+
+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 000000000000..9959aeb99b61
--- /dev/null
+++ b/gcc/rust/checks/errors/borrowck/polonius/rust-polonius.h
@@ -0,0 +1,92 @@
+// Copyright (C) 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_H
+#define RUST_POLONIUS_H
+
+// Interface to the Polonius borrow checker engine.
+// See (https://github.com/rust-lang/polonius/blob/master/polonius-engine/)
+
+#include "rust-polonius-ffi.h"
+
+namespace Rust {
+namespace Polonius {
+
+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};
+  }
+};
+
+/**
+ * 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 */
diff --git a/gcc/rust/checks/errors/borrowck/rust-borrow-checker.cc b/gcc/rust/checks/errors/borrowck/rust-borrow-checker.cc
index 0c952ad32fa5..9a1865f3a252 100644
--- a/gcc/rust/checks/errors/borrowck/rust-borrow-checker.cc
+++ b/gcc/rust/checks/errors/borrowck/rust-borrow-checker.cc
@@ -20,6 +20,7 @@
 #include "rust-function-collector.h"
 #include "rust-bir-builder.h"
 #include "rust-bir-dump.h"
+#include "polonius/rust-polonius.h"
 
 namespace Rust {
 namespace HIR {
@@ -86,6 +87,9 @@ BorrowChecker::go (HIR::Crate &crate)
 	  dump_function_bir (filename, bir,
 			     func->get_function_name ().as_string ());
 	}
+
+      Polonius::Facts facts; // Dummy facts for now.
+      Polonius::polonius_run (facts.freeze (), true);
     }
 
   for (auto closure ATTRIBUTE_UNUSED : collector.get_closures ())