public inbox for gcc-rust@gcc.gnu.org
 help / color / mirror / Atom feed
* Rust frontend patches v2
@ 2022-08-24 11:59 herron.philip
  2022-08-24 11:59 ` [PATCH Rust front-end v2 01/37] Use DW_ATE_UTF for the Rust 'char' type herron.philip
                   ` (37 more replies)
  0 siblings, 38 replies; 59+ messages in thread
From: herron.philip @ 2022-08-24 11:59 UTC (permalink / raw)
  To: gcc-patches; +Cc: gcc-rust

This is the 2nd patch set for gccrs, since v1 we have dropped the changes
for target hooks which are not nessecary for us right now. This now
focuses directly on the front-end the only patch that affects GCC now is a
tweak to debug info. Note we are close to merging our port of the C++
constexpr code into our front-end but this patch set does not include this
yet.

Thanks to Open Source Security, inc and Embecosm for sponsoring this work.
Special thanks to all of those who have contributed thus far.

See our branch over on https://gcc.gnu.org/git/?p=gcc.git;a=shortlog;h=refs/heads/devel/rust/master

We are currently testing on every commit the following systems:

- Debian i386 - all tests passing
- Debian testing-x86_64 - all tests passing
- Fedora arm64 - all tests passing
- Fedora X86_64 - all tests passing
- OpenSUSE Leap X86_64 - all tests passing
- OpenSUSE tw X86_64 - all tests passing
- Rawhide X86_64 - all tests passing
- macos x86_64 - all tests passing
- Debian ppc64 - some tests failing
- Fedora ppc64le - some tests failing
- Fedora s390x - some tests failing

The patch set is as follows:

[PATCH Rust front-end v2 01/37] Use DW_ATE_UTF for the Rust 'char'
[PATCH Rust front-end v2 02/37] gccrs: Add nessecary hooks for a Rust
[PATCH Rust front-end v2 03/37] gccrs: Add Debug info testsuite
[PATCH Rust front-end v2 04/37] gccrs: Add link cases testsuite
[PATCH Rust front-end v2 05/37] gccrs: Add general compilation test
[PATCH Rust front-end v2 06/37] gccrs: Add execution test cases
[PATCH Rust front-end v2 07/37] gccrs: Add gcc-check-target
[PATCH Rust front-end v2 08/37] gccrs: Add the Rust front-end AST
[PATCH Rust front-end v2 09/37] gccrs: Add Lexer for Rust front-end
[PATCH Rust front-end v2 10/37] gccrs: Add Parser for Rust front-end
[PATCH Rust front-end v2 11/37] gccrs: Add expansion pass for the
[PATCH Rust front-end v2 12/37] gccrs: Add name resolution pass to
[PATCH Rust front-end v2 13/37] gccrs: Add second intermedite
[PATCH Rust front-end v2 14/37] gccrs: Add AST to HIR lowering pass
[PATCH Rust front-end v2 15/37] gccrs: Add wrapper for make_unique
[PATCH Rust front-end v2 16/37] gccrs: Add port of FNV hash used
[PATCH Rust front-end v2 17/37] gccrs: Add Rust ABI enum helpers
[PATCH Rust front-end v2 18/37] gccrs: Add Base62 implementation
[PATCH Rust front-end v2 19/37] gccrs: Add implementation of Optional
[PATCH Rust front-end v2 20/37] gccrs: Add attributes checker
[PATCH Rust front-end v2 21/37] gccrs: Add helpers mappings canonical
[PATCH Rust front-end v2 22/37] gccrs: Add type resolution and trait
[PATCH Rust front-end v2 23/37] gccrs: Add unsafe checks for Rust
[PATCH Rust front-end v2 24/37] gccrs: Add const checker
[PATCH Rust front-end v2 25/37] gccrs: Add privacy checks
[PATCH Rust front-end v2 26/37] gccrs: Add dead code scan on HIR
[PATCH Rust front-end v2 27/37] gccrs: Add unused variable scan
[PATCH Rust front-end v2 28/37] gccrs: Add metadata ouptput pass
[PATCH Rust front-end v2 29/37] gccrs: HIR to GCC GENERIC lowering
[PATCH Rust front-end v2 30/37] gccrs: These are wrappers ported from
[PATCH Rust front-end v2 31/37] gccrs: Add GCC Rust front-end
[PATCH Rust front-end v2 32/37] gccrs: Add config-lang.in
[PATCH Rust front-end v2 33/37] gccrs: add lang-spec.h
[PATCH Rust front-end v2 34/37] gccrs: add lang.opt
[PATCH Rust front-end v2 35/37] gccrs: add compiler driver
[PATCH Rust front-end v2 36/37] gccrs: compiler proper interface
[PATCH Rust front-end v2 37/37] gccrs: Add README, CONTRIBUTING and


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [PATCH Rust front-end v2 01/37] Use DW_ATE_UTF for the Rust 'char' type
  2022-08-24 11:59 Rust frontend patches v2 herron.philip
@ 2022-08-24 11:59 ` herron.philip
  2022-08-24 14:28   ` Jason Merrill
  2022-08-24 11:59 ` [PATCH Rust front-end v2 02/37] gccrs: Add nessecary hooks for a Rust front-end testsuite herron.philip
                   ` (36 subsequent siblings)
  37 siblings, 1 reply; 59+ messages in thread
From: herron.philip @ 2022-08-24 11:59 UTC (permalink / raw)
  To: gcc-patches; +Cc: gcc-rust, Tom Tromey

From: Tom Tromey <tom@tromey.com>

The Rust 'char' type should use the DWARF DW_ATE_UTF encoding.
---
 gcc/dwarf2out.cc | 23 ++++++++++++++++++++++-
 1 file changed, 22 insertions(+), 1 deletion(-)

diff --git a/gcc/dwarf2out.cc b/gcc/dwarf2out.cc
index e3920c898f5..a8bccbabca4 100644
--- a/gcc/dwarf2out.cc
+++ b/gcc/dwarf2out.cc
@@ -5600,6 +5600,16 @@ is_fortran (const_tree decl)
   return is_fortran ();
 }
 
+/* Return TRUE if the language is Rust.  */
+
+static inline bool
+is_rust ()
+{
+  unsigned int lang = get_AT_unsigned (comp_unit_die (), DW_AT_language);
+
+  return lang == DW_LANG_Rust || lang == DW_LANG_Rust_old;
+}
+
 /* Return TRUE if the language is Ada.  */
 
 static inline bool
@@ -13231,7 +13241,11 @@ base_type_die (tree type, bool reverse)
 	}
       if (TYPE_STRING_FLAG (type))
 	{
-	  if (TYPE_UNSIGNED (type))
+	  if ((dwarf_version >= 4 || !dwarf_strict)
+	      && is_rust ()
+	      && int_size_in_bytes (type) == 4)
+	    encoding = DW_ATE_UTF;
+	  else if (TYPE_UNSIGNED (type))
 	    encoding = DW_ATE_unsigned_char;
 	  else
 	    encoding = DW_ATE_signed_char;
@@ -25201,6 +25215,13 @@ gen_compile_unit_die (const char *filename)
     }
   else if (strcmp (language_string, "GNU F77") == 0)
     language = DW_LANG_Fortran77;
+  else if (strcmp (language_string, "GNU Rust") == 0)
+    {
+      if (dwarf_version >= 5 || !dwarf_strict)
+	language = DW_LANG_Rust;
+      else
+	language = DW_LANG_Rust_old;
+    }
   else if (dwarf_version >= 3 || !dwarf_strict)
     {
       if (strcmp (language_string, "GNU Ada") == 0)
-- 
2.25.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [PATCH Rust front-end v2 02/37] gccrs: Add nessecary hooks for a Rust front-end testsuite
  2022-08-24 11:59 Rust frontend patches v2 herron.philip
  2022-08-24 11:59 ` [PATCH Rust front-end v2 01/37] Use DW_ATE_UTF for the Rust 'char' type herron.philip
@ 2022-08-24 11:59 ` herron.philip
  2022-09-10  4:05   ` Mike Stump
  2022-08-24 11:59 ` [PATCH Rust front-end v2 03/37] gccrs: Add Debug info testsuite herron.philip
                   ` (35 subsequent siblings)
  37 siblings, 1 reply; 59+ messages in thread
From: herron.philip @ 2022-08-24 11:59 UTC (permalink / raw)
  To: gcc-patches; +Cc: gcc-rust, Philip Herron, Marc Poulhiès, Thomas Schwinge

From: Philip Herron <philip.herron@embecosm.com>

This copy's over code from other front-end testsuites to enable testing
for the rust front-end specifically.

Co-authored-by: Marc Poulhiès <dkm@kataplop.net>
Co-authored-by: Thomas Schwinge <thomas@codesourcery.com>
---
 gcc/testsuite/lib/rust-dg.exp |  49 +++++++++
 gcc/testsuite/lib/rust.exp    | 186 ++++++++++++++++++++++++++++++++++
 2 files changed, 235 insertions(+)
 create mode 100644 gcc/testsuite/lib/rust-dg.exp
 create mode 100644 gcc/testsuite/lib/rust.exp

diff --git a/gcc/testsuite/lib/rust-dg.exp b/gcc/testsuite/lib/rust-dg.exp
new file mode 100644
index 00000000000..a8a2ac0c8eb
--- /dev/null
+++ b/gcc/testsuite/lib/rust-dg.exp
@@ -0,0 +1,49 @@
+# Copyright (C) 1997-2022 Free Software Foundation, Inc.
+
+# This program 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 of the License, or
+# (at your option) any later version.
+#
+# This program 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/>.
+
+load_lib gcc-dg.exp
+
+# Define rust callbacks for dg.exp.
+
+proc rust-dg-test { prog do_what extra_tool_flags } {
+    return [gcc-dg-test-1 rust_target_compile $prog $do_what $extra_tool_flags]
+}
+
+proc rust-dg-prune { system text } {
+    return [gcc-dg-prune $system $text]
+}
+
+# Utility routines.
+
+#
+# rust_load -- wrapper around default rust_load to handle tests that
+# require program arguments passed to them.
+#
+
+if { [info procs rust_load] != [list] \
+      && [info procs prev_rust_load] == [list] } {
+    rename rust_load prev_rust_load
+
+    proc rust_load { program args } {
+	global RUST_EXECUTE_ARGS
+	if [info exists RUST_EXECUTE_ARGS] then {
+	    set args [concat "{$RUST_EXECUTE_ARGS}"]
+	}
+	set result [eval [list prev_rust_load $program] $args ]
+	return $result
+    }
+}
+
diff --git a/gcc/testsuite/lib/rust.exp b/gcc/testsuite/lib/rust.exp
new file mode 100644
index 00000000000..6993c976304
--- /dev/null
+++ b/gcc/testsuite/lib/rust.exp
@@ -0,0 +1,186 @@
+# Copyright (C) 2012-2022 Free Software Foundation, Inc.
+
+# This program 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 of the License, or
+# (at your option) any later version.
+#
+# This program 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/>.
+
+#
+# rust support library routines
+#
+
+load_lib prune.exp
+load_lib gcc-defs.exp
+load_lib timeout.exp
+load_lib target-libpath.exp
+
+#
+# RUST_UNDER_TEST is the compiler under test.
+#
+
+set rust_compile_options ""
+
+
+#
+# rust_include_flags -- include flags for the gcc tree structure
+#
+
+proc rust_include_flags { paths } {
+    global srcdir
+    global TESTING_IN_BUILD_TREE
+
+    set flags ""
+
+    if { [is_remote host] || ![info exists TESTING_IN_BUILD_TREE] } {
+	return "${flags}"
+    }
+
+    set gccpath ${paths}
+
+    return "$flags"
+}
+
+#
+# rust_link_flags -- linker flags for the gcc tree structure
+#
+
+proc rust_link_flags { paths } {
+    global srcdir
+    global ld_library_path
+    global RUST_UNDER_TEST
+    global shlib_ext
+    global SHARED_OPTION
+
+    set gccpath ${paths}
+    set libio_dir ""
+    set flags ""
+    set ld_library_path "."
+    set shlib_ext [get_shlib_extension]
+    set SHARED_OPTION ""
+    verbose "shared lib extension: $shlib_ext"
+
+    set_ld_library_path_env_vars
+
+    return "$flags"
+}
+
+#
+# rust_init -- called at the start of each subdir of tests
+#
+
+proc rust_init { args } {
+    global subdir
+    global rust_initialized
+    global base_dir
+    global tmpdir
+    global libdir
+    global gluefile wrap_flags
+    global objdir srcdir
+    global ALWAYS_RUSTFLAGS
+    global TOOL_EXECUTABLE TOOL_OPTIONS
+    global RUST_UNDER_TEST
+    global TESTING_IN_BUILD_TREE
+    global TEST_ALWAYS_FLAGS
+    global gcc_warning_prefix
+    global gcc_error_prefix
+
+    # We set LC_ALL and LANG to C so that we get the same error messages as expected.
+    setenv LC_ALL C
+    setenv LANG C
+
+    if ![info exists RUST_UNDER_TEST] then {
+	if [info exists TOOL_EXECUTABLE] {
+	    set RUST_UNDER_TEST $TOOL_EXECUTABLE
+	} else {
+	    if { [is_remote host] || ! [info exists TESTING_IN_BUILD_TREE] } {
+		set RUST_UNDER_TEST [transform gccrs]
+	    } else {
+		set RUST_UNDER_TEST [findfile $base_dir/../../gccrs "$base_dir/../../gccrs -B$base_dir/../../" [findfile $base_dir/gccrs "$base_dir/gccrs -B$base_dir/" [transform gccrs]]]
+	    }
+	}
+    }
+
+    if ![is_remote host] {
+	if { [which $RUST_UNDER_TEST] == 0 } then {
+	    perror "RUST_UNDER_TEST ($RUST_UNDER_TEST) does not exist"
+	    exit 1
+	}
+    }
+
+    if ![info exists tmpdir] {
+	set tmpdir "/tmp"
+    }
+
+    if [info exists gluefile] {
+	unset gluefile
+    }
+
+    rust_maybe_build_wrapper "${tmpdir}/rust-testglue.o"
+
+    set ALWAYS_RUSTFLAGS ""
+
+    # TEST_ALWAYS_FLAGS are flags that should be passed to every
+    # compilation.  They are passed first to allow individual
+    # tests to override them.
+    if [info exists TEST_ALWAYS_FLAGS] {
+	lappend ALWAYS_RUSTFLAGS "additional_flags=$TEST_ALWAYS_FLAGS"
+    }
+
+    if ![is_remote host] {
+	if [info exists TOOL_OPTIONS] {
+	    lappend ALWAYS_RUSTFLAGS "additional_flags=[rust_include_flags [get_multilibs ${TOOL_OPTIONS}] ]"
+	    lappend ALWAYS_RUSTFLAGS "ldflags=[rust_link_flags [get_multilibs ${TOOL_OPTIONS}] ]"
+	} else {
+	    lappend ALWAYS_RUSTFLAGS "additional_flags=[rust_include_flags [get_multilibs] ]"
+	    lappend ALWAYS_RUSTFLAGS "ldflags=[rust_link_flags [get_multilibs] ]"
+	}
+    }
+
+    if [info exists TOOL_OPTIONS] {
+	lappend ALWAYS_RUSTFLAGS "additional_flags=$TOOL_OPTIONS"
+    }
+
+    verbose -log "ALWAYS_RUSTFLAGS set to $ALWAYS_RUSTFLAGS"
+
+    set gcc_warning_prefix "warning:"
+    set gcc_error_prefix "(fatal )?error:"
+
+    verbose "rust is initialized" 3
+}
+
+#
+# rust_target_compile -- compile a source file
+#
+
+proc rust_target_compile { source dest type options } {
+    global tmpdir
+    global gluefile wrap_flags
+    global ALWAYS_RUSTFLAGS
+    global RUST_UNDER_TEST
+    global individual_timeout
+
+    # HACK: guard against infinite loops in the compiler
+    set individual_timeout 10
+
+    if { [target_info needs_status_wrapper] != "" && [info exists gluefile] } {
+	lappend options "libs=${gluefile}"
+	lappend options "ldflags=${wrap_flags}"
+    }
+
+    lappend options "timeout=[timeout_value]"
+    lappend options "compiler=$RUST_UNDER_TEST"
+
+    set options [concat "$ALWAYS_RUSTFLAGS" $options]
+    set options [dg-additional-files-options $options $source]
+
+    return [target_compile $source $dest $type $options]
+}
-- 
2.25.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [PATCH Rust front-end v2 03/37] gccrs: Add Debug info testsuite
  2022-08-24 11:59 Rust frontend patches v2 herron.philip
  2022-08-24 11:59 ` [PATCH Rust front-end v2 01/37] Use DW_ATE_UTF for the Rust 'char' type herron.philip
  2022-08-24 11:59 ` [PATCH Rust front-end v2 02/37] gccrs: Add nessecary hooks for a Rust front-end testsuite herron.philip
@ 2022-08-24 11:59 ` herron.philip
  2022-08-24 11:59 ` [PATCH Rust front-end v2 04/37] gccrs: Add link cases testsuite herron.philip
                   ` (34 subsequent siblings)
  37 siblings, 0 replies; 59+ messages in thread
From: herron.philip @ 2022-08-24 11:59 UTC (permalink / raw)
  To: gcc-patches; +Cc: gcc-rust, Philip Herron, Tom Tromey

From: Philip Herron <philip.herron@embecosm.com>

This testsuite is specifically about testcases which scan the asm debug
info for results.

Co-authored-by: Tom Tromey <tom@tromey.com>
---
 gcc/testsuite/rust/debug/chartype.rs          | 10 ++++++
 .../rust/debug/custom_link_section.rs         | 13 ++++++++
 gcc/testsuite/rust/debug/debug.exp            | 33 +++++++++++++++++++
 gcc/testsuite/rust/debug/i8u8.rs              | 12 +++++++
 gcc/testsuite/rust/debug/lang.rs              |  6 ++++
 gcc/testsuite/rust/debug/no_mangle.rs         | 17 ++++++++++
 gcc/testsuite/rust/debug/oldlang.rs           |  6 ++++
 gcc/testsuite/rust/debug/tuple.rs             |  8 +++++
 gcc/testsuite/rust/debug/win64-abi.rs         | 11 +++++++
 9 files changed, 116 insertions(+)
 create mode 100644 gcc/testsuite/rust/debug/chartype.rs
 create mode 100644 gcc/testsuite/rust/debug/custom_link_section.rs
 create mode 100644 gcc/testsuite/rust/debug/debug.exp
 create mode 100644 gcc/testsuite/rust/debug/i8u8.rs
 create mode 100644 gcc/testsuite/rust/debug/lang.rs
 create mode 100644 gcc/testsuite/rust/debug/no_mangle.rs
 create mode 100644 gcc/testsuite/rust/debug/oldlang.rs
 create mode 100644 gcc/testsuite/rust/debug/tuple.rs
 create mode 100644 gcc/testsuite/rust/debug/win64-abi.rs

diff --git a/gcc/testsuite/rust/debug/chartype.rs b/gcc/testsuite/rust/debug/chartype.rs
new file mode 100644
index 00000000000..69e7ab0b17f
--- /dev/null
+++ b/gcc/testsuite/rust/debug/chartype.rs
@@ -0,0 +1,10 @@
+// 'char' should use DW_ATE_UTF
+fn main () {
+    let c = 'x';
+// { dg-do compile }
+// Use -w to avoid warnings about the unused variables
+// DW_ATE_UTF entered in DWARF 4.
+// { dg-options "-w -gdwarf-4 -dA" }
+// DW_ATE_UTF = 0x10
+// { dg-final { scan-assembler "0x10\[ \t]\[^\n\r]* DW_AT_encoding" } } */
+}
diff --git a/gcc/testsuite/rust/debug/custom_link_section.rs b/gcc/testsuite/rust/debug/custom_link_section.rs
new file mode 100644
index 00000000000..142f3513136
--- /dev/null
+++ b/gcc/testsuite/rust/debug/custom_link_section.rs
@@ -0,0 +1,13 @@
+#[link_section = ".universe"]
+fn not_in_text() -> i32 {
+    42
+}
+
+fn main() -> i32 {
+// { dg-do compile }
+// { dg-options "-gdwarf-5 -dA -w" }
+    not_in_text();
+// { dg-final { scan-assembler ".universe" } } */
+
+    0
+}
diff --git a/gcc/testsuite/rust/debug/debug.exp b/gcc/testsuite/rust/debug/debug.exp
new file mode 100644
index 00000000000..c71b5930d90
--- /dev/null
+++ b/gcc/testsuite/rust/debug/debug.exp
@@ -0,0 +1,33 @@
+# Copyright (C) 2021-2022 Free Software Foundation, Inc.
+
+# This program 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 of the License, or
+# (at your option) any later version.
+# 
+# This program 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/>.
+
+# Debugging tests.
+
+# Load support procs.
+load_lib rust-dg.exp
+
+# Initialize `dg'.
+dg-init
+
+# Main loop.
+set saved-dg-do-what-default ${dg-do-what-default}
+
+set dg-do-what-default "compile"
+dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.rs]] "" ""
+set dg-do-what-default ${saved-dg-do-what-default}
+
+# All done.
+dg-finish
diff --git a/gcc/testsuite/rust/debug/i8u8.rs b/gcc/testsuite/rust/debug/i8u8.rs
new file mode 100644
index 00000000000..1cd21a4a8ff
--- /dev/null
+++ b/gcc/testsuite/rust/debug/i8u8.rs
@@ -0,0 +1,12 @@
+// i8 and u8 types should not have the DWARF 'char' encoding.
+fn main () {
+    let x : i8 = 5;
+    let y : u8 = 7;
+// { dg-do compile }
+// Use -w to avoid warnings about the unused variables
+// { dg-options "-w -g -dA" }
+// DW_ATE_signed_char = 6
+// { dg-final { scan-assembler-not "0x6\[ \t]\[^\n\r]* DW_AT_encoding" } } */
+// DW_ATE_unsigned_char = 8
+// { dg-final { scan-assembler-not "0x8\[ \t]\[^\n\r]* DW_AT_encoding" } } */
+}
diff --git a/gcc/testsuite/rust/debug/lang.rs b/gcc/testsuite/rust/debug/lang.rs
new file mode 100644
index 00000000000..12e0b587a02
--- /dev/null
+++ b/gcc/testsuite/rust/debug/lang.rs
@@ -0,0 +1,6 @@
+fn main () {
+// { dg-do compile }
+// { dg-options "-gdwarf-5 -dA" }
+// DW_LANG_Rust is 0x1c
+// { dg-final { scan-assembler "0x1c\[ \t]\[^\n\r]* DW_AT_language" } } */
+}
diff --git a/gcc/testsuite/rust/debug/no_mangle.rs b/gcc/testsuite/rust/debug/no_mangle.rs
new file mode 100644
index 00000000000..0cef40482f4
--- /dev/null
+++ b/gcc/testsuite/rust/debug/no_mangle.rs
@@ -0,0 +1,17 @@
+#[no_mangle]
+fn do_not_mangle() -> i32 {
+    0 
+}
+
+fn please_mangle() {}
+
+fn main() {
+// { dg-do compile }
+// { dg-options "-gdwarf-5 -dA" }
+    let _ = do_not_mangle();
+    please_mangle();
+// look for unmangled function name:
+// { dg-final { scan-assembler "do_not_mangle:" } } */
+// look for legacy mangled function name:
+// { dg-final { scan-assembler "13please_mangle" } } */
+}
diff --git a/gcc/testsuite/rust/debug/oldlang.rs b/gcc/testsuite/rust/debug/oldlang.rs
new file mode 100644
index 00000000000..ddacf0e4392
--- /dev/null
+++ b/gcc/testsuite/rust/debug/oldlang.rs
@@ -0,0 +1,6 @@
+fn main () {
+// { dg-do compile }
+// { dg-options "-gstrict-dwarf -gdwarf-3 -dA" }
+// DW_LANG_Rust_old is 0x9000
+// { dg-final { scan-assembler "0x9000\[ \t]\[^\n\r]* DW_AT_language" } } */
+}
diff --git a/gcc/testsuite/rust/debug/tuple.rs b/gcc/testsuite/rust/debug/tuple.rs
new file mode 100644
index 00000000000..e51a5ffdbb6
--- /dev/null
+++ b/gcc/testsuite/rust/debug/tuple.rs
@@ -0,0 +1,8 @@
+fn main () {
+// { dg-do compile }
+// { dg-options "-gdwarf-5 -dA -w" }
+    let x = (32, 32);
+// Look for field __0 and __1
+// { dg-final { scan-assembler "__0" } } */
+// { dg-final { scan-assembler "__1" } } */
+}
diff --git a/gcc/testsuite/rust/debug/win64-abi.rs b/gcc/testsuite/rust/debug/win64-abi.rs
new file mode 100644
index 00000000000..b2b08cd5114
--- /dev/null
+++ b/gcc/testsuite/rust/debug/win64-abi.rs
@@ -0,0 +1,11 @@
+// { dg-do compile { target { x86_64-*-* } } }
+// { dg-options "-gdwarf-5 -dA -w -O1 -m64" }
+pub extern "win64" fn square(num: i32) -> i32 {
+    num * num
+}
+
+fn main() {
+    // MS ABI dictates that the first argument is ecx instead of edi from the sysv world
+    // { dg-final { scan-assembler "%ecx, %ecx" } }
+    square(1);
+}
-- 
2.25.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [PATCH Rust front-end v2 04/37] gccrs: Add link cases testsuite
  2022-08-24 11:59 Rust frontend patches v2 herron.philip
                   ` (2 preceding siblings ...)
  2022-08-24 11:59 ` [PATCH Rust front-end v2 03/37] gccrs: Add Debug info testsuite herron.philip
@ 2022-08-24 11:59 ` herron.philip
  2022-08-24 11:59 ` [PATCH Rust front-end v2 05/37] gccrs: Add general compilation test cases herron.philip
                   ` (33 subsequent siblings)
  37 siblings, 0 replies; 59+ messages in thread
From: herron.philip @ 2022-08-24 11:59 UTC (permalink / raw)
  To: gcc-patches; +Cc: gcc-rust, Philip Herron

From: Philip Herron <philip.herron@embecosm.com>

This testsuite is heavily inspired from the lto testsuite which uses a
pattern that each file is compiled to an object file and finally linked
together. Since rust does not have headers/prototypes we rely on the
ordering here so that all files numbered greater than zero get compiled to
object files first leaving the _0 file free to test the 'extern crate' and
use keywords to force testing of the compiler to read metadata from the
other 'crates'.
---
 gcc/testsuite/rust/link/generic_function_0.rs |   7 +
 gcc/testsuite/rust/link/generic_function_1.rs |   3 +
 gcc/testsuite/rust/link/link.exp              | 172 ++++++++++++++++++
 gcc/testsuite/rust/link/simple_function_0.rs  |   8 +
 gcc/testsuite/rust/link/simple_function_1.rs  |   3 +
 gcc/testsuite/rust/link/trait_import_0.rs     |  19 ++
 gcc/testsuite/rust/link/trait_import_1.rs     |   6 +
 7 files changed, 218 insertions(+)
 create mode 100644 gcc/testsuite/rust/link/generic_function_0.rs
 create mode 100644 gcc/testsuite/rust/link/generic_function_1.rs
 create mode 100644 gcc/testsuite/rust/link/link.exp
 create mode 100644 gcc/testsuite/rust/link/simple_function_0.rs
 create mode 100644 gcc/testsuite/rust/link/simple_function_1.rs
 create mode 100644 gcc/testsuite/rust/link/trait_import_0.rs
 create mode 100644 gcc/testsuite/rust/link/trait_import_1.rs

diff --git a/gcc/testsuite/rust/link/generic_function_0.rs b/gcc/testsuite/rust/link/generic_function_0.rs
new file mode 100644
index 00000000000..58b8eb13db6
--- /dev/null
+++ b/gcc/testsuite/rust/link/generic_function_0.rs
@@ -0,0 +1,7 @@
+extern crate generic_function_1;
+use generic_function_1::generic_function;
+
+fn main() -> i32 {
+    let a = generic_function(123);
+    a - 123
+}
diff --git a/gcc/testsuite/rust/link/generic_function_1.rs b/gcc/testsuite/rust/link/generic_function_1.rs
new file mode 100644
index 00000000000..8fb0788e388
--- /dev/null
+++ b/gcc/testsuite/rust/link/generic_function_1.rs
@@ -0,0 +1,3 @@
+pub fn generic_function<X>(a: X) -> X {
+    a
+}
diff --git a/gcc/testsuite/rust/link/link.exp b/gcc/testsuite/rust/link/link.exp
new file mode 100644
index 00000000000..8b2e93ceab6
--- /dev/null
+++ b/gcc/testsuite/rust/link/link.exp
@@ -0,0 +1,172 @@
+# Copyright (C) 2021-2022 Free Software Foundation, Inc.
+
+# This program 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 of the License, or
+# (at your option) any later version.
+# 
+# This program 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/>.
+
+# Execute tests, torture testing.
+
+# Load support procs.
+load_lib rust-dg.exp
+
+# Initialize `dg'.
+dg-init
+
+# Main loop.
+set saved-dg-do-what-default ${dg-do-what-default}
+
+set dg-do-what-default "assemble"
+
+# rs-obj -- compile to an object file
+#
+# SOURCE is the source file
+# DEST is the object file
+# OPTALL is the list of compiler options to use with all tests
+# OPTFILE is the list of compiler options to use with this file
+# OPTSTR is the options to print with test messages
+# XFAILDATA is the xfail data to be passed to the compiler
+proc rs-obj { source dest optall optfile optstr xfaildata } {
+    global tool
+    global compiler_conditional_xfail_data
+
+    # Set up the options for compiling this file.
+    set options ""
+    lappend options "additional_flags=$optall $optfile"
+
+    set compiler_conditional_xfail_data $xfaildata
+    set comp_output [${tool}_target_compile "$source" "$dest" object $options]
+}
+
+# rs-execute -- compile multi-file tests
+#
+# SRC1 is the full pathname of the main file of the testcase.
+# SID identifies a test suite in the names of temporary files.
+proc rs-execute-1 { src1 } {
+    global srcdir tmpdir
+    
+    # Get extra flags for this test from the primary source file, and
+    # process other dg-* options that this suite supports.  Warn about
+    # unsupported flags.
+    verbose "rs-execute: $src1" 1
+    set compile_type "run"
+    set compile_xfail(0) "" 
+
+    # Set up the names of the other source files.
+    set dir [file dirname $src1]
+    set base [file rootname $src1]
+    set base [string range $base [string length $dir] end]
+    regsub "_0" $base "" base
+    regsub "/" $base "" base
+    set src_list $src1
+    set i 1
+    set done 0
+    while { !$done } {
+	set names [glob -nocomplain -types f -- "${dir}/${base}_${i}.*"]
+	if { [llength ${names}] > 1 } {
+	    warning "rs-link-execute: more than one file matched ${dir}/${base}_${i}.*"
+	}
+	if { [llength ${names}] == 1 } {
+	    lappend src_list [lindex ${names} 0]
+	    incr i
+	} else {
+	    set num_srcs ${i}
+	    set done 1
+	}
+    }
+
+
+    # Define the names of the object files.
+    set obj_list ""
+    for {set i 0} {$i < $num_srcs} {incr i} {
+	lappend obj_list "${base}_${i}.o"
+    }
+
+    # Get the base name of this test, for use in messages.
+    set testcase [lindex ${src_list} 0]
+
+    # Remove the $srcdir and $tmpdir prefixes from $src1.  (It would
+    # be possible to use "regsub" here, if we were careful to escape
+    # all regular expression characters in $srcdir and $tmpdir, but
+    # that would be more complicated that this approach.) 
+    if {[string first "$srcdir/" "${testcase}"] == 0} {
+	set testcase [string range "${testcase}" [string length "$srcdir/"] end]
+    }
+    if {[string first "$tmpdir/" "$testcase"] == 0} {
+	set testcase [string range "$testcase" [string length "$tmpdir/"] end]
+	set testcase "tmpdir-$testcase"
+    }
+    # If we couldn't rip $srcdir out of `src1' then just do the best we can.
+    # The point is to reduce the unnecessary noise in the logs.  Don't strip
+    # out too much because different testcases with the same name can confuse
+    # `test-tool'.
+    if [string match "/*" $testcase] then {
+        set testcase "[file tail [file dirname $src1]]/[file tail $src1]"
+    }
+
+    # Set up the base name of executable files so they'll be unique.
+    regsub -all "\[./\]" $testcase "-" execbase
+
+    verbose "Testing $testcase - $obj_list - $src_list"
+    
+    # There's a unique name for each executable we generate.
+    set execname "${execbase}-1.exe"
+
+    # The LTO tests don't use dg-test, so testname_with_flags and
+    # output_file need to be defined explicitly for each file.  scan-symbol
+    # directives rely on both of these to be defined to find the symbol to
+    # scan and for the text to print in the PASS/FAIL since they can also
+    # be called from dg-test.  testname_with_flags is also used via
+    # testname-for-summary when calling into generic function below to
+    # clean temporary files.
+    set output_file $execname
+    set testname_with_flags $execname
+
+    file_on_host delete $execname
+    
+    rs-obj [lindex ${src_list} 1] [lindex ${obj_list} 1] "" "" "" ""
+    rs-obj [lindex ${src_list} 0] [lindex ${obj_list} 0] "" "" "" ""
+
+    gcc-dg-runtest [lindex ${src_list} 0] "" ""
+
+    # FIXME it would be ideal if we could link then execute these tests.
+    # I was not able to figure out how to specify gc-dg-runtest to link
+    # against the first object.
+}
+
+proc rs-link-execute { src1 } {
+    rs-execute-1 $src1
+}
+
+# Main loop.
+foreach src [lsort [find $srcdir/$subdir *_0.rs]] {
+    # If we're only testing specific files and this isn't one of them, skip it.
+    if ![runtest_file_p $runtests $src] then {
+	continue
+    }
+
+    # To prevent 'runtest_file_p' being tested again (for example, via
+    # 'gcc-dg-runtest'), with undesirable consequences due to its side effects,
+    # interpose a dummy:
+    rename runtest_file_p saved_runtest_file_p
+    proc runtest_file_p { runtests testcase } {
+	return 1
+    }
+    rs-link-execute $src
+    rename runtest_file_p {}
+    rename saved_runtest_file_p runtest_file_p
+}
+
+set dg-do-what-default ${saved-dg-do-what-default}
+
+# All done.
+dg-finish
diff --git a/gcc/testsuite/rust/link/simple_function_0.rs b/gcc/testsuite/rust/link/simple_function_0.rs
new file mode 100644
index 00000000000..5bd4926def8
--- /dev/null
+++ b/gcc/testsuite/rust/link/simple_function_0.rs
@@ -0,0 +1,8 @@
+extern crate simple_function_1;
+use simple_function_1::test_func;
+
+fn main() -> i32 {
+    let a = test_func(123);
+    // { dg-bogus "call to extern function" "" { xfail *-*-* } .-1 }
+    a - 124
+}
diff --git a/gcc/testsuite/rust/link/simple_function_1.rs b/gcc/testsuite/rust/link/simple_function_1.rs
new file mode 100644
index 00000000000..aaa1fc39367
--- /dev/null
+++ b/gcc/testsuite/rust/link/simple_function_1.rs
@@ -0,0 +1,3 @@
+pub fn test_func(a: i32) -> i32 {
+    a + 1
+}
diff --git a/gcc/testsuite/rust/link/trait_import_0.rs b/gcc/testsuite/rust/link/trait_import_0.rs
new file mode 100644
index 00000000000..ac8c5811d22
--- /dev/null
+++ b/gcc/testsuite/rust/link/trait_import_0.rs
@@ -0,0 +1,19 @@
+extern crate trait_import_1;
+use trait_import_1::Add;
+
+struct Foo(i32);
+
+impl Add for Foo {
+    type Output = Foo;
+
+    fn add(self, other: Foo) -> Foo {
+        Foo(self.0 + other.0)
+    }
+}
+
+fn main() -> i32 {
+    let a;
+    a = Foo(1) + Foo(2);
+
+    0
+}
diff --git a/gcc/testsuite/rust/link/trait_import_1.rs b/gcc/testsuite/rust/link/trait_import_1.rs
new file mode 100644
index 00000000000..fc7f5168ede
--- /dev/null
+++ b/gcc/testsuite/rust/link/trait_import_1.rs
@@ -0,0 +1,6 @@
+#[lang = "add"]
+pub trait Add<Rhs = Self> {
+    type Output;
+
+    fn add(self, rhs: Rhs) -> Self::Output;
+}
-- 
2.25.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [PATCH Rust front-end v2 05/37] gccrs: Add general compilation test cases
  2022-08-24 11:59 Rust frontend patches v2 herron.philip
                   ` (3 preceding siblings ...)
  2022-08-24 11:59 ` [PATCH Rust front-end v2 04/37] gccrs: Add link cases testsuite herron.philip
@ 2022-08-24 11:59 ` herron.philip
  2022-08-24 11:59 ` [PATCH Rust front-end v2 06/37] gccrs: Add execution " herron.philip
                   ` (32 subsequent siblings)
  37 siblings, 0 replies; 59+ messages in thread
From: herron.philip @ 2022-08-24 11:59 UTC (permalink / raw)
  To: gcc-patches
  Cc: gcc-rust, Philip Herron, Arthur Cohen, Thomas Schwinge,
	Mark Wielaard, Marc Poulhiès

From: Philip Herron <philip.herron@embecosm.com>

This suite of tests has two sections compile/*.rs and compile/torture/*.rs.
The first section are all dg-compile tests which contain dg-warning or
dg-error annotations and some with no annotations to ensure they do create
a resulting asm output. The second section is the same but have tests which
are ran with the full torture options, as during development the test case
may have had an issue with a specific optimization level.

Co-authored-by: Arthur Cohen <arthur.cohen@embecosm.com>
Co-authored-by: Thomas Schwinge <thomas@codesourcery.com>
Co-authored-by: Mark Wielaard <mark@klomp.org>
Co-authored-by: Marc Poulhiès <dkm@kataplop.net>
---
 gcc/testsuite/rust/compile/abi-options1.rs    |   7 ++
 gcc/testsuite/rust/compile/array3.rs          |   4 +
 .../rust/compile/array_empty_list.rs          |   4 +
 gcc/testsuite/rust/compile/arrays1.rs         |   4 +
 gcc/testsuite/rust/compile/arrays2.rs         |   5 +
 .../rust/compile/attr-mismatch-crate-name.rs  |   4 +
 gcc/testsuite/rust/compile/attr_cold.rs       |  12 +++
 gcc/testsuite/rust/compile/attr_deprecated.rs |  14 +++
 .../rust/compile/attr_deprecated_2.rs         |  11 +++
 gcc/testsuite/rust/compile/bad-crate-name.rs  |   4 +
 gcc/testsuite/rust/compile/bad=file-name.rs   |   7 ++
 .../rust/compile/bad_as_bool_char.rs          |  18 ++++
 .../rust/compile/bad_file_name.txt.rs         |   3 +
 gcc/testsuite/rust/compile/bad_inner_doc.rs   |  15 +++
 .../rust/compile/bad_pub_enumitems.rs         |  47 +++++++++
 gcc/testsuite/rust/compile/bad_stmt_enums.rs  |  22 +++++
 .../rust/compile/bad_toplevel_enums.rs        |  19 ++++
 gcc/testsuite/rust/compile/bad_tuple_index.rs |  66 +++++++++++++
 gcc/testsuite/rust/compile/bad_type1.rs       |   3 +
 gcc/testsuite/rust/compile/bad_type2.rs       |  14 +++
 gcc/testsuite/rust/compile/break1.rs          |   6 ++
 gcc/testsuite/rust/compile/break2.rs          |  15 +++
 .../compile/builtin_macro_compile_error.rs    |  13 +++
 .../rust/compile/builtin_macro_concat.rs      |  17 ++++
 .../rust/compile/builtin_macro_env.rs         |  20 ++++
 .../compile/builtin_macro_include_bytes.rs    |  13 +++
 .../rust/compile/builtin_macro_include_str.rs |  13 +++
 .../rust/compile/builtin_macro_not_found.rs   |   4 +
 gcc/testsuite/rust/compile/bytecharstring.rs  |   8 ++
 .../rust/compile/canonical_paths1.rs          |  25 +++++
 gcc/testsuite/rust/compile/cast1.rs           |   5 +
 gcc/testsuite/rust/compile/cfg1.rs            |  31 ++++++
 gcc/testsuite/rust/compile/cfg2.rs            |  13 +++
 gcc/testsuite/rust/compile/cfg3.rs            |  11 +++
 gcc/testsuite/rust/compile/cfg4.rs            |  11 +++
 gcc/testsuite/rust/compile/cfg5.rs            |  11 +++
 gcc/testsuite/rust/compile/compile.exp        |  35 +++++++
 gcc/testsuite/rust/compile/complex-path1.rs   |  18 ++++
 gcc/testsuite/rust/compile/const-issue1440.rs |  76 +++++++++++++++
 gcc/testsuite/rust/compile/const1.rs          |   6 ++
 gcc/testsuite/rust/compile/const2.rs          |   7 ++
 gcc/testsuite/rust/compile/const3.rs          |   7 ++
 .../rust/compile/const_generics_1.rs          |  19 ++++
 .../rust/compile/const_generics_2.rs          |   4 +
 .../rust/compile/const_generics_3.rs          |  26 +++++
 .../rust/compile/const_generics_4.rs          |   7 ++
 .../rust/compile/const_generics_5.rs          |  12 +++
 .../rust/compile/const_generics_6.rs          |   2 +
 gcc/testsuite/rust/compile/continue1.rs       |  10 ++
 gcc/testsuite/rust/compile/deadcode_err1.rs   |  11 +++
 gcc/testsuite/rust/compile/deadcode_err2.rs   |  16 +++
 .../rust/compile/debug-diagnostics-default.rs |   5 +
 .../rust/compile/debug-diagnostics-off.rs     |   7 ++
 .../rust/compile/debug-diagnostics-on.rs      |   7 ++
 .../compile/doc_isolated_cr_block_comment.rs  |   3 +
 .../doc_isolated_cr_inner_block_comment.rs    |   5 +
 .../doc_isolated_cr_inner_line_comment.rs     |   5 +
 .../compile/doc_isolated_cr_line_comment.rs   |   3 +
 gcc/testsuite/rust/compile/dup_fields.rs      |  23 +++++
 .../compile/empty_comment_before_match.rs     |   7 ++
 .../rust/compile/expected_type_args2.rs       |   6 ++
 .../rust/compile/expected_type_args3.rs       |   8 ++
 gcc/testsuite/rust/compile/func1.rs           |   9 ++
 gcc/testsuite/rust/compile/func2.rs           |   7 ++
 gcc/testsuite/rust/compile/func3.rs           |   9 ++
 gcc/testsuite/rust/compile/func4.rs           |   6 ++
 gcc/testsuite/rust/compile/func5.rs           |   7 ++
 .../rust/compile/generic-default1.rs          |   7 ++
 gcc/testsuite/rust/compile/generics1.rs       |  11 +++
 gcc/testsuite/rust/compile/generics10.rs      |  12 +++
 gcc/testsuite/rust/compile/generics11.rs      |  12 +++
 gcc/testsuite/rust/compile/generics12.rs      |   6 ++
 gcc/testsuite/rust/compile/generics13.rs      |   1 +
 gcc/testsuite/rust/compile/generics2.rs       |  11 +++
 gcc/testsuite/rust/compile/generics3.rs       |  10 ++
 gcc/testsuite/rust/compile/generics4.rs       |  16 +++
 gcc/testsuite/rust/compile/generics5.rs       |  10 ++
 gcc/testsuite/rust/compile/generics6.rs       |  31 ++++++
 gcc/testsuite/rust/compile/generics7.rs       |  26 +++++
 gcc/testsuite/rust/compile/generics8.rs       |  15 +++
 gcc/testsuite/rust/compile/generics9.rs       |  10 ++
 .../rust/compile/implicit_returns_err1.rs     |  12 +++
 .../rust/compile/implicit_returns_err2.rs     |  10 ++
 .../rust/compile/implicit_returns_err3.rs     |   9 ++
 .../rust/compile/implicit_returns_err4.rs     |  10 ++
 .../rust/compile/infer-crate-name.rs          |   7 ++
 gcc/testsuite/rust/compile/inline_1.rs        |  16 +++
 gcc/testsuite/rust/compile/inline_2.rs        |   6 ++
 gcc/testsuite/rust/compile/issue-1005.rs      |   4 +
 gcc/testsuite/rust/compile/issue-1019.rs      |  19 ++++
 gcc/testsuite/rust/compile/issue-1023.rs      |   4 +
 gcc/testsuite/rust/compile/issue-1031.rs      |  17 ++++
 gcc/testsuite/rust/compile/issue-1034.rs      |  16 +++
 gcc/testsuite/rust/compile/issue-1089.rs      |   6 ++
 gcc/testsuite/rust/compile/issue-1128.rs      |   6 ++
 gcc/testsuite/rust/compile/issue-1129-1.rs    |   4 +
 gcc/testsuite/rust/compile/issue-1129-2.rs    |  22 +++++
 gcc/testsuite/rust/compile/issue-1130.rs      |  47 +++++++++
 gcc/testsuite/rust/compile/issue-1131.rs      |   4 +
 gcc/testsuite/rust/compile/issue-1152.rs      |   8 ++
 gcc/testsuite/rust/compile/issue-1165.rs      |   5 +
 gcc/testsuite/rust/compile/issue-1173.rs      |  23 +++++
 gcc/testsuite/rust/compile/issue-1226.rs      |   6 ++
 gcc/testsuite/rust/compile/issue-1234.rs      |   4 +
 gcc/testsuite/rust/compile/issue-1235.rs      |  21 ++++
 gcc/testsuite/rust/compile/issue-1237.rs      |  23 +++++
 gcc/testsuite/rust/compile/issue-1251.rs      |  14 +++
 gcc/testsuite/rust/compile/issue-1271.rs      |   5 +
 gcc/testsuite/rust/compile/issue-1289.rs      |  43 +++++++++
 gcc/testsuite/rust/compile/issue-1323-1.rs    |  18 ++++
 gcc/testsuite/rust/compile/issue-1323-2.rs    |  16 +++
 gcc/testsuite/rust/compile/issue-1383.rs      |   8 ++
 gcc/testsuite/rust/compile/issue-1393.rs      |  13 +++
 gcc/testsuite/rust/compile/issue-1447.rs      |  28 ++++++
 gcc/testsuite/rust/compile/issue-407-2.rs     |  21 ++++
 gcc/testsuite/rust/compile/issue-407.rs       |   9 ++
 gcc/testsuite/rust/compile/issue-557.rs       |   4 +
 gcc/testsuite/rust/compile/issue-635-1.rs     |   5 +
 gcc/testsuite/rust/compile/issue-635-2.rs     |   5 +
 gcc/testsuite/rust/compile/lookup_err1.rs     |   7 ++
 .../rust/compile/macro-issue1053-2.rs         |   5 +
 gcc/testsuite/rust/compile/macro-issue1053.rs |   3 +
 gcc/testsuite/rust/compile/macro-issue1224.rs |   9 ++
 gcc/testsuite/rust/compile/macro-issue1233.rs |  22 +++++
 .../rust/compile/macro-issue1395-2.rs         |   7 ++
 gcc/testsuite/rust/compile/macro-issue1395.rs |   5 +
 .../rust/compile/macro-issue1400-2.rs         |  32 ++++++
 gcc/testsuite/rust/compile/macro-issue1400.rs |  33 +++++++
 gcc/testsuite/rust/compile/macro1.rs          |   3 +
 gcc/testsuite/rust/compile/macro10.rs         |  11 +++
 gcc/testsuite/rust/compile/macro11.rs         |  11 +++
 gcc/testsuite/rust/compile/macro12.rs         |   8 ++
 gcc/testsuite/rust/compile/macro13.rs         |  12 +++
 gcc/testsuite/rust/compile/macro14.rs         |  10 ++
 gcc/testsuite/rust/compile/macro15.rs         |  12 +++
 gcc/testsuite/rust/compile/macro16.rs         |  11 +++
 gcc/testsuite/rust/compile/macro17.rs         |  10 ++
 gcc/testsuite/rust/compile/macro18.rs         |  14 +++
 gcc/testsuite/rust/compile/macro19.rs         |  19 ++++
 gcc/testsuite/rust/compile/macro2.rs          |   3 +
 gcc/testsuite/rust/compile/macro20.rs         |  16 +++
 gcc/testsuite/rust/compile/macro21.rs         |   9 ++
 gcc/testsuite/rust/compile/macro22.rs         |  10 ++
 gcc/testsuite/rust/compile/macro23.rs         |  25 +++++
 gcc/testsuite/rust/compile/macro25.rs         |   9 ++
 gcc/testsuite/rust/compile/macro26.rs         |  10 ++
 gcc/testsuite/rust/compile/macro27.rs         |   8 ++
 gcc/testsuite/rust/compile/macro28.rs         |   8 ++
 gcc/testsuite/rust/compile/macro29.rs         |   8 ++
 gcc/testsuite/rust/compile/macro3.rs          |   3 +
 gcc/testsuite/rust/compile/macro30.rs         |   8 ++
 gcc/testsuite/rust/compile/macro31.rs         |   8 ++
 gcc/testsuite/rust/compile/macro32.rs         |  19 ++++
 gcc/testsuite/rust/compile/macro33.rs         |   5 +
 gcc/testsuite/rust/compile/macro34.rs         |   3 +
 gcc/testsuite/rust/compile/macro35.rs         |   7 ++
 gcc/testsuite/rust/compile/macro36.rs         |   3 +
 gcc/testsuite/rust/compile/macro37.rs         |   5 +
 gcc/testsuite/rust/compile/macro38.rs         |   5 +
 gcc/testsuite/rust/compile/macro39.rs         |   5 +
 gcc/testsuite/rust/compile/macro4.rs          |   3 +
 gcc/testsuite/rust/compile/macro40.rs         |  48 +++++++++
 gcc/testsuite/rust/compile/macro41.rs         |  13 +++
 gcc/testsuite/rust/compile/macro42.rs         |  32 ++++++
 gcc/testsuite/rust/compile/macro5.rs          |   3 +
 gcc/testsuite/rust/compile/macro6.rs          |  11 +++
 gcc/testsuite/rust/compile/macro7.rs          |  13 +++
 gcc/testsuite/rust/compile/macro8.rs          |  12 +++
 gcc/testsuite/rust/compile/macro9.rs          |  17 ++++
 gcc/testsuite/rust/compile/macro_return.rs    |  10 ++
 gcc/testsuite/rust/compile/match1.rs          |  16 +++
 gcc/testsuite/rust/compile/match2.rs          |  15 +++
 gcc/testsuite/rust/compile/match3.rs          |  16 +++
 gcc/testsuite/rust/compile/match4.rs          |  16 +++
 gcc/testsuite/rust/compile/match5.rs          |  15 +++
 gcc/testsuite/rust/compile/match6.rs          |  18 ++++
 gcc/testsuite/rust/compile/match7.rs          |  12 +++
 gcc/testsuite/rust/compile/method1.rs         |  13 +++
 gcc/testsuite/rust/compile/method2.rs         |  16 +++
 .../rust/compile/mismatch-crate-name.rs       |   4 +
 .../rust/compile/missing_middle/both_path.rs  |   3 +
 .../compile/missing_middle/explicit.not.rs    |   1 +
 .../rust/compile/missing_middle/inner_path.rs |   3 +
 .../rust/compile/missing_middle/other.rs      |   3 +
 .../rust/compile/missing_middle/outer_path.rs |   3 +
 .../rust/compile/missing_middle/sub/mod.rs    |   3 +
 gcc/testsuite/rust/compile/missing_return1.rs |   6 ++
 .../rust/compile/mod_missing_middle.rs        |  29 ++++++
 gcc/testsuite/rust/compile/never_type_err1.rs |  14 +++
 gcc/testsuite/rust/compile/privacy1.rs        |  11 +++
 gcc/testsuite/rust/compile/privacy2.rs        |  13 +++
 gcc/testsuite/rust/compile/privacy3.rs        |  28 ++++++
 gcc/testsuite/rust/compile/privacy4.rs        |  19 ++++
 gcc/testsuite/rust/compile/privacy5.rs        |  17 ++++
 gcc/testsuite/rust/compile/privacy6.rs        |  39 ++++++++
 .../rust/compile/pub_restricted_1.rs          |  13 +++
 .../rust/compile/pub_restricted_2.rs          |  18 ++++
 .../rust/compile/pub_restricted_3.rs          |  11 +++
 .../compile/raw_identifiers_bad_keywords.rs   |   3 +
 .../compile/raw_identifiers_underscore.rs     |   3 +
 gcc/testsuite/rust/compile/rawbytestring.rs   | Bin 0 -> 3234 bytes
 gcc/testsuite/rust/compile/redef_error1.rs    |   8 ++
 gcc/testsuite/rust/compile/redef_error2.rs    |   4 +
 gcc/testsuite/rust/compile/redef_error3.rs    |   9 ++
 gcc/testsuite/rust/compile/redef_error4.rs    |  27 ++++++
 gcc/testsuite/rust/compile/redef_error5.rs    |   8 ++
 gcc/testsuite/rust/compile/redef_error6.rs    |  13 +++
 gcc/testsuite/rust/compile/reference1.rs      |   6 ++
 gcc/testsuite/rust/compile/self-path1.rs      |  12 +++
 gcc/testsuite/rust/compile/self-path2.rs      |  21 ++++
 gcc/testsuite/rust/compile/shadow1.rs         |   7 ++
 .../rust/compile/specify-crate-name.rs        |   7 ++
 gcc/testsuite/rust/compile/static_var1.rs     |   5 +
 .../rust/compile/stmt_with_block_err1.rs      |  17 ++++
 gcc/testsuite/rust/compile/struct_align1.rs   |  19 ++++
 gcc/testsuite/rust/compile/struct_align2.rs   |  18 ++++
 gcc/testsuite/rust/compile/struct_init1.rs    |  10 ++
 gcc/testsuite/rust/compile/struct_pack1.rs    |  19 ++++
 gcc/testsuite/rust/compile/struct_pack2.rs    |  18 ++++
 gcc/testsuite/rust/compile/syntax-only.rs     |   6 ++
 gcc/testsuite/rust/compile/test_mod.rs        |   6 ++
 .../torture/all_doc_comment_line_blocks.rs    |  45 +++++++++
 .../all_doc_comment_line_blocks_crlf.rs       |  48 +++++++++
 .../torture/arithmetic_expressions1.rs        |  30 ++++++
 .../compile/torture/array_const_fold_1.rs     |   2 +
 .../compile/torture/array_const_fold_2.rs     |   3 +
 .../rust/compile/torture/array_function.rs    |   8 ++
 .../rust/compile/torture/array_type_infer.rs  |   4 +
 .../rust/compile/torture/array_zero_length.rs |   4 +
 gcc/testsuite/rust/compile/torture/arrays1.rs |   9 ++
 gcc/testsuite/rust/compile/torture/arrays2.rs |   8 ++
 gcc/testsuite/rust/compile/torture/arrays3.rs |   6 ++
 gcc/testsuite/rust/compile/torture/arrays4.rs |   6 ++
 gcc/testsuite/rust/compile/torture/arrays5.rs |   6 ++
 gcc/testsuite/rust/compile/torture/arrays6.rs |  10 ++
 .../rust/compile/torture/arrays_index1.rs     |   9 ++
 .../rust/compile/torture/arrays_index2.rs     |   4 +
 .../rust/compile/torture/arrays_index3.rs     |  15 +++
 .../rust/compile/torture/as_bool_char.rs      |  36 +++++++
 .../rust/compile/torture/associated_types1.rs |  12 +++
 .../rust/compile/torture/autoderef1.rs        |  15 +++
 .../rust/compile/torture/block_expr1.rs       |  29 ++++++
 .../rust/compile/torture/block_expr2.rs       |  15 +++
 .../rust/compile/torture/block_expr3.rs       |  14 +++
 .../rust/compile/torture/block_expr4.rs       |   8 ++
 .../rust/compile/torture/block_expr5.rs       |  40 ++++++++
 .../compile/torture/block_expr_parser_bug.rs  |   5 +
 gcc/testsuite/rust/compile/torture/bom.rs     |   1 +
 .../rust/compile/torture/bom_comment.rs       |   2 +
 .../rust/compile/torture/bom_shebang.rs       |   2 +
 .../rust/compile/torture/bom_whitespace.rs    |   2 +
 .../rust/compile/torture/bools_eq.rs          |  18 ++++
 gcc/testsuite/rust/compile/torture/borrow1.rs |  17 ++++
 .../rust/compile/torture/borrow_function.rs   |   5 +
 .../rust/compile/torture/break_function.rs    |  10 ++
 .../rust/compile/torture/byte_char_str.rs     |   8 ++
 .../rust/compile/torture/byte_str.rs          |   4 +
 gcc/testsuite/rust/compile/torture/cast1.rs   |   5 +
 gcc/testsuite/rust/compile/torture/cast2.rs   |   5 +
 gcc/testsuite/rust/compile/torture/cast3.rs   |   6 ++
 .../rust/compile/torture/cfg_attr.rs          |   7 ++
 gcc/testsuite/rust/compile/torture/char1.rs   |   4 +
 .../compile/torture/check-doc-attr-string.rs  |  18 ++++
 .../rust/compile/torture/coercion1.rs         |  11 +++
 .../rust/compile/torture/coercion2.rs         |  20 ++++
 .../rust/compile/torture/comparison_expr1.rs  |  38 ++++++++
 .../rust/compile/torture/compile.exp          |  33 +++++++
 .../torture/compound_assignment_expr1.rs      |  23 +++++
 .../rust/compile/torture/conditional.rs       |  11 +++
 .../rust/compile/torture/constant1.rs         |   9 ++
 .../rust/compile/torture/constant2.rs         |   6 ++
 .../rust/compile/torture/constant3.rs         |  10 ++
 .../rust/compile/torture/deadcode1.rs         |  22 +++++
 .../rust/compile/torture/deadcode2.rs         |  10 ++
 gcc/testsuite/rust/compile/torture/deref1.rs  |   6 ++
 .../rust/compile/torture/deref_function.rs    |  10 ++
 .../rust/compile/torture/doc_comment.rs       |  16 +++
 gcc/testsuite/rust/compile/torture/enum1.rs   |  13 +++
 .../rust/compile/torture/extern_mod1.rs       |   6 ++
 .../rust/compile/torture/extern_mod2.rs       |  23 +++++
 gcc/testsuite/rust/compile/torture/float1.rs  |   9 ++
 .../rust/compile/torture/float_types.rs       |  13 +++
 .../rust/compile/torture/forward_decl_1.rs    |  11 +++
 .../rust/compile/torture/forward_decl_2.rs    |   6 ++
 .../compile/torture/forward_decl_3-unsafe.rs  |  13 +++
 .../rust/compile/torture/forward_decl_3.rs    |  11 +++
 .../rust/compile/torture/forward_decl_4.rs    |   9 ++
 .../rust/compile/torture/forward_decl_5.rs    |  19 ++++
 gcc/testsuite/rust/compile/torture/func1.rs   |   7 ++
 gcc/testsuite/rust/compile/torture/func2.rs   |  20 ++++
 .../compile/torture/function_reference1.rs    |   9 ++
 .../compile/torture/function_reference2.rs    |   9 ++
 .../compile/torture/function_reference3.rs    |  20 ++++
 .../compile/torture/function_reference4.rs    |   9 ++
 .../rust/compile/torture/generics1.rs         |  51 ++++++++++
 .../rust/compile/torture/generics10.rs        |  20 ++++
 .../rust/compile/torture/generics11.rs        |   8 ++
 .../rust/compile/torture/generics12.rs        |  17 ++++
 .../rust/compile/torture/generics13.rs        |  41 ++++++++
 .../rust/compile/torture/generics14.rs        |  20 ++++
 .../rust/compile/torture/generics15.rs        |  23 +++++
 .../rust/compile/torture/generics16.rs        |  31 ++++++
 .../rust/compile/torture/generics17.rs        |  19 ++++
 .../rust/compile/torture/generics18.rs        |  20 ++++
 .../rust/compile/torture/generics19.rs        |  12 +++
 .../rust/compile/torture/generics2.rs         |  45 +++++++++
 .../rust/compile/torture/generics20.rs        |  12 +++
 .../rust/compile/torture/generics21.rs        |  13 +++
 .../rust/compile/torture/generics22.rs        |  13 +++
 .../rust/compile/torture/generics23.rs        |   6 ++
 .../rust/compile/torture/generics24.rs        |  34 +++++++
 .../rust/compile/torture/generics25.rs        |   9 ++
 .../rust/compile/torture/generics26.rs        |  21 ++++
 .../rust/compile/torture/generics27.rs        |  16 +++
 .../rust/compile/torture/generics28.rs        |  18 ++++
 .../rust/compile/torture/generics29.rs        |  16 +++
 .../rust/compile/torture/generics3.rs         |  15 +++
 .../rust/compile/torture/generics30.rs        |  16 +++
 .../rust/compile/torture/generics31.rs        |  15 +++
 .../rust/compile/torture/generics32.rs        |  15 +++
 .../rust/compile/torture/generics4.rs         |  17 ++++
 .../rust/compile/torture/generics5.rs         |  10 ++
 .../rust/compile/torture/generics6.rs         |  16 +++
 .../rust/compile/torture/generics7.rs         |  14 +++
 .../rust/compile/torture/generics8.rs         |  18 ++++
 .../rust/compile/torture/generics9.rs         |  25 +++++
 .../compile/torture/grouped_expr_function.rs  |   6 ++
 .../torture/identifier-missing-impl-1.rs      |  19 ++++
 gcc/testsuite/rust/compile/torture/if.rs      |  19 ++++
 gcc/testsuite/rust/compile/torture/if_elif.rs |  20 ++++
 .../compile/torture/if_elif_else_expr1.rs     |  14 +++
 gcc/testsuite/rust/compile/torture/if_else.rs |  19 ++++
 .../rust/compile/torture/ifunaryexpr.rs       |  22 +++++
 .../rust/compile/torture/impl_block1.rs       |  23 +++++
 .../rust/compile/torture/impl_block2.rs       |  28 ++++++
 .../rust/compile/torture/impl_block3.rs       |  36 +++++++
 .../rust/compile/torture/impl_block_unused.rs |  17 ++++
 .../rust/compile/torture/implicit_returns1.rs |  73 ++++++++++++++
 .../rust/compile/torture/infer_type1.rs       |   4 +
 .../rust/compile/torture/inner_attributes.rs  |   3 +
 .../compile/torture/integer_inference_var1.rs |   6 ++
 .../compile/torture/integer_inference_var2.rs |   6 ++
 .../compile/torture/integer_inference_var3.rs |  11 +++
 .../compile/torture/integer_inference_var4.rs |   4 +
 .../compile/torture/integer_inference_var5.rs |  25 +++++
 .../rust/compile/torture/integer_types.rs     |  27 ++++++
 .../rust/compile/torture/intrinsics-1.rs      |  22 +++++
 .../rust/compile/torture/intrinsics-2.rs      |  22 +++++
 .../torture/isolated_cr_block_comment.rs      |   2 +
 .../torture/isolated_cr_line_comment.rs       |   2 +
 .../rust/compile/torture/issue-1024.rs        |  11 +++
 .../rust/compile/torture/issue-1075.rs        |  42 ++++++++
 .../rust/compile/torture/issue-1432.rs        |  77 +++++++++++++++
 .../rust/compile/torture/issue-1434.rs        |  53 ++++++++++
 .../rust/compile/torture/issue-368.rs         |   9 ++
 .../rust/compile/torture/issue-808.rs         |  20 ++++
 .../rust/compile/torture/issue-862.rs         |  74 ++++++++++++++
 .../rust/compile/torture/issue-893-2.rs       |  35 +++++++
 .../rust/compile/torture/issue-893.rs         |  11 +++
 .../torture/lazybooleanexpr_function.rs       |  14 +++
 .../rust/compile/torture/lifetime1.rs         |   7 ++
 .../rust/compile/torture/literals1.rs         |  11 +++
 gcc/testsuite/rust/compile/torture/loop1.rs   |  10 ++
 gcc/testsuite/rust/compile/torture/loop2.rs   |  14 +++
 gcc/testsuite/rust/compile/torture/loop3.rs   |  14 +++
 gcc/testsuite/rust/compile/torture/loop4.rs   |   7 ++
 gcc/testsuite/rust/compile/torture/loop5.rs   |  14 +++
 gcc/testsuite/rust/compile/torture/loop6.rs   |  11 +++
 gcc/testsuite/rust/compile/torture/loop7.rs   |  13 +++
 .../rust/compile/torture/macro-issue1403.rs   |  23 +++++
 .../rust/compile/torture/macro-issue1426.rs   |  32 ++++++
 .../rust/compile/torture/macro_as_expr.rs     |  14 +++
 gcc/testsuite/rust/compile/torture/match1.rs  |  16 +++
 .../rust/compile/torture/methods1.rs          |  41 ++++++++
 .../rust/compile/torture/methods2.rs          |  43 +++++++++
 .../rust/compile/torture/methods3.rs          |  44 +++++++++
 .../rust/compile/torture/mod-nameresolve.rs   |   5 +
 gcc/testsuite/rust/compile/torture/mod1.rs    |  11 +++
 gcc/testsuite/rust/compile/torture/mod2.rs    |  13 +++
 gcc/testsuite/rust/compile/torture/mod3.rs    |  22 +++++
 .../rust/compile/torture/modules/mod.rs       |   3 +
 .../compile/torture/modules/valid_path.rs     |   1 +
 .../rust/compile/torture/must_use1.rs         |  16 +++
 .../rust/compile/torture/must_use2.rs         |  16 +++
 .../rust/compile/torture/name_resolve1.rs     |  23 +++++
 .../rust/compile/torture/negation_function.rs |   7 ++
 .../rust/compile/torture/nested_fn1.rs        |  10 ++
 .../rust/compile/torture/nested_fn2.rs        |  11 +++
 .../rust/compile/torture/nested_struct1.rs    |  20 ++++
 .../rust/compile/torture/never_type1.rs       |  22 +++++
 .../rust/compile/torture/not_shebang.rs       |   3 +
 .../torture/not_shebang_block_comment.rs      |   1 +
 .../compile/torture/not_shebang_comment.rs    |   3 +
 .../torture/not_shebang_multiline_comment.rs  |   7 ++
 .../compile/torture/not_shebang_spaces.rs     |   6 ++
 .../rust/compile/torture/parameter_usage1.rs  |   8 ++
 gcc/testsuite/rust/compile/torture/parens1.rs |   5 +
 .../rust/compile/torture/pointer1.rs          |   9 ++
 .../rust/compile/torture/primconsts.rs        |  72 ++++++++++++++
 .../rust/compile/torture/prims_struct_eq.rs   |  91 ++++++++++++++++++
 .../rust/compile/torture/range-lang-item1.rs  |  32 ++++++
 .../rust/compile/torture/raw_identifiers.rs   |   3 +
 .../torture/raw_identifiers_keywords.rs       |   3 +
 .../rust/compile/torture/recursive_fn1.rs     |  12 +++
 .../rust/compile/torture/return_function.rs   |   5 +
 .../rust/compile/torture/scoping1.rs          |  11 +++
 .../rust/compile/torture/self_type1.rs        |  12 +++
 gcc/testsuite/rust/compile/torture/shadow1.rs |   6 ++
 gcc/testsuite/rust/compile/torture/shadow2.rs |   5 +
 gcc/testsuite/rust/compile/torture/shebang.rs |   3 +
 .../rust/compile/torture/shebang_plus_attr.rs |   3 +
 .../compile/torture/shebang_plus_attr2.rs     |   3 +
 .../rust/compile/torture/static_function.rs   |   8 ++
 .../rust/compile/torture/static_var1.rs       |   6 ++
 .../rust/compile/torture/stmt_with_block1.rs  |  13 +++
 gcc/testsuite/rust/compile/torture/str1.rs    |   7 ++
 .../rust/compile/torture/struct_access1.rs    |  12 +++
 .../compile/torture/struct_base_init_1.rs     |  13 +++
 .../rust/compile/torture/struct_decl.rs       |  14 +++
 .../rust/compile/torture/struct_init.rs       |  11 +++
 .../rust/compile/torture/struct_init_10.rs    |   9 ++
 .../rust/compile/torture/struct_init_11.rs    |  34 +++++++
 .../rust/compile/torture/struct_init_2.rs     |   6 ++
 .../rust/compile/torture/struct_init_3.rs     |  13 +++
 .../rust/compile/torture/struct_init_4.rs     |  13 +++
 .../rust/compile/torture/struct_init_5.rs     |  10 ++
 .../rust/compile/torture/struct_init_6.rs     |  11 +++
 .../rust/compile/torture/struct_init_7.rs     |  11 +++
 .../rust/compile/torture/struct_init_8.rs     |   7 ++
 .../rust/compile/torture/struct_init_9.rs     |   6 ++
 .../rust/compile/torture/top_attr.rs          |   5 +
 gcc/testsuite/rust/compile/torture/traits1.rs |  16 +++
 .../rust/compile/torture/traits10.rs          |  30 ++++++
 .../rust/compile/torture/traits11.rs          |  31 ++++++
 .../rust/compile/torture/traits12.rs          |  29 ++++++
 .../rust/compile/torture/traits13.rs          |  17 ++++
 .../rust/compile/torture/traits14.rs          |  23 +++++
 .../rust/compile/torture/traits15.rs          |  23 +++++
 .../rust/compile/torture/traits16.rs          |  20 ++++
 .../rust/compile/torture/traits17.rs          |  23 +++++
 .../rust/compile/torture/traits18.rs          |   5 +
 .../rust/compile/torture/traits19.rs          |  33 +++++++
 gcc/testsuite/rust/compile/torture/traits2.rs |  16 +++
 gcc/testsuite/rust/compile/torture/traits3.rs |  15 +++
 gcc/testsuite/rust/compile/torture/traits4.rs |  21 ++++
 gcc/testsuite/rust/compile/torture/traits5.rs |  21 ++++
 gcc/testsuite/rust/compile/torture/traits6.rs |  20 ++++
 gcc/testsuite/rust/compile/torture/traits7.rs |  19 ++++
 gcc/testsuite/rust/compile/torture/traits8.rs |  21 ++++
 gcc/testsuite/rust/compile/torture/traits9.rs |  27 ++++++
 .../compile/torture/transmute-size-check-1.rs |  11 +++
 .../rust/compile/torture/transmute1.rs        |  11 +++
 gcc/testsuite/rust/compile/torture/tuple1.rs  |   6 ++
 gcc/testsuite/rust/compile/torture/tuple2.rs  |   5 +
 gcc/testsuite/rust/compile/torture/tuple3.rs  |   9 ++
 .../compile/torture/tuple_enum_variants.rs    |  23 +++++
 .../compile/torture/tuple_field_access.rs     |   6 ++
 .../rust/compile/torture/tuple_function.rs    |   6 ++
 .../rust/compile/torture/tuple_index.rs       |  32 ++++++
 .../rust/compile/torture/tuple_struct1.rs     |   6 ++
 .../rust/compile/torture/tuple_struct2.rs     |  11 +++
 .../rust/compile/torture/tuple_struct_unit.rs |  11 +++
 .../compile/torture/tuple_struct_unused.rs    |   4 +
 .../rust/compile/torture/type-alias1.rs       |   6 ++
 .../rust/compile/torture/type-alias2.rs       |   8 ++
 .../rust/compile/torture/type_infer1.rs       |  24 +++++
 .../rust/compile/torture/type_infer2.rs       |   9 ++
 .../rust/compile/torture/type_infer3.rs       |  14 +++
 .../rust/compile/torture/type_infer4.rs       |  11 +++
 .../rust/compile/torture/type_infer5.rs       |  13 +++
 .../rust/compile/torture/type_infer6.rs       |  14 +++
 .../rust/compile/torture/unary_operators.rs   |   8 ++
 .../rust/compile/torture/undended-string-1.rs |   5 +
 .../rust/compile/torture/undended-string-2.rs |   5 +
 .../rust/compile/torture/underscore_id.rs     |   4 +
 gcc/testsuite/rust/compile/torture/union.rs   |  32 ++++++
 .../rust/compile/torture/union_union.rs       |  27 ++++++
 .../rust/compile/torture/unit_type1.rs        |   7 ++
 .../rust/compile/torture/unit_type2.rs        |   8 ++
 .../rust/compile/torture/unit_type3.rs        |   6 ++
 .../rust/compile/torture/unit_type4.rs        |   5 +
 .../rust/compile/torture/unit_type5.rs        |   8 ++
 gcc/testsuite/rust/compile/torture/unsafe1.rs |  12 +++
 gcc/testsuite/rust/compile/torture/unsafe2.rs |   4 +
 gcc/testsuite/rust/compile/torture/unsafe3.rs |   9 ++
 gcc/testsuite/rust/compile/torture/unsafe4.rs |  12 +++
 gcc/testsuite/rust/compile/torture/unused.rs  |  17 ++++
 gcc/testsuite/rust/compile/torture/unused1.rs |  15 +++
 .../rust/compile/torture/unused_struct.rs     |   7 ++
 .../compile/torture/unused_struct_field.rs    |   9 ++
 gcc/testsuite/rust/compile/torture/usize1.rs  |   6 ++
 .../torture/very-broken-attr-string.rs        |   3 +
 .../rust/compile/torture/while_function.rs    |  10 ++
 gcc/testsuite/rust/compile/traits1.rs         |  13 +++
 gcc/testsuite/rust/compile/traits10.rs        |  15 +++
 gcc/testsuite/rust/compile/traits11.rs        |  19 ++++
 gcc/testsuite/rust/compile/traits12.rs        |  20 ++++
 gcc/testsuite/rust/compile/traits2.rs         |  14 +++
 gcc/testsuite/rust/compile/traits3.rs         |  22 +++++
 gcc/testsuite/rust/compile/traits4.rs         |  16 +++
 gcc/testsuite/rust/compile/traits5.rs         |   9 ++
 gcc/testsuite/rust/compile/traits6.rs         |  15 +++
 gcc/testsuite/rust/compile/traits7.rs         |  24 +++++
 gcc/testsuite/rust/compile/traits8.rs         |  35 +++++++
 gcc/testsuite/rust/compile/traits9.rs         |  13 +++
 gcc/testsuite/rust/compile/tuple1.rs          |   5 +
 gcc/testsuite/rust/compile/tuple_struct1.rs   |   8 ++
 gcc/testsuite/rust/compile/tuple_struct2.rs   |   5 +
 gcc/testsuite/rust/compile/tuple_struct3.rs   |   6 ++
 gcc/testsuite/rust/compile/type-alias1.rs     |   6 ++
 gcc/testsuite/rust/compile/type-bindings1.rs  |  10 ++
 gcc/testsuite/rust/compile/unary_negation.rs  |   9 ++
 gcc/testsuite/rust/compile/unary_not.rs       |   9 ++
 .../rust/compile/unconstrained_type_param.rs  |  12 +++
 gcc/testsuite/rust/compile/unicode_escape.rs  |  60 ++++++++++++
 gcc/testsuite/rust/compile/unsafe1.rs         |  14 +++
 gcc/testsuite/rust/compile/unsafe10.rs        |  12 +++
 gcc/testsuite/rust/compile/unsafe2.rs         |  16 +++
 gcc/testsuite/rust/compile/unsafe3.rs         |  10 ++
 gcc/testsuite/rust/compile/unsafe4.rs         |  29 ++++++
 gcc/testsuite/rust/compile/unsafe5.rs         |   4 +
 gcc/testsuite/rust/compile/unsafe6.rs         |  14 +++
 gcc/testsuite/rust/compile/unsafe7.rs         |   9 ++
 gcc/testsuite/rust/compile/unsafe8.rs         |  14 +++
 gcc/testsuite/rust/compile/unsafe9.rs         |  10 ++
 .../rust/compile/unterminated_c_comment.rs    |   2 +
 gcc/testsuite/rust/compile/use_1.rs           |  16 +++
 gcc/testsuite/rust/compile/usize1.rs          |   6 ++
 .../rust/compile/xfail/lifetime_param.rs      |  11 +++
 .../rust/compile/xfail/struct_field_vis.rs    |  15 +++
 gcc/testsuite/rust/compile/xfail/xfail.exp    |  63 ++++++++++++
 531 files changed, 7556 insertions(+)
 create mode 100644 gcc/testsuite/rust/compile/abi-options1.rs
 create mode 100644 gcc/testsuite/rust/compile/array3.rs
 create mode 100644 gcc/testsuite/rust/compile/array_empty_list.rs
 create mode 100644 gcc/testsuite/rust/compile/arrays1.rs
 create mode 100644 gcc/testsuite/rust/compile/arrays2.rs
 create mode 100644 gcc/testsuite/rust/compile/attr-mismatch-crate-name.rs
 create mode 100644 gcc/testsuite/rust/compile/attr_cold.rs
 create mode 100644 gcc/testsuite/rust/compile/attr_deprecated.rs
 create mode 100644 gcc/testsuite/rust/compile/attr_deprecated_2.rs
 create mode 100644 gcc/testsuite/rust/compile/bad-crate-name.rs
 create mode 100644 gcc/testsuite/rust/compile/bad=file-name.rs
 create mode 100644 gcc/testsuite/rust/compile/bad_as_bool_char.rs
 create mode 100644 gcc/testsuite/rust/compile/bad_file_name.txt.rs
 create mode 100644 gcc/testsuite/rust/compile/bad_inner_doc.rs
 create mode 100644 gcc/testsuite/rust/compile/bad_pub_enumitems.rs
 create mode 100644 gcc/testsuite/rust/compile/bad_stmt_enums.rs
 create mode 100644 gcc/testsuite/rust/compile/bad_toplevel_enums.rs
 create mode 100644 gcc/testsuite/rust/compile/bad_tuple_index.rs
 create mode 100644 gcc/testsuite/rust/compile/bad_type1.rs
 create mode 100644 gcc/testsuite/rust/compile/bad_type2.rs
 create mode 100644 gcc/testsuite/rust/compile/break1.rs
 create mode 100644 gcc/testsuite/rust/compile/break2.rs
 create mode 100644 gcc/testsuite/rust/compile/builtin_macro_compile_error.rs
 create mode 100644 gcc/testsuite/rust/compile/builtin_macro_concat.rs
 create mode 100644 gcc/testsuite/rust/compile/builtin_macro_env.rs
 create mode 100644 gcc/testsuite/rust/compile/builtin_macro_include_bytes.rs
 create mode 100644 gcc/testsuite/rust/compile/builtin_macro_include_str.rs
 create mode 100644 gcc/testsuite/rust/compile/builtin_macro_not_found.rs
 create mode 100644 gcc/testsuite/rust/compile/bytecharstring.rs
 create mode 100644 gcc/testsuite/rust/compile/canonical_paths1.rs
 create mode 100644 gcc/testsuite/rust/compile/cast1.rs
 create mode 100644 gcc/testsuite/rust/compile/cfg1.rs
 create mode 100644 gcc/testsuite/rust/compile/cfg2.rs
 create mode 100644 gcc/testsuite/rust/compile/cfg3.rs
 create mode 100644 gcc/testsuite/rust/compile/cfg4.rs
 create mode 100644 gcc/testsuite/rust/compile/cfg5.rs
 create mode 100644 gcc/testsuite/rust/compile/compile.exp
 create mode 100644 gcc/testsuite/rust/compile/complex-path1.rs
 create mode 100644 gcc/testsuite/rust/compile/const-issue1440.rs
 create mode 100644 gcc/testsuite/rust/compile/const1.rs
 create mode 100644 gcc/testsuite/rust/compile/const2.rs
 create mode 100644 gcc/testsuite/rust/compile/const3.rs
 create mode 100644 gcc/testsuite/rust/compile/const_generics_1.rs
 create mode 100644 gcc/testsuite/rust/compile/const_generics_2.rs
 create mode 100644 gcc/testsuite/rust/compile/const_generics_3.rs
 create mode 100644 gcc/testsuite/rust/compile/const_generics_4.rs
 create mode 100644 gcc/testsuite/rust/compile/const_generics_5.rs
 create mode 100644 gcc/testsuite/rust/compile/const_generics_6.rs
 create mode 100644 gcc/testsuite/rust/compile/continue1.rs
 create mode 100644 gcc/testsuite/rust/compile/deadcode_err1.rs
 create mode 100644 gcc/testsuite/rust/compile/deadcode_err2.rs
 create mode 100644 gcc/testsuite/rust/compile/debug-diagnostics-default.rs
 create mode 100644 gcc/testsuite/rust/compile/debug-diagnostics-off.rs
 create mode 100644 gcc/testsuite/rust/compile/debug-diagnostics-on.rs
 create mode 100644 gcc/testsuite/rust/compile/doc_isolated_cr_block_comment.rs
 create mode 100644 gcc/testsuite/rust/compile/doc_isolated_cr_inner_block_comment.rs
 create mode 100644 gcc/testsuite/rust/compile/doc_isolated_cr_inner_line_comment.rs
 create mode 100644 gcc/testsuite/rust/compile/doc_isolated_cr_line_comment.rs
 create mode 100644 gcc/testsuite/rust/compile/dup_fields.rs
 create mode 100644 gcc/testsuite/rust/compile/empty_comment_before_match.rs
 create mode 100644 gcc/testsuite/rust/compile/expected_type_args2.rs
 create mode 100644 gcc/testsuite/rust/compile/expected_type_args3.rs
 create mode 100644 gcc/testsuite/rust/compile/func1.rs
 create mode 100644 gcc/testsuite/rust/compile/func2.rs
 create mode 100644 gcc/testsuite/rust/compile/func3.rs
 create mode 100644 gcc/testsuite/rust/compile/func4.rs
 create mode 100644 gcc/testsuite/rust/compile/func5.rs
 create mode 100644 gcc/testsuite/rust/compile/generic-default1.rs
 create mode 100644 gcc/testsuite/rust/compile/generics1.rs
 create mode 100644 gcc/testsuite/rust/compile/generics10.rs
 create mode 100644 gcc/testsuite/rust/compile/generics11.rs
 create mode 100644 gcc/testsuite/rust/compile/generics12.rs
 create mode 100644 gcc/testsuite/rust/compile/generics13.rs
 create mode 100644 gcc/testsuite/rust/compile/generics2.rs
 create mode 100644 gcc/testsuite/rust/compile/generics3.rs
 create mode 100644 gcc/testsuite/rust/compile/generics4.rs
 create mode 100644 gcc/testsuite/rust/compile/generics5.rs
 create mode 100644 gcc/testsuite/rust/compile/generics6.rs
 create mode 100644 gcc/testsuite/rust/compile/generics7.rs
 create mode 100644 gcc/testsuite/rust/compile/generics8.rs
 create mode 100644 gcc/testsuite/rust/compile/generics9.rs
 create mode 100644 gcc/testsuite/rust/compile/implicit_returns_err1.rs
 create mode 100644 gcc/testsuite/rust/compile/implicit_returns_err2.rs
 create mode 100644 gcc/testsuite/rust/compile/implicit_returns_err3.rs
 create mode 100644 gcc/testsuite/rust/compile/implicit_returns_err4.rs
 create mode 100644 gcc/testsuite/rust/compile/infer-crate-name.rs
 create mode 100644 gcc/testsuite/rust/compile/inline_1.rs
 create mode 100644 gcc/testsuite/rust/compile/inline_2.rs
 create mode 100644 gcc/testsuite/rust/compile/issue-1005.rs
 create mode 100644 gcc/testsuite/rust/compile/issue-1019.rs
 create mode 100644 gcc/testsuite/rust/compile/issue-1023.rs
 create mode 100644 gcc/testsuite/rust/compile/issue-1031.rs
 create mode 100644 gcc/testsuite/rust/compile/issue-1034.rs
 create mode 100644 gcc/testsuite/rust/compile/issue-1089.rs
 create mode 100644 gcc/testsuite/rust/compile/issue-1128.rs
 create mode 100644 gcc/testsuite/rust/compile/issue-1129-1.rs
 create mode 100644 gcc/testsuite/rust/compile/issue-1129-2.rs
 create mode 100644 gcc/testsuite/rust/compile/issue-1130.rs
 create mode 100644 gcc/testsuite/rust/compile/issue-1131.rs
 create mode 100644 gcc/testsuite/rust/compile/issue-1152.rs
 create mode 100644 gcc/testsuite/rust/compile/issue-1165.rs
 create mode 100644 gcc/testsuite/rust/compile/issue-1173.rs
 create mode 100644 gcc/testsuite/rust/compile/issue-1226.rs
 create mode 100644 gcc/testsuite/rust/compile/issue-1234.rs
 create mode 100644 gcc/testsuite/rust/compile/issue-1235.rs
 create mode 100644 gcc/testsuite/rust/compile/issue-1237.rs
 create mode 100644 gcc/testsuite/rust/compile/issue-1251.rs
 create mode 100644 gcc/testsuite/rust/compile/issue-1271.rs
 create mode 100644 gcc/testsuite/rust/compile/issue-1289.rs
 create mode 100644 gcc/testsuite/rust/compile/issue-1323-1.rs
 create mode 100644 gcc/testsuite/rust/compile/issue-1323-2.rs
 create mode 100644 gcc/testsuite/rust/compile/issue-1383.rs
 create mode 100644 gcc/testsuite/rust/compile/issue-1393.rs
 create mode 100644 gcc/testsuite/rust/compile/issue-1447.rs
 create mode 100644 gcc/testsuite/rust/compile/issue-407-2.rs
 create mode 100644 gcc/testsuite/rust/compile/issue-407.rs
 create mode 100644 gcc/testsuite/rust/compile/issue-557.rs
 create mode 100644 gcc/testsuite/rust/compile/issue-635-1.rs
 create mode 100644 gcc/testsuite/rust/compile/issue-635-2.rs
 create mode 100644 gcc/testsuite/rust/compile/lookup_err1.rs
 create mode 100644 gcc/testsuite/rust/compile/macro-issue1053-2.rs
 create mode 100644 gcc/testsuite/rust/compile/macro-issue1053.rs
 create mode 100644 gcc/testsuite/rust/compile/macro-issue1224.rs
 create mode 100644 gcc/testsuite/rust/compile/macro-issue1233.rs
 create mode 100644 gcc/testsuite/rust/compile/macro-issue1395-2.rs
 create mode 100644 gcc/testsuite/rust/compile/macro-issue1395.rs
 create mode 100644 gcc/testsuite/rust/compile/macro-issue1400-2.rs
 create mode 100644 gcc/testsuite/rust/compile/macro-issue1400.rs
 create mode 100644 gcc/testsuite/rust/compile/macro1.rs
 create mode 100644 gcc/testsuite/rust/compile/macro10.rs
 create mode 100644 gcc/testsuite/rust/compile/macro11.rs
 create mode 100644 gcc/testsuite/rust/compile/macro12.rs
 create mode 100644 gcc/testsuite/rust/compile/macro13.rs
 create mode 100644 gcc/testsuite/rust/compile/macro14.rs
 create mode 100644 gcc/testsuite/rust/compile/macro15.rs
 create mode 100644 gcc/testsuite/rust/compile/macro16.rs
 create mode 100644 gcc/testsuite/rust/compile/macro17.rs
 create mode 100644 gcc/testsuite/rust/compile/macro18.rs
 create mode 100644 gcc/testsuite/rust/compile/macro19.rs
 create mode 100644 gcc/testsuite/rust/compile/macro2.rs
 create mode 100644 gcc/testsuite/rust/compile/macro20.rs
 create mode 100644 gcc/testsuite/rust/compile/macro21.rs
 create mode 100644 gcc/testsuite/rust/compile/macro22.rs
 create mode 100644 gcc/testsuite/rust/compile/macro23.rs
 create mode 100644 gcc/testsuite/rust/compile/macro25.rs
 create mode 100644 gcc/testsuite/rust/compile/macro26.rs
 create mode 100644 gcc/testsuite/rust/compile/macro27.rs
 create mode 100644 gcc/testsuite/rust/compile/macro28.rs
 create mode 100644 gcc/testsuite/rust/compile/macro29.rs
 create mode 100644 gcc/testsuite/rust/compile/macro3.rs
 create mode 100644 gcc/testsuite/rust/compile/macro30.rs
 create mode 100644 gcc/testsuite/rust/compile/macro31.rs
 create mode 100644 gcc/testsuite/rust/compile/macro32.rs
 create mode 100644 gcc/testsuite/rust/compile/macro33.rs
 create mode 100644 gcc/testsuite/rust/compile/macro34.rs
 create mode 100644 gcc/testsuite/rust/compile/macro35.rs
 create mode 100644 gcc/testsuite/rust/compile/macro36.rs
 create mode 100644 gcc/testsuite/rust/compile/macro37.rs
 create mode 100644 gcc/testsuite/rust/compile/macro38.rs
 create mode 100644 gcc/testsuite/rust/compile/macro39.rs
 create mode 100644 gcc/testsuite/rust/compile/macro4.rs
 create mode 100644 gcc/testsuite/rust/compile/macro40.rs
 create mode 100644 gcc/testsuite/rust/compile/macro41.rs
 create mode 100644 gcc/testsuite/rust/compile/macro42.rs
 create mode 100644 gcc/testsuite/rust/compile/macro5.rs
 create mode 100644 gcc/testsuite/rust/compile/macro6.rs
 create mode 100644 gcc/testsuite/rust/compile/macro7.rs
 create mode 100644 gcc/testsuite/rust/compile/macro8.rs
 create mode 100644 gcc/testsuite/rust/compile/macro9.rs
 create mode 100644 gcc/testsuite/rust/compile/macro_return.rs
 create mode 100644 gcc/testsuite/rust/compile/match1.rs
 create mode 100644 gcc/testsuite/rust/compile/match2.rs
 create mode 100644 gcc/testsuite/rust/compile/match3.rs
 create mode 100644 gcc/testsuite/rust/compile/match4.rs
 create mode 100644 gcc/testsuite/rust/compile/match5.rs
 create mode 100644 gcc/testsuite/rust/compile/match6.rs
 create mode 100644 gcc/testsuite/rust/compile/match7.rs
 create mode 100644 gcc/testsuite/rust/compile/method1.rs
 create mode 100644 gcc/testsuite/rust/compile/method2.rs
 create mode 100644 gcc/testsuite/rust/compile/mismatch-crate-name.rs
 create mode 100644 gcc/testsuite/rust/compile/missing_middle/both_path.rs
 create mode 100644 gcc/testsuite/rust/compile/missing_middle/explicit.not.rs
 create mode 100644 gcc/testsuite/rust/compile/missing_middle/inner_path.rs
 create mode 100644 gcc/testsuite/rust/compile/missing_middle/other.rs
 create mode 100644 gcc/testsuite/rust/compile/missing_middle/outer_path.rs
 create mode 100644 gcc/testsuite/rust/compile/missing_middle/sub/mod.rs
 create mode 100644 gcc/testsuite/rust/compile/missing_return1.rs
 create mode 100644 gcc/testsuite/rust/compile/mod_missing_middle.rs
 create mode 100644 gcc/testsuite/rust/compile/never_type_err1.rs
 create mode 100644 gcc/testsuite/rust/compile/privacy1.rs
 create mode 100644 gcc/testsuite/rust/compile/privacy2.rs
 create mode 100644 gcc/testsuite/rust/compile/privacy3.rs
 create mode 100644 gcc/testsuite/rust/compile/privacy4.rs
 create mode 100644 gcc/testsuite/rust/compile/privacy5.rs
 create mode 100644 gcc/testsuite/rust/compile/privacy6.rs
 create mode 100644 gcc/testsuite/rust/compile/pub_restricted_1.rs
 create mode 100644 gcc/testsuite/rust/compile/pub_restricted_2.rs
 create mode 100644 gcc/testsuite/rust/compile/pub_restricted_3.rs
 create mode 100644 gcc/testsuite/rust/compile/raw_identifiers_bad_keywords.rs
 create mode 100644 gcc/testsuite/rust/compile/raw_identifiers_underscore.rs
 create mode 100644 gcc/testsuite/rust/compile/rawbytestring.rs
 create mode 100644 gcc/testsuite/rust/compile/redef_error1.rs
 create mode 100644 gcc/testsuite/rust/compile/redef_error2.rs
 create mode 100644 gcc/testsuite/rust/compile/redef_error3.rs
 create mode 100644 gcc/testsuite/rust/compile/redef_error4.rs
 create mode 100644 gcc/testsuite/rust/compile/redef_error5.rs
 create mode 100644 gcc/testsuite/rust/compile/redef_error6.rs
 create mode 100644 gcc/testsuite/rust/compile/reference1.rs
 create mode 100644 gcc/testsuite/rust/compile/self-path1.rs
 create mode 100644 gcc/testsuite/rust/compile/self-path2.rs
 create mode 100644 gcc/testsuite/rust/compile/shadow1.rs
 create mode 100644 gcc/testsuite/rust/compile/specify-crate-name.rs
 create mode 100644 gcc/testsuite/rust/compile/static_var1.rs
 create mode 100644 gcc/testsuite/rust/compile/stmt_with_block_err1.rs
 create mode 100644 gcc/testsuite/rust/compile/struct_align1.rs
 create mode 100644 gcc/testsuite/rust/compile/struct_align2.rs
 create mode 100644 gcc/testsuite/rust/compile/struct_init1.rs
 create mode 100644 gcc/testsuite/rust/compile/struct_pack1.rs
 create mode 100644 gcc/testsuite/rust/compile/struct_pack2.rs
 create mode 100644 gcc/testsuite/rust/compile/syntax-only.rs
 create mode 100644 gcc/testsuite/rust/compile/test_mod.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/all_doc_comment_line_blocks.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/all_doc_comment_line_blocks_crlf.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/arithmetic_expressions1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/array_const_fold_1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/array_const_fold_2.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/array_function.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/array_type_infer.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/array_zero_length.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/arrays1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/arrays2.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/arrays3.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/arrays4.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/arrays5.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/arrays6.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/arrays_index1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/arrays_index2.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/arrays_index3.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/as_bool_char.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/associated_types1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/autoderef1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/block_expr1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/block_expr2.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/block_expr3.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/block_expr4.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/block_expr5.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/block_expr_parser_bug.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/bom.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/bom_comment.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/bom_shebang.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/bom_whitespace.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/bools_eq.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/borrow1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/borrow_function.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/break_function.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/byte_char_str.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/byte_str.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/cast1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/cast2.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/cast3.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/cfg_attr.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/char1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/check-doc-attr-string.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/coercion1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/coercion2.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/comparison_expr1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/compile.exp
 create mode 100644 gcc/testsuite/rust/compile/torture/compound_assignment_expr1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/conditional.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/constant1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/constant2.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/constant3.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/deadcode1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/deadcode2.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/deref1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/deref_function.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/doc_comment.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/enum1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/extern_mod1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/extern_mod2.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/float1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/float_types.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/forward_decl_1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/forward_decl_2.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/forward_decl_3-unsafe.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/forward_decl_3.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/forward_decl_4.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/forward_decl_5.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/func1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/func2.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/function_reference1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/function_reference2.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/function_reference3.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/function_reference4.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/generics1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/generics10.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/generics11.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/generics12.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/generics13.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/generics14.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/generics15.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/generics16.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/generics17.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/generics18.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/generics19.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/generics2.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/generics20.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/generics21.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/generics22.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/generics23.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/generics24.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/generics25.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/generics26.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/generics27.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/generics28.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/generics29.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/generics3.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/generics30.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/generics31.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/generics32.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/generics4.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/generics5.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/generics6.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/generics7.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/generics8.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/generics9.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/grouped_expr_function.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/identifier-missing-impl-1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/if.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/if_elif.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/if_elif_else_expr1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/if_else.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/ifunaryexpr.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/impl_block1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/impl_block2.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/impl_block3.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/impl_block_unused.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/implicit_returns1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/infer_type1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/inner_attributes.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/integer_inference_var1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/integer_inference_var2.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/integer_inference_var3.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/integer_inference_var4.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/integer_inference_var5.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/integer_types.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/intrinsics-1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/intrinsics-2.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/isolated_cr_block_comment.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/isolated_cr_line_comment.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/issue-1024.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/issue-1075.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/issue-1432.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/issue-1434.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/issue-368.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/issue-808.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/issue-862.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/issue-893-2.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/issue-893.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/lazybooleanexpr_function.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/lifetime1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/literals1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/loop1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/loop2.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/loop3.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/loop4.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/loop5.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/loop6.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/loop7.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/macro-issue1403.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/macro-issue1426.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/macro_as_expr.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/match1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/methods1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/methods2.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/methods3.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/mod-nameresolve.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/mod1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/mod2.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/mod3.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/modules/mod.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/modules/valid_path.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/must_use1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/must_use2.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/name_resolve1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/negation_function.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/nested_fn1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/nested_fn2.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/nested_struct1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/never_type1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/not_shebang.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/not_shebang_block_comment.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/not_shebang_comment.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/not_shebang_multiline_comment.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/not_shebang_spaces.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/parameter_usage1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/parens1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/pointer1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/primconsts.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/prims_struct_eq.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/range-lang-item1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/raw_identifiers.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/raw_identifiers_keywords.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/recursive_fn1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/return_function.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/scoping1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/self_type1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/shadow1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/shadow2.rs
 create mode 100755 gcc/testsuite/rust/compile/torture/shebang.rs
 create mode 100755 gcc/testsuite/rust/compile/torture/shebang_plus_attr.rs
 create mode 100755 gcc/testsuite/rust/compile/torture/shebang_plus_attr2.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/static_function.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/static_var1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/stmt_with_block1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/str1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/struct_access1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/struct_base_init_1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/struct_decl.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/struct_init.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/struct_init_10.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/struct_init_11.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/struct_init_2.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/struct_init_3.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/struct_init_4.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/struct_init_5.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/struct_init_6.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/struct_init_7.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/struct_init_8.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/struct_init_9.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/top_attr.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/traits1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/traits10.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/traits11.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/traits12.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/traits13.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/traits14.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/traits15.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/traits16.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/traits17.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/traits18.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/traits19.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/traits2.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/traits3.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/traits4.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/traits5.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/traits6.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/traits7.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/traits8.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/traits9.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/transmute-size-check-1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/transmute1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/tuple1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/tuple2.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/tuple3.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/tuple_enum_variants.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/tuple_field_access.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/tuple_function.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/tuple_index.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/tuple_struct1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/tuple_struct2.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/tuple_struct_unit.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/tuple_struct_unused.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/type-alias1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/type-alias2.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/type_infer1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/type_infer2.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/type_infer3.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/type_infer4.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/type_infer5.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/type_infer6.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/unary_operators.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/undended-string-1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/undended-string-2.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/underscore_id.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/union.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/union_union.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/unit_type1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/unit_type2.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/unit_type3.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/unit_type4.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/unit_type5.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/unsafe1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/unsafe2.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/unsafe3.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/unsafe4.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/unused.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/unused1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/unused_struct.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/unused_struct_field.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/usize1.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/very-broken-attr-string.rs
 create mode 100644 gcc/testsuite/rust/compile/torture/while_function.rs
 create mode 100644 gcc/testsuite/rust/compile/traits1.rs
 create mode 100644 gcc/testsuite/rust/compile/traits10.rs
 create mode 100644 gcc/testsuite/rust/compile/traits11.rs
 create mode 100644 gcc/testsuite/rust/compile/traits12.rs
 create mode 100644 gcc/testsuite/rust/compile/traits2.rs
 create mode 100644 gcc/testsuite/rust/compile/traits3.rs
 create mode 100644 gcc/testsuite/rust/compile/traits4.rs
 create mode 100644 gcc/testsuite/rust/compile/traits5.rs
 create mode 100644 gcc/testsuite/rust/compile/traits6.rs
 create mode 100644 gcc/testsuite/rust/compile/traits7.rs
 create mode 100644 gcc/testsuite/rust/compile/traits8.rs
 create mode 100644 gcc/testsuite/rust/compile/traits9.rs
 create mode 100644 gcc/testsuite/rust/compile/tuple1.rs
 create mode 100644 gcc/testsuite/rust/compile/tuple_struct1.rs
 create mode 100644 gcc/testsuite/rust/compile/tuple_struct2.rs
 create mode 100644 gcc/testsuite/rust/compile/tuple_struct3.rs
 create mode 100644 gcc/testsuite/rust/compile/type-alias1.rs
 create mode 100644 gcc/testsuite/rust/compile/type-bindings1.rs
 create mode 100644 gcc/testsuite/rust/compile/unary_negation.rs
 create mode 100644 gcc/testsuite/rust/compile/unary_not.rs
 create mode 100644 gcc/testsuite/rust/compile/unconstrained_type_param.rs
 create mode 100644 gcc/testsuite/rust/compile/unicode_escape.rs
 create mode 100644 gcc/testsuite/rust/compile/unsafe1.rs
 create mode 100644 gcc/testsuite/rust/compile/unsafe10.rs
 create mode 100644 gcc/testsuite/rust/compile/unsafe2.rs
 create mode 100644 gcc/testsuite/rust/compile/unsafe3.rs
 create mode 100644 gcc/testsuite/rust/compile/unsafe4.rs
 create mode 100644 gcc/testsuite/rust/compile/unsafe5.rs
 create mode 100644 gcc/testsuite/rust/compile/unsafe6.rs
 create mode 100644 gcc/testsuite/rust/compile/unsafe7.rs
 create mode 100644 gcc/testsuite/rust/compile/unsafe8.rs
 create mode 100644 gcc/testsuite/rust/compile/unsafe9.rs
 create mode 100644 gcc/testsuite/rust/compile/unterminated_c_comment.rs
 create mode 100644 gcc/testsuite/rust/compile/use_1.rs
 create mode 100644 gcc/testsuite/rust/compile/usize1.rs
 create mode 100644 gcc/testsuite/rust/compile/xfail/lifetime_param.rs
 create mode 100644 gcc/testsuite/rust/compile/xfail/struct_field_vis.rs
 create mode 100644 gcc/testsuite/rust/compile/xfail/xfail.exp

diff --git a/gcc/testsuite/rust/compile/abi-options1.rs b/gcc/testsuite/rust/compile/abi-options1.rs
new file mode 100644
index 00000000000..a4b6241dc15
--- /dev/null
+++ b/gcc/testsuite/rust/compile/abi-options1.rs
@@ -0,0 +1,7 @@
+extern "foobar" {
+    // { dg-error "unknown ABI option" "" { target *-*-* } .-1 }
+    fn printf(s: *const i8, ...);
+}
+
+pub extern "baz" fn test() {}
+// { dg-error "unknown ABI option" "" { target *-*-* } .-1 }
diff --git a/gcc/testsuite/rust/compile/array3.rs b/gcc/testsuite/rust/compile/array3.rs
new file mode 100644
index 00000000000..a56be9a0e8b
--- /dev/null
+++ b/gcc/testsuite/rust/compile/array3.rs
@@ -0,0 +1,4 @@
+fn foo(state: &mut [u32; 16], a: usize) {
+    // { dg-warning "function is never used: .foo." "" { target *-*-* } .-1 }
+    state[a] = 1;
+}
diff --git a/gcc/testsuite/rust/compile/array_empty_list.rs b/gcc/testsuite/rust/compile/array_empty_list.rs
new file mode 100644
index 00000000000..76e082a6d57
--- /dev/null
+++ b/gcc/testsuite/rust/compile/array_empty_list.rs
@@ -0,0 +1,4 @@
+fn main() {
+    let arr = [];
+    // { dg-error "type annotations needed" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/arrays1.rs b/gcc/testsuite/rust/compile/arrays1.rs
new file mode 100644
index 00000000000..714a6be7afb
--- /dev/null
+++ b/gcc/testsuite/rust/compile/arrays1.rs
@@ -0,0 +1,4 @@
+fn main() {
+    let xs: [i32; 5] = [1, 2, 3, 4, 5];
+    let a: bool = xs[0]; // { dg-error "expected .bool. got .i32." }
+}
diff --git a/gcc/testsuite/rust/compile/arrays2.rs b/gcc/testsuite/rust/compile/arrays2.rs
new file mode 100644
index 00000000000..c96f4f7d820
--- /dev/null
+++ b/gcc/testsuite/rust/compile/arrays2.rs
@@ -0,0 +1,5 @@
+// { dg-additional-options "-w" }
+fn main() {
+    let array: [i32; 5] = [1, 2, 3];
+    // { dg-error "expected an array with a fixed size of 5 elements, found one with 3 elements" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/attr-mismatch-crate-name.rs b/gcc/testsuite/rust/compile/attr-mismatch-crate-name.rs
new file mode 100644
index 00000000000..1d406031fee
--- /dev/null
+++ b/gcc/testsuite/rust/compile/attr-mismatch-crate-name.rs
@@ -0,0 +1,4 @@
+// { dg-additional-options "-fdump-tree-gimple" }
+#![crate_name = "specified_name"]
+// { dg-final { scan-tree-dump-times {specified_name::main} 1 gimple } }
+fn main() {}
diff --git a/gcc/testsuite/rust/compile/attr_cold.rs b/gcc/testsuite/rust/compile/attr_cold.rs
new file mode 100644
index 00000000000..f705ea9b2ff
--- /dev/null
+++ b/gcc/testsuite/rust/compile/attr_cold.rs
@@ -0,0 +1,12 @@
+// { dg-additional-options "-fdump-tree-gimple" }
+#[cold]
+fn cold_function() -> i32 {
+    42
+}
+
+fn main() -> i32 {
+    // { dg-final { scan-tree-dump-times {__attribute__\(\(cdecl, cold\)\)} 1 gimple } }
+    cold_function();
+
+    0
+}
diff --git a/gcc/testsuite/rust/compile/attr_deprecated.rs b/gcc/testsuite/rust/compile/attr_deprecated.rs
new file mode 100644
index 00000000000..01bc9c41502
--- /dev/null
+++ b/gcc/testsuite/rust/compile/attr_deprecated.rs
@@ -0,0 +1,14 @@
+#[deprecated(since="1.0", note="do not use this function")]
+fn test1() {}
+
+#[deprecated]
+fn test() {}
+
+#[deprecated = "a different message"]
+fn test2() {}
+
+fn main() {
+    test(); // { dg-warning ".attr_deprecated::test. is deprecated" }
+    test1(); // { dg-warning ".attr_deprecated::test1. is deprecated: do not use this function" }
+    test2(); // { dg-warning ".attr_deprecated::test2. is deprecated: a different message" }
+}
diff --git a/gcc/testsuite/rust/compile/attr_deprecated_2.rs b/gcc/testsuite/rust/compile/attr_deprecated_2.rs
new file mode 100644
index 00000000000..66f4ce3b076
--- /dev/null
+++ b/gcc/testsuite/rust/compile/attr_deprecated_2.rs
@@ -0,0 +1,11 @@
+#[deprecated(since="1.0")]
+fn test1() {}
+
+// { dg-excess-errors "unknown meta item ...." }
+#[deprecated(invalid="invalid")]
+fn test2() {}
+
+fn main() {
+    test1(); // { dg-warning ".attr_deprecated_2::test1. is deprecated" }
+    test2();
+}
diff --git a/gcc/testsuite/rust/compile/bad-crate-name.rs b/gcc/testsuite/rust/compile/bad-crate-name.rs
new file mode 100644
index 00000000000..6c59c255cc2
--- /dev/null
+++ b/gcc/testsuite/rust/compile/bad-crate-name.rs
@@ -0,0 +1,4 @@
+// { dg-additional-options "-frust-crate=bad+name" }
+// { dg-excess-errors "invalid crate name: ...." }
+// { dg-excess-errors "unrecognized command-line option ...." }
+fn main() {}
diff --git a/gcc/testsuite/rust/compile/bad=file-name.rs b/gcc/testsuite/rust/compile/bad=file-name.rs
new file mode 100644
index 00000000000..cfbebb0698d
--- /dev/null
+++ b/gcc/testsuite/rust/compile/bad=file-name.rs
@@ -0,0 +1,7 @@
+// { dg-additional-options "-fdump-tree-gimple -frust-crate=good_name" }
+pub fn does_nothing() {}
+fn main() {
+    does_nothing()
+}
+// { dg-final { scan-tree-dump-times {good_name::does_nothing} 2 gimple } }
+// { dg-final { scan-tree-dump-times {good_name::main} 1 gimple } }
diff --git a/gcc/testsuite/rust/compile/bad_as_bool_char.rs b/gcc/testsuite/rust/compile/bad_as_bool_char.rs
new file mode 100644
index 00000000000..91a28eebe00
--- /dev/null
+++ b/gcc/testsuite/rust/compile/bad_as_bool_char.rs
@@ -0,0 +1,18 @@
+pub fn main ()
+{
+  let t = true;
+  let f = false;
+  let fone = t as f32;   // { dg-error "invalid cast" }
+  let fzero = f as f64;  // { dg-error "invalid cast" }
+
+  let nb = 0u8 as bool;  // { dg-error "invalid cast" }
+  let nc = true as char; // { dg-error "invalid cast" }
+
+  let a = 'a';
+  let b = 'b';
+  let fa = a as f32;     // { dg-error "invalid cast" }
+  let bb = b as bool;    // { dg-error "invalid cast" }
+
+  let t32: u32 = 33;
+  let ab = t32 as char;  // { dg-error "invalid cast" }
+}
diff --git a/gcc/testsuite/rust/compile/bad_file_name.txt.rs b/gcc/testsuite/rust/compile/bad_file_name.txt.rs
new file mode 100644
index 00000000000..56e2093b27c
--- /dev/null
+++ b/gcc/testsuite/rust/compile/bad_file_name.txt.rs
@@ -0,0 +1,3 @@
+// { dg-excess-errors "invalid crate name: ...." }
+// { dg-bogus "unrecognized command-line option ...." }
+fn main() {}
diff --git a/gcc/testsuite/rust/compile/bad_inner_doc.rs b/gcc/testsuite/rust/compile/bad_inner_doc.rs
new file mode 100644
index 00000000000..cfd166ce3ec
--- /dev/null
+++ b/gcc/testsuite/rust/compile/bad_inner_doc.rs
@@ -0,0 +1,15 @@
+pub fn main ()
+{
+  //! inner doc allowed
+  let _x = 42;
+  // { dg-error "inner doc" "" { target *-*-* } .+1 }
+  //! inner doc disallowed
+  mod module
+  {
+    /*! inner doc allowed */
+    /// outer doc allowed
+    // { dg-error "inner doc" "" { target *-*-* } .+1 }
+    /*! but inner doc not here */
+    mod x { }
+  }
+}
diff --git a/gcc/testsuite/rust/compile/bad_pub_enumitems.rs b/gcc/testsuite/rust/compile/bad_pub_enumitems.rs
new file mode 100644
index 00000000000..e7fd5edb981
--- /dev/null
+++ b/gcc/testsuite/rust/compile/bad_pub_enumitems.rs
@@ -0,0 +1,47 @@
+pub enum E
+{
+  pub A { a: i32 }, // { dg-error "visibility qualifier" }
+  B (u8),
+  pub C, // { dg-error "visibility qualifier" }
+  D
+}
+
+enum E1
+{
+  A,
+  pub B = 42, // { dg-error "visibility qualifier" }
+  C = 3,
+  D,
+  pub E // { dg-error "visibility qualifier" }
+}
+
+enum E2
+{
+  pub A (u8, i32, u64), // { dg-error "visibility qualifier" }
+  B { a: u8, a: u8 }  // { dg-error "duplicate field" }}
+}
+
+fn main ()
+{
+  enum EE
+    {
+      Alpha { alpha: i32 },
+      pub Beta (u8), // { dg-error "visibility qualifier" }
+      pub Gamma, // { dg-error "visibility qualifier" }
+      Delta { delta: u32 }
+    }
+
+  enum EE1
+    {
+      pub Alpha, // { dg-error "visibility qualifier" }
+      Beta = 41,
+      pub Gamma = 3, // { dg-error "visibility qualifier" }
+      Delta,
+    }
+
+  enum E2
+    {
+      Alpha { a: u8, a: u8 },  // { dg-error "duplicate field" }}
+      pub Beta (u8, i32, u64) // { dg-error "visibility qualifier" }
+    }
+}
diff --git a/gcc/testsuite/rust/compile/bad_stmt_enums.rs b/gcc/testsuite/rust/compile/bad_stmt_enums.rs
new file mode 100644
index 00000000000..7b09a94fd27
--- /dev/null
+++ b/gcc/testsuite/rust/compile/bad_stmt_enums.rs
@@ -0,0 +1,22 @@
+fn main ()
+{
+  enum EE
+    {
+      Alpha { alpha: i32 },
+      pub Beta (u8),
+      pub Gamma,
+      Gamma { gamma: u32 } // { dg-error "redefined" }
+    }
+
+  struct EE2 { }
+  enum EE2 { } // { dg-error "redefined" }
+
+  enum EE1
+    {
+      pub Alpha,
+      Beta = 41,
+      Beta = 42, // { dg-error "redefined" }
+      pub Gamma = 3,
+      D,
+    }
+}
diff --git a/gcc/testsuite/rust/compile/bad_toplevel_enums.rs b/gcc/testsuite/rust/compile/bad_toplevel_enums.rs
new file mode 100644
index 00000000000..b655e30a93d
--- /dev/null
+++ b/gcc/testsuite/rust/compile/bad_toplevel_enums.rs
@@ -0,0 +1,19 @@
+pub enum E
+{
+  pub A { a: i32 },
+  B (u8),
+  pub C,
+  B // { dg-error "redefined" }
+}
+
+enum E2 { }
+struct E2 { } // { dg-error "redefined" }
+
+enum E1
+{
+  A,
+  pub B = 42,
+  C = 3,
+  A { a: u8 }, // { dg-error "redefined" }
+  pub D
+}
diff --git a/gcc/testsuite/rust/compile/bad_tuple_index.rs b/gcc/testsuite/rust/compile/bad_tuple_index.rs
new file mode 100644
index 00000000000..c3bd1e91d10
--- /dev/null
+++ b/gcc/testsuite/rust/compile/bad_tuple_index.rs
@@ -0,0 +1,66 @@
+fn main()
+{
+  // tuples
+  let z = ();
+
+  let o = (0,);
+  /* Binary, Octal and Hex literals are invalid.  */
+  let _fb = o.0b0; // { dg-error "tuple index should be a pure decimal literal" }
+  let _fo = o.0o0; // { dg-error "tuple index should be a pure decimal literal" }
+  let _fh = o.0x0; // { dg-error "tuple index should be a pure decimal literal" }
+
+  /* No underscores.  */
+  let _fua = o.0_; // { dg-error "tuple index should be a pure decimal literal" }
+
+  /* Suffix is not allowed.  */
+  let _fu8 = o.0u8; // { dg-error "tuple index should be a pure decimal literal" }
+  let _fi8 = o.0i8; // { dg-error "tuple index should be a pure decimal literal" }
+  let _fu16 = o.0u16; // { dg-error "tuple index should be a pure decimal literal" }
+  let _fi16 = o.0i16; // { dg-error "tuple index should be a pure decimal literal" }
+  let _fu32 = o.0u32; // { dg-error "tuple index should be a pure decimal literal" }
+  let _fi32 = o.0i32; // { dg-error "tuple index should be a pure decimal literal" }
+  let _fu64 = o.0u64; // { dg-error "tuple index should be a pure decimal literal" }
+  let _fi64 = o.0i64; // { dg-error "tuple index should be a pure decimal literal" }
+  let _fu128 = o.0u128; // { dg-error "tuple index should be a pure decimal literal" }
+  let _fi128 = o.0i128; // { dg-error "tuple index should be a pure decimal literal" }
+  let _fusize = o.0usize; // { dg-error "tuple index should be a pure decimal literal" }
+  let _fisize = o.0isize; // { dg-error "tuple index should be a pure decimal literal" }
+
+  let t = (0,1);
+  /* No extra zero prefix.  */
+  let _s = t.01; // { dg-error "tuple index should be a pure decimal literal" }
+
+  let m = (0,1,2,3,4,5,6,7,8,9,10);
+  /* No extra zero prefix.  */
+  let _l = m.010; // { dg-error "tuple index should be a pure decimal literal" }
+
+  /* No underscores.  */
+  let _lu = m.1_0; // { dg-error "tuple index should be a pure decimal literal" }
+
+  // tuple structs
+  struct E();
+  let _e = E();
+
+  struct O(i32);
+  let so = O(0);
+  /* No leading zeros, no underscores.  */
+  let _sf = so.0_0; // { dg-error "tuple index should be a pure decimal literal" }
+  /* Binary, Octal and Hex literals are invalid.  */
+  let _sb = so.0b0; // { dg-error "tuple index should be a pure decimal literal" }
+  let _so = so.0o0; // { dg-error "tuple index should be a pure decimal literal" }
+  let _sh = so.0x0; // { dg-error "tuple index should be a pure decimal literal" }
+
+  struct T(i32,i32);
+  let st = T(0,1);
+  /* Suffix is not allowed.  */
+  let _stfu32 = st.1u32; // { dg-error "tuple index should be a pure decimal literal" }
+  let _stfi32 = st.1i32; // { dg-error "tuple index should be a pure decimal literal" }
+
+  struct M(i32,i32,i32,i32,i32,i32,i32,i32,i32,i32,i32);
+  let sm = M(0,1,2,3,4,5,6,7,8,9,10);
+  /* No underscores. */
+  let _sl2 = sm.1_0; // { dg-error "tuple index should be a pure decimal literal" }
+  let _sl3 = sm.10_; // { dg-error "tuple index should be a pure decimal literal" }
+
+  z
+}
diff --git a/gcc/testsuite/rust/compile/bad_type1.rs b/gcc/testsuite/rust/compile/bad_type1.rs
new file mode 100644
index 00000000000..93de439704f
--- /dev/null
+++ b/gcc/testsuite/rust/compile/bad_type1.rs
@@ -0,0 +1,3 @@
+fn main() {
+    let logical: bool = 123; // { dg-error "expected .bool. got .<integer>." }
+}
diff --git a/gcc/testsuite/rust/compile/bad_type2.rs b/gcc/testsuite/rust/compile/bad_type2.rs
new file mode 100644
index 00000000000..e47b8aac0e7
--- /dev/null
+++ b/gcc/testsuite/rust/compile/bad_type2.rs
@@ -0,0 +1,14 @@
+fn test(x: i32) -> i32 {
+    return x + 1;
+}
+
+fn main() {
+    let mut an_integer = 5;
+    an_integer = test(1) + 3;
+
+    let mut x;
+    x = 1;
+    x = true; // { dg-error "expected .<integer>. got .bool." }
+
+    let call_test = test(1);
+}
diff --git a/gcc/testsuite/rust/compile/break1.rs b/gcc/testsuite/rust/compile/break1.rs
new file mode 100644
index 00000000000..91cabffa894
--- /dev/null
+++ b/gcc/testsuite/rust/compile/break1.rs
@@ -0,0 +1,6 @@
+fn main() {
+    let a;
+    a = 1;
+    break a; // { dg-error "cannot 'break' outside of a loop" }
+    // { dg-error "failed to type resolve expression" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/break2.rs b/gcc/testsuite/rust/compile/break2.rs
new file mode 100644
index 00000000000..5ac806aeb9e
--- /dev/null
+++ b/gcc/testsuite/rust/compile/break2.rs
@@ -0,0 +1,15 @@
+fn main() {
+    let mut a = 1;
+    let mut b = 1;
+
+    let mut c;
+    while b > 10 {
+        if (b == 2) {
+            break b;  // { dg-error "can only break with a value inside 'loop'" }
+            // { dg-error "failed to type resolve expression" "" { target *-*-* } .-1 }
+        }
+        c = a + b;
+        a = b;
+        b = c;
+    }
+}
diff --git a/gcc/testsuite/rust/compile/builtin_macro_compile_error.rs b/gcc/testsuite/rust/compile/builtin_macro_compile_error.rs
new file mode 100644
index 00000000000..9d224406a3e
--- /dev/null
+++ b/gcc/testsuite/rust/compile/builtin_macro_compile_error.rs
@@ -0,0 +1,13 @@
+#[rustc_builtin_macro]
+macro_rules! compile_error {
+  () => {{}};
+}
+
+fn main () {
+  let message = "error message";
+  compile_error! (message); // { dg-error "argument must be a string literal" "" }
+  compile_error! (); // { dg-error "macro takes 1 argument" "" }
+  compile_error! ("a", "b"); // { dg-error "macro takes 1 argument" "" }
+  compile_error! ("expected error message"); // { dg-error "expected error message" }
+  compile_error! ("expected error message",); // { dg-error "expected error message" }
+}
diff --git a/gcc/testsuite/rust/compile/builtin_macro_concat.rs b/gcc/testsuite/rust/compile/builtin_macro_concat.rs
new file mode 100644
index 00000000000..9b878af764d
--- /dev/null
+++ b/gcc/testsuite/rust/compile/builtin_macro_concat.rs
@@ -0,0 +1,17 @@
+#[rustc_builtin_macro]
+macro_rules! concat {
+    () => {{}};
+}
+
+fn main() {
+    let not_literal = "identifier";
+    concat!();
+    concat! (,); // { dg-error "argument must be a constant literal" }
+    concat!(not_literal); // { dg-error "argument must be a constant literal" }
+    concat!("message");
+    concat!("message",);
+    concat!("message", 1, true, false, 1.0, 10usize, 2000u64);
+    concat!("message", 1, true, false, 1.0, 10usize, 2000u64,);
+    concat! ("m", not_literal); // { dg-error "argument must be a constant literal" }
+    concat!(not_literal invalid 'm' !!,); // { dg-error "argument must be a constant literal" }
+}
diff --git a/gcc/testsuite/rust/compile/builtin_macro_env.rs b/gcc/testsuite/rust/compile/builtin_macro_env.rs
new file mode 100644
index 00000000000..289e6db2cf1
--- /dev/null
+++ b/gcc/testsuite/rust/compile/builtin_macro_env.rs
@@ -0,0 +1,20 @@
+#[rustc_builtin_macro]
+macro_rules! env {
+  () => {{}};
+}
+
+fn main () {
+  let message = "error message";
+  env! (message); // { dg-error "argument must be a string literal" "" }
+  env! (); // { dg-error "env! takes 1 or 2 arguments" "" }
+  env! (,); // { dg-error "argument must be a string literal" "" }
+  env! (1); // { dg-error "argument must be a string literal" "" }
+  env! ("NOT_DEFINED"); // { dg-error "environment variable 'NOT_DEFINED' not defined" "" }
+  env! ("NOT_DEFINED",); // { dg-error "environment variable 'NOT_DEFINED' not defined" "" }
+  env! ("NOT_DEFINED", 1); // { dg-error "argument must be a string literal" "" }
+  env! ("NOT_DEFINED", "two", "three"); // { dg-error "env! takes 1 or 2 arguments" "" }
+  env! ("NOT_DEFINED" "expected error message"); // { dg-error "expected token: ','" "" }
+  env! ("NOT_DEFINED", "expected error message"); // { dg-error "expected error message" "" }
+  env! ("NOT_DEFINED", "expected error message",); // { dg-error "expected error message" "" }
+  env! (1, "two"); // { dg-error "argument must be a string literal" "" }
+}
diff --git a/gcc/testsuite/rust/compile/builtin_macro_include_bytes.rs b/gcc/testsuite/rust/compile/builtin_macro_include_bytes.rs
new file mode 100644
index 00000000000..38716d33bcd
--- /dev/null
+++ b/gcc/testsuite/rust/compile/builtin_macro_include_bytes.rs
@@ -0,0 +1,13 @@
+#[rustc_builtin_macro]
+macro_rules! include_bytes {
+  () => {{}};
+}
+
+fn main () {
+  let file = "include.txt";
+  include_bytes! (file); // { dg-error "argument must be a string literal" "" }
+  include_bytes! (); // { dg-error "macro takes 1 argument" "" }
+  include_bytes! ("foo.txt", "bar.txt"); // { dg-error "macro takes 1 argument" "" }
+  include_bytes! ("builtin_macro_include_bytes.rs"); // ok
+  include_bytes! ("builtin_macro_include_bytes.rs",); // trailing comma ok
+}
diff --git a/gcc/testsuite/rust/compile/builtin_macro_include_str.rs b/gcc/testsuite/rust/compile/builtin_macro_include_str.rs
new file mode 100644
index 00000000000..38f5e3b7334
--- /dev/null
+++ b/gcc/testsuite/rust/compile/builtin_macro_include_str.rs
@@ -0,0 +1,13 @@
+#[rustc_builtin_macro]
+macro_rules! include_str {
+  () => {{}};
+}
+
+fn main () {
+  let file = "include.txt";
+  include_str! (file); // { dg-error "argument must be a string literal" "" }
+  include_str! (); // { dg-error "macro takes 1 argument" "" }
+  include_str! ("foo.txt", "bar.txt"); // { dg-error "macro takes 1 argument" "" }
+  include_str! ("builtin_macro_include_str.rs"); // ok
+  include_str! ("builtin_macro_include_str.rs",); // trailing comma ok
+}
diff --git a/gcc/testsuite/rust/compile/builtin_macro_not_found.rs b/gcc/testsuite/rust/compile/builtin_macro_not_found.rs
new file mode 100644
index 00000000000..1a3228b9284
--- /dev/null
+++ b/gcc/testsuite/rust/compile/builtin_macro_not_found.rs
@@ -0,0 +1,4 @@
+#[rustc_builtin_macro]
+macro_rules! crabby_crab_carb { // { dg-error "cannot find a built-in macro with name .crabby_crab_carb." }
+    () => {{}};
+}
diff --git a/gcc/testsuite/rust/compile/bytecharstring.rs b/gcc/testsuite/rust/compile/bytecharstring.rs
new file mode 100644
index 00000000000..9242e2c5a0b
--- /dev/null
+++ b/gcc/testsuite/rust/compile/bytecharstring.rs
@@ -0,0 +1,8 @@
+fn main ()
+{
+  let _bc = b'\x80';
+  let _bs = b"foo\x80bar";
+
+  let _c = '\xef';        // { dg-error "out of range" }
+  let _s = "Foo\xEFBar";  // { dg-error "out of range" }
+}
diff --git a/gcc/testsuite/rust/compile/canonical_paths1.rs b/gcc/testsuite/rust/compile/canonical_paths1.rs
new file mode 100644
index 00000000000..193e7b5b698
--- /dev/null
+++ b/gcc/testsuite/rust/compile/canonical_paths1.rs
@@ -0,0 +1,25 @@
+// { dg-additional-options "-w -fdump-tree-gimple -frust-crate=example" }
+struct Foo(i32);
+
+trait TR {
+    fn test(&self) -> i32;
+}
+
+mod A {
+    impl ::Foo {
+        pub fn test(self) {}
+        // { dg-final { scan-tree-dump-times {example::A::<impl example::Foo>::test} 2 gimple } }
+    }
+
+    impl ::TR for ::Foo {
+        fn test(&self) -> i32 {
+            // { dg-final { scan-tree-dump-times {example::A::<impl example::Foo as example::TR>::test} 1 gimple } }
+            self.0
+        }
+    }
+}
+
+pub fn test() {
+    let a = Foo(123);
+    a.test();
+}
diff --git a/gcc/testsuite/rust/compile/cast1.rs b/gcc/testsuite/rust/compile/cast1.rs
new file mode 100644
index 00000000000..74c4b1eaac4
--- /dev/null
+++ b/gcc/testsuite/rust/compile/cast1.rs
@@ -0,0 +1,5 @@
+fn main() {
+    let a: i32 = 123;
+    let b = a as char;
+    // { dg-error "invalid cast .i32. to .char." "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/cfg1.rs b/gcc/testsuite/rust/compile/cfg1.rs
new file mode 100644
index 00000000000..6984f04d1b9
--- /dev/null
+++ b/gcc/testsuite/rust/compile/cfg1.rs
@@ -0,0 +1,31 @@
+// { dg-additional-options "-w" }
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+#[cfg(A)]
+fn test() {
+    unsafe {
+        let a = "test1\n\0";
+        let b = a as *const str;
+        let c = b as *const i8;
+
+        printf(c);
+    }
+}
+
+#[cfg(B)]
+fn test() {
+    unsafe {
+        let a = "test2\n\0";
+        let b = a as *const str;
+        let c = b as *const i8;
+
+        printf(c);
+    }
+}
+
+fn main() {
+    test();
+    // { dg-error "Cannot find path .test. in this scope" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/cfg2.rs b/gcc/testsuite/rust/compile/cfg2.rs
new file mode 100644
index 00000000000..939384c5b7d
--- /dev/null
+++ b/gcc/testsuite/rust/compile/cfg2.rs
@@ -0,0 +1,13 @@
+// { dg-additional-options "-w -frust-cfg=A" }
+struct Foo;
+impl Foo {
+    #[cfg(not(A))]
+    fn test(&self) {}
+}
+
+fn main() {
+    let a = Foo;
+    a.test();
+    // { dg-error "failed to resolve method for .test." "" { target *-*-* } .-1 }
+    // { dg-error "failed to type resolve expression" "" { target *-*-* } .-2 }
+}
diff --git a/gcc/testsuite/rust/compile/cfg3.rs b/gcc/testsuite/rust/compile/cfg3.rs
new file mode 100644
index 00000000000..d6ffab6bfc6
--- /dev/null
+++ b/gcc/testsuite/rust/compile/cfg3.rs
@@ -0,0 +1,11 @@
+// { dg-additional-options "-w -frust-cfg=A -frust-cfg=B" }
+struct Foo;
+impl Foo {
+    #[cfg(all(A, B))]
+    fn test(&self) {}
+}
+
+fn main() {
+    let a = Foo;
+    a.test();
+}
diff --git a/gcc/testsuite/rust/compile/cfg4.rs b/gcc/testsuite/rust/compile/cfg4.rs
new file mode 100644
index 00000000000..2834c277ddf
--- /dev/null
+++ b/gcc/testsuite/rust/compile/cfg4.rs
@@ -0,0 +1,11 @@
+// { dg-additional-options "-w -frust-cfg=A" }
+struct Foo;
+impl Foo {
+    #[cfg(any(A, B))]
+    fn test(&self) {}
+}
+
+fn main() {
+    let a = Foo;
+    a.test();
+}
diff --git a/gcc/testsuite/rust/compile/cfg5.rs b/gcc/testsuite/rust/compile/cfg5.rs
new file mode 100644
index 00000000000..1852efaf8df
--- /dev/null
+++ b/gcc/testsuite/rust/compile/cfg5.rs
@@ -0,0 +1,11 @@
+// { dg-additional-options "-w -frust-cfg=A=\"B\"" }
+struct Foo;
+impl Foo {
+    #[cfg(A = "B")]
+    fn test(&self) {}
+}
+
+fn main() {
+    let a = Foo;
+    a.test();
+}
diff --git a/gcc/testsuite/rust/compile/compile.exp b/gcc/testsuite/rust/compile/compile.exp
new file mode 100644
index 00000000000..13423d76c92
--- /dev/null
+++ b/gcc/testsuite/rust/compile/compile.exp
@@ -0,0 +1,35 @@
+# Copyright (C) 2021-2022 Free Software Foundation, Inc.
+
+# This program 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 of the License, or
+# (at your option) any later version.
+# 
+# This program 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/>.
+
+# Compile tests, no torture testing.
+#
+# These tests raise errors in the front end; torture testing doesn't apply.
+
+# Load support procs.
+load_lib rust-dg.exp
+
+# Initialize `dg'.
+dg-init
+
+# Main loop.
+set saved-dg-do-what-default ${dg-do-what-default}
+
+set dg-do-what-default "compile"
+dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.rs]] "" ""
+set dg-do-what-default ${saved-dg-do-what-default}
+
+# All done.
+dg-finish
diff --git a/gcc/testsuite/rust/compile/complex-path1.rs b/gcc/testsuite/rust/compile/complex-path1.rs
new file mode 100644
index 00000000000..54011bd6976
--- /dev/null
+++ b/gcc/testsuite/rust/compile/complex-path1.rs
@@ -0,0 +1,18 @@
+// { dg-additional-options "-w" }
+mod a {
+    pub fn foo() {}
+}
+
+mod b {
+    pub fn foo() {
+        super::a::foo();
+    }
+}
+
+mod foo {
+    pub struct bar(pub i32);
+}
+
+fn test() -> crate::foo::bar {
+    foo::bar(123)
+}
diff --git a/gcc/testsuite/rust/compile/const-issue1440.rs b/gcc/testsuite/rust/compile/const-issue1440.rs
new file mode 100644
index 00000000000..9b974b96bbb
--- /dev/null
+++ b/gcc/testsuite/rust/compile/const-issue1440.rs
@@ -0,0 +1,76 @@
+// { dg-additional-options "-w" }
+
+mod intrinsics {
+    extern "rust-intrinsic" {
+        pub fn wrapping_add<T>(a: T, b: T) -> T;
+        pub fn rotate_left<T>(a: T, b: T) -> T;
+        pub fn rotate_right<T>(a: T, b: T) -> T;
+        pub fn offset<T>(ptr: *const T, count: isize) -> *const T;
+    }
+}
+
+mod mem {
+    extern "rust-intrinsic" {
+        #[rustc_const_stable(feature = "const_transmute", since = "1.46.0")]
+        pub fn transmute<T, U>(_: T) -> U;
+        pub fn size_of<T>() -> usize;
+    }
+}
+
+macro_rules! impl_uint {
+    ($($ty:ident = $lang:literal),*) => {
+        $(
+            impl $ty {
+                pub fn wrapping_add(self, rhs: Self) -> Self {
+                    // intrinsics::wrapping_add(self, rhs)
+                    self + rhs
+                }
+
+                pub fn rotate_left(self, n: u32) -> Self {
+                    unsafe {
+                        intrinsics::rotate_left(self, n as Self)
+                    }
+                }
+
+                pub fn rotate_right(self, n: u32) -> Self {
+                    unsafe {
+                        intrinsics::rotate_right(self, n as Self)
+                    }
+                }
+
+                pub fn to_le(self) -> Self {
+                    #[cfg(target_endian = "little")]
+                    {
+                        self
+                    }
+                }
+
+                pub const fn from_le_bytes(bytes: [u8; mem::size_of::<Self>()]) -> Self {
+                    // { dg-error "only functions marked as .const. are allowed to be called from constant contexts" "" { target *-*-* } .-1 }
+                    Self::from_le(Self::from_ne_bytes(bytes))
+                }
+
+                pub const fn from_le(x: Self) -> Self {
+                    #[cfg(target_endian = "little")]
+                    {
+                        x
+                    }
+                }
+
+                pub const fn from_ne_bytes(bytes: [u8; mem::size_of::<Self>()]) -> Self {
+                    // { dg-error "only functions marked as .const. are allowed to be called from constant contexts" "" { target *-*-* } .-1 }
+                    unsafe { mem::transmute(bytes) }
+                }
+            }
+        )*
+    }
+}
+
+impl_uint!(
+    u8 = "u8",
+    u16 = "u16",
+    u32 = "u32",
+    u64 = "u64",
+    u128 = "u128",
+    usize = "usize"
+);
diff --git a/gcc/testsuite/rust/compile/const1.rs b/gcc/testsuite/rust/compile/const1.rs
new file mode 100644
index 00000000000..5f19c674c94
--- /dev/null
+++ b/gcc/testsuite/rust/compile/const1.rs
@@ -0,0 +1,6 @@
+fn bar() {}
+
+const fn foo() {
+    bar(); // { dg-error "only functions marked as .const. are allowed to be called from constant contexts" }
+}
+
diff --git a/gcc/testsuite/rust/compile/const2.rs b/gcc/testsuite/rust/compile/const2.rs
new file mode 100644
index 00000000000..17b6de573dd
--- /dev/null
+++ b/gcc/testsuite/rust/compile/const2.rs
@@ -0,0 +1,7 @@
+// { dg-additional-options "-w" }
+
+const fn foo() {
+    const fn bar() {}
+
+    bar();
+}
diff --git a/gcc/testsuite/rust/compile/const3.rs b/gcc/testsuite/rust/compile/const3.rs
new file mode 100644
index 00000000000..22dc3d356ca
--- /dev/null
+++ b/gcc/testsuite/rust/compile/const3.rs
@@ -0,0 +1,7 @@
+fn size() -> usize {
+    15
+}
+
+fn main() {
+    let a = [15; size()]; // { dg-error "only functions marked as .const. are allowed to be called from constant contexts" }
+}
diff --git a/gcc/testsuite/rust/compile/const_generics_1.rs b/gcc/testsuite/rust/compile/const_generics_1.rs
new file mode 100644
index 00000000000..bcad8ee6a19
--- /dev/null
+++ b/gcc/testsuite/rust/compile/const_generics_1.rs
@@ -0,0 +1,19 @@
+// { dg-additional-options "-w" }
+
+// There are errors about unused generic parameters, but we can't handle that yet.
+// Still, this code is invalid Rust.
+
+mod sain {
+    struct Foo<const N: usize>;
+    struct Bar<T, const N: usize>;
+    struct Baz<'l, T, const N: usize>;
+}
+
+mod doux {
+    struct Foo<const N: usize = 15>;
+    struct Bar<T, const N: usize = { 14 * 2 }>;
+
+    const N_DEFAULT: usize = 3;
+
+    struct Baz<'l, T, const N: usize = N_DEFAULT>;
+}
diff --git a/gcc/testsuite/rust/compile/const_generics_2.rs b/gcc/testsuite/rust/compile/const_generics_2.rs
new file mode 100644
index 00000000000..98495cf404d
--- /dev/null
+++ b/gcc/testsuite/rust/compile/const_generics_2.rs
@@ -0,0 +1,4 @@
+struct Foo<const N>; // { dg-error "expecting .:. but .>. found" }
+struct Bar<const N: >; // { dg-error "unrecognised token .>. in type" }
+struct Baz<const N: usize = >; // { dg-error "invalid token for start of default value for const generic parameter" }
+// { dg-error "unrecognised token .>. in type" "" { target *-*-* } .-1 }
diff --git a/gcc/testsuite/rust/compile/const_generics_3.rs b/gcc/testsuite/rust/compile/const_generics_3.rs
new file mode 100644
index 00000000000..6a3a0fe27bf
--- /dev/null
+++ b/gcc/testsuite/rust/compile/const_generics_3.rs
@@ -0,0 +1,26 @@
+// { dg-additional-options "-w" }
+
+const M: usize = 4;
+
+struct Foo<T, const N: usize = 1> {
+    // FIXME: This error is bogus. But having it means parsing is valid!
+    value: [i32; N], // { dg-error "failed to find name: N" }
+}
+
+fn main() {
+    let foo = Foo::<i32> { value: [15] };
+    let foo = Foo::<i32, 2> { value: [15, 13] };
+    let foo: Foo<i32, 2> = Foo { value: [15, 13] };
+    let foo: Foo<i32, 2> = Foo::<i32, 2> { value: [15, 13] };
+    let foo: Foo<i32, { 1 + 1 }> = Foo { value: [15, 13] };
+    let foo = Foo::<i32, { 1 + 1 }> { value: [15, 13] };
+    let foo: Foo<i32, { 1 + 1 }> = Foo::<i32, { 1 + 1 }> { value: [15, 13] };
+    let foo: Foo<i32, M> = Foo::<i32, 4> {
+        value: [15, 13, 11, 9],
+    };
+
+    // FIXME: Add proper const typecheck errors here
+    let invalid_foo: Foo<i32, { 1 + 1 }> = Foo::<i32, 3> { value: [15, 13] };
+    let invalid_foo: Foo<i32, { 1 + 1 }> = Foo::<i32, M> { value: [15, 13] };
+    let invalid_foo: Foo<i32> = Foo::<i32, 2> { value: [15, 13] };
+}
diff --git a/gcc/testsuite/rust/compile/const_generics_4.rs b/gcc/testsuite/rust/compile/const_generics_4.rs
new file mode 100644
index 00000000000..8a3754da433
--- /dev/null
+++ b/gcc/testsuite/rust/compile/const_generics_4.rs
@@ -0,0 +1,7 @@
+// { dg-additional-options "-w" }
+
+const P: usize = 14;
+
+struct Foo<const N: usize = { M }>; // { dg-error "failed to find name: M" }
+struct Bar<const N: usize = { P }>;
+struct Baz<const N: NotAType = { P }>; // { dg-error "failed to resolve TypePath: NotAType in this scope" }
diff --git a/gcc/testsuite/rust/compile/const_generics_5.rs b/gcc/testsuite/rust/compile/const_generics_5.rs
new file mode 100644
index 00000000000..5344e31a140
--- /dev/null
+++ b/gcc/testsuite/rust/compile/const_generics_5.rs
@@ -0,0 +1,12 @@
+struct Foo<const N: usize = { 14 }>;
+
+const M: usize = 15;
+type N = Foo<3>;
+
+fn main() {
+    let _: Foo<15> = Foo;
+    let _: Foo<{ M }> = Foo;
+    let _: Foo<M> = Foo;
+    // bogus error, but it means the above const generic gets disambiguated properly
+    let _: Foo<N> = Foo; // { dg-error "TypePath Foo<N> declares generic arguments but the type Foo{Foo {}} does not have any" }
+}
diff --git a/gcc/testsuite/rust/compile/const_generics_6.rs b/gcc/testsuite/rust/compile/const_generics_6.rs
new file mode 100644
index 00000000000..de261236d93
--- /dev/null
+++ b/gcc/testsuite/rust/compile/const_generics_6.rs
@@ -0,0 +1,2 @@
+struct Foo<const N: usize>;
+struct Bar<const N: usize = { 15i32 }>; // { dg-error "expected .usize. got .i32." }
diff --git a/gcc/testsuite/rust/compile/continue1.rs b/gcc/testsuite/rust/compile/continue1.rs
new file mode 100644
index 00000000000..994312b52cc
--- /dev/null
+++ b/gcc/testsuite/rust/compile/continue1.rs
@@ -0,0 +1,10 @@
+fn main() {
+    let mut a = 1;
+    let mut b = 1;
+
+    let _fib = {
+        continue; // { dg-error "cannot 'continue' outside of a loop" }
+        // { dg-error "failed to type resolve expression" "" { target *-*-* } .-1 }
+        123
+    };
+}
diff --git a/gcc/testsuite/rust/compile/deadcode_err1.rs b/gcc/testsuite/rust/compile/deadcode_err1.rs
new file mode 100644
index 00000000000..1dbe95731e1
--- /dev/null
+++ b/gcc/testsuite/rust/compile/deadcode_err1.rs
@@ -0,0 +1,11 @@
+fn foo() -> i32 {
+    return 1;
+
+    let mut a = 1; // { dg-warning "unreachable statement" }
+    a = 1.1; // { dg-warning "unreachable statement" }
+    // { dg-error "expected .<integer>. got .<float>." "" { target *-*-* } .-1 }
+}
+
+fn main() {
+    foo();
+}
diff --git a/gcc/testsuite/rust/compile/deadcode_err2.rs b/gcc/testsuite/rust/compile/deadcode_err2.rs
new file mode 100644
index 00000000000..8c0eb4617a7
--- /dev/null
+++ b/gcc/testsuite/rust/compile/deadcode_err2.rs
@@ -0,0 +1,16 @@
+fn foo() -> i32 {
+    return 1;
+    return 1.5; // { dg-error "expected .i32. got .<float>." }
+    // { dg-warning "unreachable statement" "" { target *-*-* } .-1 } 
+}
+
+fn bar() -> i32 {
+    return 1.5; // { dg-error "expected .i32. got .<float>." }
+    return 1;
+    // { dg-warning "unreachable statement" "" { target *-*-* } .-1 } 
+}
+
+fn main() {
+    foo();
+    bar();
+}
diff --git a/gcc/testsuite/rust/compile/debug-diagnostics-default.rs b/gcc/testsuite/rust/compile/debug-diagnostics-default.rs
new file mode 100644
index 00000000000..90b0e575b45
--- /dev/null
+++ b/gcc/testsuite/rust/compile/debug-diagnostics-default.rs
@@ -0,0 +1,5 @@
+// Make sure we don't see any 'note's:
+// { dg-bogus {note: } "" { target *-*-* } 0 }
+
+fn main() {
+}
diff --git a/gcc/testsuite/rust/compile/debug-diagnostics-off.rs b/gcc/testsuite/rust/compile/debug-diagnostics-off.rs
new file mode 100644
index 00000000000..77b82b35e62
--- /dev/null
+++ b/gcc/testsuite/rust/compile/debug-diagnostics-off.rs
@@ -0,0 +1,7 @@
+// { dg-additional-options "-fno-rust-debug" }
+
+// Make sure we don't see any 'note's:
+// { dg-bogus {note: } "" { target *-*-* } 0 }
+
+fn main() {
+}
diff --git a/gcc/testsuite/rust/compile/debug-diagnostics-on.rs b/gcc/testsuite/rust/compile/debug-diagnostics-on.rs
new file mode 100644
index 00000000000..847fd24d7bd
--- /dev/null
+++ b/gcc/testsuite/rust/compile/debug-diagnostics-on.rs
@@ -0,0 +1,7 @@
+// { dg-additional-options "-frust-debug" }
+
+// Just scan for one of the Rust front end debug diagnostics:
+// { dg-message {note: Attempting to parse file: .+/gcc/testsuite/rust/compile/debug-diagnostics-on\.rs} "" { target *-*-* } 0 }
+
+fn main() {
+}
diff --git a/gcc/testsuite/rust/compile/doc_isolated_cr_block_comment.rs b/gcc/testsuite/rust/compile/doc_isolated_cr_block_comment.rs
new file mode 100644
index 00000000000..0ada77f69cf
--- /dev/null
+++ b/gcc/testsuite/rust/compile/doc_isolated_cr_block_comment.rs
@@ -0,0 +1,3 @@
+// { dg-error "Isolated CR" "" { target *-*-* } .+1 }
+/** doc cr\r comment */
+pub fn main () { }
diff --git a/gcc/testsuite/rust/compile/doc_isolated_cr_inner_block_comment.rs b/gcc/testsuite/rust/compile/doc_isolated_cr_inner_block_comment.rs
new file mode 100644
index 00000000000..7db35341bee
--- /dev/null
+++ b/gcc/testsuite/rust/compile/doc_isolated_cr_inner_block_comment.rs
@@ -0,0 +1,5 @@
+pub fn main ()
+{
+// { dg-error "Isolated CR" "" { target *-*-* } .+1 }
+  /*! doc cr\r comment */
+}
diff --git a/gcc/testsuite/rust/compile/doc_isolated_cr_inner_line_comment.rs b/gcc/testsuite/rust/compile/doc_isolated_cr_inner_line_comment.rs
new file mode 100644
index 00000000000..d75da75e218
--- /dev/null
+++ b/gcc/testsuite/rust/compile/doc_isolated_cr_inner_line_comment.rs
@@ -0,0 +1,5 @@
+pub fn main ()
+{
+// { dg-error "Isolated CR" "" { target *-*-* } .+1 }
+  //! doc cr\r comment
+}
diff --git a/gcc/testsuite/rust/compile/doc_isolated_cr_line_comment.rs b/gcc/testsuite/rust/compile/doc_isolated_cr_line_comment.rs
new file mode 100644
index 00000000000..7b6ef989c30
--- /dev/null
+++ b/gcc/testsuite/rust/compile/doc_isolated_cr_line_comment.rs
@@ -0,0 +1,3 @@
+// { dg-error "Isolated CR" "" { target *-*-* } .+1 }
+/// doc cr\r comment
+pub fn main () { }
diff --git a/gcc/testsuite/rust/compile/dup_fields.rs b/gcc/testsuite/rust/compile/dup_fields.rs
new file mode 100644
index 00000000000..ab39955eca0
--- /dev/null
+++ b/gcc/testsuite/rust/compile/dup_fields.rs
@@ -0,0 +1,23 @@
+struct S { a: i32, b: i32, c: u8, a: i128 }
+// { dg-error "duplicate field" "" { target *-*-* } .-1 }
+
+union U
+  {
+    a: i32,
+    b: i32,
+    c: u8,
+    b: char // { dg-error "duplicate field" "" { target *-*-* } }
+  }
+
+fn main ()
+{
+  struct SS { alpha: i32, beta: i32, gamma: u8, gamma: i128 }
+  // { dg-error "duplicate field" "" { target *-*-* } .-1 }
+
+  union UU
+    {
+      alpha: i32, beta: i32,
+      gamma: u8, beta: char
+      // { dg-error "duplicate field" "" { target *-*-* } .-1 }
+    }
+}
diff --git a/gcc/testsuite/rust/compile/empty_comment_before_match.rs b/gcc/testsuite/rust/compile/empty_comment_before_match.rs
new file mode 100644
index 00000000000..3d344d3e758
--- /dev/null
+++ b/gcc/testsuite/rust/compile/empty_comment_before_match.rs
@@ -0,0 +1,7 @@
+fn foo (x: i8) -> i32 { // { dg-warning "function is never used" }
+    //
+    match x {
+        1 => { return 1; }
+        _ => { return 0; }
+    }
+}
diff --git a/gcc/testsuite/rust/compile/expected_type_args2.rs b/gcc/testsuite/rust/compile/expected_type_args2.rs
new file mode 100644
index 00000000000..79454202aad
--- /dev/null
+++ b/gcc/testsuite/rust/compile/expected_type_args2.rs
@@ -0,0 +1,6 @@
+struct Foo<A>(A);
+
+fn main() {
+    let a: Foo = Foo::<i32>(123);
+    // { dg-error "generic item takes at least 1 type arguments but 0 were supplied" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/expected_type_args3.rs b/gcc/testsuite/rust/compile/expected_type_args3.rs
new file mode 100644
index 00000000000..ba07239207a
--- /dev/null
+++ b/gcc/testsuite/rust/compile/expected_type_args3.rs
@@ -0,0 +1,8 @@
+struct Foo<A>(A);
+
+impl Foo {
+    // { dg-error "generic item takes at least 1 type arguments but 0 were supplied" "" { target *-*-* } .-1 }
+    fn test() -> i32 {
+        123
+    }
+}
diff --git a/gcc/testsuite/rust/compile/func1.rs b/gcc/testsuite/rust/compile/func1.rs
new file mode 100644
index 00000000000..6758a3898e3
--- /dev/null
+++ b/gcc/testsuite/rust/compile/func1.rs
@@ -0,0 +1,9 @@
+fn test(x: i32) -> bool {
+    return x + 1; // { dg-error "expected .bool. got .i32." }
+}
+
+fn main() {
+    let an_integer = 5;
+
+    let call_test = test(1);
+}
diff --git a/gcc/testsuite/rust/compile/func2.rs b/gcc/testsuite/rust/compile/func2.rs
new file mode 100644
index 00000000000..0b8d999fec1
--- /dev/null
+++ b/gcc/testsuite/rust/compile/func2.rs
@@ -0,0 +1,7 @@
+fn test(a: i32, b: i32) -> i32 {
+    a + b
+}
+
+fn main() {
+    let a = test(1); // { dg-error "unexpected number of arguments 1 expected 2" }
+}
diff --git a/gcc/testsuite/rust/compile/func3.rs b/gcc/testsuite/rust/compile/func3.rs
new file mode 100644
index 00000000000..2a329476118
--- /dev/null
+++ b/gcc/testsuite/rust/compile/func3.rs
@@ -0,0 +1,9 @@
+fn test(a: i32, b: i32) -> i32 {
+    a + b
+}
+
+fn main() {
+    let a = test(1, true);
+    // { dg-error "expected .i32. got .bool." "" { target *-*-* } .-1 }
+    // { dg-error "Type Resolution failure on parameter" "" { target *-*-* } .-2 }
+}
diff --git a/gcc/testsuite/rust/compile/func4.rs b/gcc/testsuite/rust/compile/func4.rs
new file mode 100644
index 00000000000..3b2d2b0d773
--- /dev/null
+++ b/gcc/testsuite/rust/compile/func4.rs
@@ -0,0 +1,6 @@
+fn func() -> i32 { // { dg-error "expected .i32. got ...." }
+}
+
+fn main() {
+    func();
+}
diff --git a/gcc/testsuite/rust/compile/func5.rs b/gcc/testsuite/rust/compile/func5.rs
new file mode 100644
index 00000000000..05624f524e9
--- /dev/null
+++ b/gcc/testsuite/rust/compile/func5.rs
@@ -0,0 +1,7 @@
+fn func() -> i32 {
+    return; // { dg-error "expected .i32. got ...." }
+}
+
+fn main() {
+    func();
+}
diff --git a/gcc/testsuite/rust/compile/generic-default1.rs b/gcc/testsuite/rust/compile/generic-default1.rs
new file mode 100644
index 00000000000..0a132bf5d6b
--- /dev/null
+++ b/gcc/testsuite/rust/compile/generic-default1.rs
@@ -0,0 +1,7 @@
+struct Foo<A = i321>(A);
+// { dg-error "failed to resolve TypePath: i321" "" { target *-*-* } .-1 }
+
+fn main() {
+    let a;
+    a = Foo(123);
+}
diff --git a/gcc/testsuite/rust/compile/generics1.rs b/gcc/testsuite/rust/compile/generics1.rs
new file mode 100644
index 00000000000..de1bbf5dafb
--- /dev/null
+++ b/gcc/testsuite/rust/compile/generics1.rs
@@ -0,0 +1,11 @@
+// { dg-error "expected .i32. got .i8." "" { target *-*-* } 0 }
+
+struct GenericStruct<T>(T, usize);
+
+fn main() {
+    let a2: GenericStruct<i8>;
+    a2 = GenericStruct::<_>(1, 456);
+
+    let b2: i32 = a2.0;
+    let c2: usize = a2.1;
+}
diff --git a/gcc/testsuite/rust/compile/generics10.rs b/gcc/testsuite/rust/compile/generics10.rs
new file mode 100644
index 00000000000..a734fa8a197
--- /dev/null
+++ b/gcc/testsuite/rust/compile/generics10.rs
@@ -0,0 +1,12 @@
+struct Foo<A, B>(A, B);
+
+impl<X = i32> Foo<X, f32> { // { dg-error "defaults for type parameters are not allowed here" }
+    fn new(a: X, b: f32) -> Self {
+        Self(a, b)
+    }
+}
+
+fn main() {
+    let a;
+    a = Foo::new(123, 456f32);
+}
diff --git a/gcc/testsuite/rust/compile/generics11.rs b/gcc/testsuite/rust/compile/generics11.rs
new file mode 100644
index 00000000000..4d3b9e1777c
--- /dev/null
+++ b/gcc/testsuite/rust/compile/generics11.rs
@@ -0,0 +1,12 @@
+struct Foo<T>(T, bool);
+
+impl<T> Foo<T> {
+    fn test() -> i32 {
+        123
+    }
+}
+
+fn main() {
+    let a = Foo::test();
+    // { dg-error "type annotations needed" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/generics12.rs b/gcc/testsuite/rust/compile/generics12.rs
new file mode 100644
index 00000000000..f1ac8b0314b
--- /dev/null
+++ b/gcc/testsuite/rust/compile/generics12.rs
@@ -0,0 +1,6 @@
+fn main() {
+    bar();
+    // { dg-error "type annotations needed" "" { target *-*-* } .-1 }
+}
+
+fn bar<T>() {}
diff --git a/gcc/testsuite/rust/compile/generics13.rs b/gcc/testsuite/rust/compile/generics13.rs
new file mode 100644
index 00000000000..05c75c5f63d
--- /dev/null
+++ b/gcc/testsuite/rust/compile/generics13.rs
@@ -0,0 +1 @@
+struct Foo<A, 'a>; // { dg-error "invalid order for generic parameters: lifetimes should always come before types" }
diff --git a/gcc/testsuite/rust/compile/generics2.rs b/gcc/testsuite/rust/compile/generics2.rs
new file mode 100644
index 00000000000..5812b133038
--- /dev/null
+++ b/gcc/testsuite/rust/compile/generics2.rs
@@ -0,0 +1,11 @@
+// { dg-error "expected .i32. got .i8." "" { target *-*-* } 0 }
+
+struct GenericStruct<T>(T, usize);
+
+fn main() {
+    let a2: GenericStruct<i8>;
+    a2 = GenericStruct(1, 456);
+
+    let b2: i32 = a2.0;
+    let c2: usize = a2.1;
+}
diff --git a/gcc/testsuite/rust/compile/generics3.rs b/gcc/testsuite/rust/compile/generics3.rs
new file mode 100644
index 00000000000..2d4210588fb
--- /dev/null
+++ b/gcc/testsuite/rust/compile/generics3.rs
@@ -0,0 +1,10 @@
+// { dg-error "expected .i32. got .i8." "" { target *-*-* } 0 }
+struct GenericStruct<T>(T, usize);
+
+fn main() {
+    let a2;
+    a2 = GenericStruct::<i8>(1, 456);
+
+    let b2: i32 = a2.0;
+    let c2: usize = a2.1;
+}
diff --git a/gcc/testsuite/rust/compile/generics4.rs b/gcc/testsuite/rust/compile/generics4.rs
new file mode 100644
index 00000000000..8af13586a37
--- /dev/null
+++ b/gcc/testsuite/rust/compile/generics4.rs
@@ -0,0 +1,16 @@
+struct GenericStruct<T>(T, usize);
+
+fn main() {
+    let a2;
+    a2 = GenericStruct::<i8, i32>(1, 456); // { dg-error "generic item takes at most 1 type arguments but 2 were supplied" }
+                                           // { dg-error {failed to type resolve expression} "" { target *-*-* } .-1 }
+                                           // { dg-error {Failed to resolve expression of function call} "" { target *-*-* } .-2 }
+                                           // { duplicate _dg-error {failed to type resolve expression} "" { target *-*-* } .-3 }
+
+    let b2: i32 = a2.0;
+    // { dg-error {Expected Tuple or ADT got: T\?} "" { target *-*-* } .-1 }
+    // { dg-error {failed to type resolve expression} "" { target *-*-* } .-2 }
+    let c2: usize = a2.1;
+    // { dg-error {Expected Tuple or ADT got: T\?} "" { target *-*-* } .-1 }
+    // { dg-error {failed to type resolve expression} "" { target *-*-* } .-2 }
+}
diff --git a/gcc/testsuite/rust/compile/generics5.rs b/gcc/testsuite/rust/compile/generics5.rs
new file mode 100644
index 00000000000..6c847b5a29b
--- /dev/null
+++ b/gcc/testsuite/rust/compile/generics5.rs
@@ -0,0 +1,10 @@
+struct GenericStruct<T>(T, usize);
+
+fn main() {
+    let a2;
+    a2 = GenericStruct::<i8, T>(1, 456);
+    // { dg-error "failed to resolve TypePath: T" "" { target *-*-* } .-1 }
+
+    let b2: i32 = a2.0;
+    let c2: usize = a2.1;
+}
diff --git a/gcc/testsuite/rust/compile/generics6.rs b/gcc/testsuite/rust/compile/generics6.rs
new file mode 100644
index 00000000000..3b81e1bbee1
--- /dev/null
+++ b/gcc/testsuite/rust/compile/generics6.rs
@@ -0,0 +1,31 @@
+struct Foo<A> {
+    a: A,
+}
+
+impl Foo<isize> {
+    fn test() -> i32 { // { dg-error "possible candidate" "TODO" { xfail *-*-* } }
+        123
+    }
+
+    fn bar(self) -> isize {
+        self.a
+    }
+}
+
+impl Foo<f32> {
+    fn test() -> i32 { // { dg-error "possible candidate" "TODO" { xfail *-*-* } }
+        123
+    }
+
+    fn bar(self) -> f32 {
+        self.a
+    }
+}
+
+fn main() {
+    let a: i32 = Foo::test(); // { dg-error "multiple applicable items in scope for: test" }
+    // { dg-error {failed to type resolve expression} "" { target *-*-* } .-1 }
+    // { dg-error {Failed to resolve expression of function call} "" { target *-*-* } .-2 }
+    // { duplicate _dg-error {failed to type resolve expression} "" { target *-*-* } .-3 }
+}
+
diff --git a/gcc/testsuite/rust/compile/generics7.rs b/gcc/testsuite/rust/compile/generics7.rs
new file mode 100644
index 00000000000..2a41632e693
--- /dev/null
+++ b/gcc/testsuite/rust/compile/generics7.rs
@@ -0,0 +1,26 @@
+struct Foo<A> {
+    a: A,
+}
+
+impl Foo<isize> {
+    fn bar(self) -> isize { // { dg-error "duplicate definitions with name bar" }
+        self.a
+    }
+}
+
+impl Foo<char> {
+    fn bar(self) -> char { // { dg-error "duplicate definitions with name bar" }
+        self.a
+    }
+}
+
+impl<T> Foo<T> {
+    fn bar(self) -> T {
+        self.a
+    }
+}
+
+fn main() {
+    let a = Foo { a: 123 };
+    a.bar();
+}
diff --git a/gcc/testsuite/rust/compile/generics8.rs b/gcc/testsuite/rust/compile/generics8.rs
new file mode 100644
index 00000000000..ceefc5d2c6a
--- /dev/null
+++ b/gcc/testsuite/rust/compile/generics8.rs
@@ -0,0 +1,15 @@
+struct Foo<A, B>(A, B);
+
+impl<T> Foo<i32, T> {
+    fn test(a: T) -> T {
+        a
+    }
+}
+
+impl Foo<i32, f32> {
+    fn test() -> f32 { // { dg-error "duplicate definitions with name test" }
+        123f32
+    }
+}
+
+fn main() {}
diff --git a/gcc/testsuite/rust/compile/generics9.rs b/gcc/testsuite/rust/compile/generics9.rs
new file mode 100644
index 00000000000..3766703431e
--- /dev/null
+++ b/gcc/testsuite/rust/compile/generics9.rs
@@ -0,0 +1,10 @@
+struct Foo<A, B = (A, B)>(A, B);
+// { dg-error "failed to resolve TypePath: B" "" { target *-*-* } .-1 }
+
+fn main() {
+    let a: Foo<bool>;
+    a = Foo::<bool>(true, (false, true));
+
+    let b: (bool, bool);
+    b = a.1;
+}
diff --git a/gcc/testsuite/rust/compile/implicit_returns_err1.rs b/gcc/testsuite/rust/compile/implicit_returns_err1.rs
new file mode 100644
index 00000000000..973ba80fb86
--- /dev/null
+++ b/gcc/testsuite/rust/compile/implicit_returns_err1.rs
@@ -0,0 +1,12 @@
+fn test(x: i32) -> i32 {
+    if x > 1 { // { dg-error "expected .... got .<integer>." }
+        1
+    } else {
+        2
+    }
+    3
+}
+
+fn main() {
+    let a = test(1);
+}
diff --git a/gcc/testsuite/rust/compile/implicit_returns_err2.rs b/gcc/testsuite/rust/compile/implicit_returns_err2.rs
new file mode 100644
index 00000000000..fb90748871f
--- /dev/null
+++ b/gcc/testsuite/rust/compile/implicit_returns_err2.rs
@@ -0,0 +1,10 @@
+fn test(x: i32) -> i32 {
+    // { dg-error "expected .i32. got .bool." "" { target *-*-* } .-1 }
+    return 1;
+    // { dg-warning "unreachable expression" "" { target *-*-* } .+1 }
+    true
+}
+
+fn main() {
+    let a = test(1);
+}
diff --git a/gcc/testsuite/rust/compile/implicit_returns_err3.rs b/gcc/testsuite/rust/compile/implicit_returns_err3.rs
new file mode 100644
index 00000000000..37b1c62414c
--- /dev/null
+++ b/gcc/testsuite/rust/compile/implicit_returns_err3.rs
@@ -0,0 +1,9 @@
+fn test(x: i32) -> i32 { // { dg-error "expected .i32. got ...." }
+    if x > 1 {
+        1
+    }
+}
+
+fn main() {
+    let a = test(9);
+}
diff --git a/gcc/testsuite/rust/compile/implicit_returns_err4.rs b/gcc/testsuite/rust/compile/implicit_returns_err4.rs
new file mode 100644
index 00000000000..59c6a020d4c
--- /dev/null
+++ b/gcc/testsuite/rust/compile/implicit_returns_err4.rs
@@ -0,0 +1,10 @@
+fn test(x: bool) -> bool {
+    // { dg-error "expected .bool. got ...." "" { target *-*-*} .-1 }
+    return x;
+    // { dg-warning "unreachable expression" "" { target *-*-* } .+1 }
+    ()
+}
+
+fn main() {
+    let a = test(true);
+}
diff --git a/gcc/testsuite/rust/compile/infer-crate-name.rs b/gcc/testsuite/rust/compile/infer-crate-name.rs
new file mode 100644
index 00000000000..b0c0086c04c
--- /dev/null
+++ b/gcc/testsuite/rust/compile/infer-crate-name.rs
@@ -0,0 +1,7 @@
+// { dg-additional-options "-fdump-tree-gimple" }
+pub fn does_nothing() {}
+fn main() {
+    does_nothing()
+}
+// { dg-final { scan-tree-dump-times {infer_crate_name::does_nothing} 2 gimple } }
+// { dg-final { scan-tree-dump-times {infer_crate_name::main} 1 gimple } }
diff --git a/gcc/testsuite/rust/compile/inline_1.rs b/gcc/testsuite/rust/compile/inline_1.rs
new file mode 100644
index 00000000000..4b0f991765a
--- /dev/null
+++ b/gcc/testsuite/rust/compile/inline_1.rs
@@ -0,0 +1,16 @@
+// { dg-additional-options "-fdump-tree-gimple" }
+#[inline]
+fn test_a() {}
+
+// { dg-final { scan-tree-dump-times {always_inline} 1 gimple } }
+#[inline(always)]
+fn test_b() {}
+
+#[inline(never)]
+fn test_c() {}
+
+fn main() {
+    test_a();
+    test_b();
+    test_c();
+}
diff --git a/gcc/testsuite/rust/compile/inline_2.rs b/gcc/testsuite/rust/compile/inline_2.rs
new file mode 100644
index 00000000000..3665fdac804
--- /dev/null
+++ b/gcc/testsuite/rust/compile/inline_2.rs
@@ -0,0 +1,6 @@
+// { dg-additional-options "-w" }
+#[inline(A)] // { dg-error "unknown inline option" }
+fn test_a() {}
+
+#[inline(A, B)] // { dg-error "invalid number of arguments" }
+fn test_b() {}
diff --git a/gcc/testsuite/rust/compile/issue-1005.rs b/gcc/testsuite/rust/compile/issue-1005.rs
new file mode 100644
index 00000000000..46c85eea91e
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issue-1005.rs
@@ -0,0 +1,4 @@
+// { dg-additional-options "-w" }
+impl<T> *const T {
+    fn test(self) {}
+}
diff --git a/gcc/testsuite/rust/compile/issue-1019.rs b/gcc/testsuite/rust/compile/issue-1019.rs
new file mode 100644
index 00000000000..aea86a821c7
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issue-1019.rs
@@ -0,0 +1,19 @@
+trait A<T> {
+    type Output;
+
+    fn test(self, a: &T) -> &Self::Output;
+}
+
+struct Foo<T> {
+    // { dg-warning "struct is never constructed" "" { target *-*-* } .-1 }
+    start: T,
+    end: T,
+}
+
+impl<X> A<X> for Foo<usize> {
+    type Output = X;
+
+    fn test(self, a: &X) -> &Self::Output {
+        a
+    }
+}
diff --git a/gcc/testsuite/rust/compile/issue-1023.rs b/gcc/testsuite/rust/compile/issue-1023.rs
new file mode 100644
index 00000000000..5a0fe6cf530
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issue-1023.rs
@@ -0,0 +1,4 @@
+// { dg-additional-options "-w" }
+fn foo(e: &str) -> &str {
+    &""
+}
diff --git a/gcc/testsuite/rust/compile/issue-1031.rs b/gcc/testsuite/rust/compile/issue-1031.rs
new file mode 100644
index 00000000000..939f0f981e0
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issue-1031.rs
@@ -0,0 +1,17 @@
+extern "rust-intrinsic" {
+    #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
+    pub fn offset<T>(dst: *const T, offset: isize) -> *const T;
+}
+
+#[lang = "const_ptr"]
+impl<T> *const T {
+    pub const unsafe fn offset(self, count: isize) -> *const T {
+        // { dg-warning "associated function is never used" "" { target *-*-* } .-1 }
+        unsafe { offset(self, count) }
+    }
+
+    pub const unsafe fn add(self, count: usize) -> Self {
+        // { dg-warning "associated function is never used" "" { target *-*-* } .-1 }
+        unsafe { self.offset(count as isize) }
+    }
+}
diff --git a/gcc/testsuite/rust/compile/issue-1034.rs b/gcc/testsuite/rust/compile/issue-1034.rs
new file mode 100644
index 00000000000..23d77005452
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issue-1034.rs
@@ -0,0 +1,16 @@
+trait Foo<T> {
+    type Output;
+
+    fn test(self, slice: &T) -> &Self::Output;
+}
+
+struct Bar<T>(T);
+// { dg-warning "struct is never constructed" "" { target *-*-* } .-1 }
+
+impl<T> Foo<[T]> for Bar<usize> {
+    type Output = [T];
+
+    fn test(self, slice: &[T]) -> &[T] {
+        slice
+    }
+}
diff --git a/gcc/testsuite/rust/compile/issue-1089.rs b/gcc/testsuite/rust/compile/issue-1089.rs
new file mode 100644
index 00000000000..635af293dbb
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issue-1089.rs
@@ -0,0 +1,6 @@
+// { dg-additional-options "-w" }
+pub mod test_mod;
+
+fn main() {
+    let a = test_mod::Test(123);
+}
diff --git a/gcc/testsuite/rust/compile/issue-1128.rs b/gcc/testsuite/rust/compile/issue-1128.rs
new file mode 100644
index 00000000000..462426b679d
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issue-1128.rs
@@ -0,0 +1,6 @@
+pub trait Hasher {
+    fn write(&mut self, bytes: &[u8]);
+    fn write_u8(&mut self, i: u8) {
+        self.write(&[i])
+    }
+}
diff --git a/gcc/testsuite/rust/compile/issue-1129-1.rs b/gcc/testsuite/rust/compile/issue-1129-1.rs
new file mode 100644
index 00000000000..a15903983f0
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issue-1129-1.rs
@@ -0,0 +1,4 @@
+// { dg-additional-options "-w" }
+fn write_u8(i: u8) {
+    let x: &[u8] = &[i];
+}
diff --git a/gcc/testsuite/rust/compile/issue-1129-2.rs b/gcc/testsuite/rust/compile/issue-1129-2.rs
new file mode 100644
index 00000000000..25d30faf4aa
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issue-1129-2.rs
@@ -0,0 +1,22 @@
+// { dg-additional-options "-w" }
+pub trait Hasher {
+    fn finish(&self) -> u64;
+    fn write(&mut self, bytes: &[u8]);
+    fn write_u8(&mut self, i: u8) {
+        self.write(&[i])
+    }
+}
+
+struct SipHasher;
+
+impl Hasher for SipHasher {
+    #[inline]
+    fn write(&mut self, msg: &[u8]) {
+        loop {}
+    }
+
+    #[inline]
+    fn finish(&self) -> u64 {
+        0
+    }
+}
diff --git a/gcc/testsuite/rust/compile/issue-1130.rs b/gcc/testsuite/rust/compile/issue-1130.rs
new file mode 100644
index 00000000000..92200c7cd5f
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issue-1130.rs
@@ -0,0 +1,47 @@
+// { dg-additional-options "-w" }
+mod mem {
+    extern "rust-intrinsic" {
+        fn size_of<T>() -> usize;
+        fn transmute<U, V>(_: U) -> V;
+    }
+}
+
+impl u16 {
+    fn to_ne_bytes(self) -> [u8; mem::size_of::<Self>()] {
+        unsafe { mem::transmute(self) }
+    }
+}
+
+pub trait Hasher {
+    fn finish(&self) -> u64;
+
+    fn write(&mut self, bytes: &[u8]);
+
+    fn write_u8(&mut self, i: u8) {
+        self.write(&[i])
+    }
+
+    fn write_i8(&mut self, i: i8) {
+        self.write_u8(i as u8)
+    }
+
+    fn write_u16(&mut self, i: u16) {
+        self.write(&i.to_ne_bytes())
+    }
+
+    fn write_i16(&mut self, i: i16) {
+        self.write_u16(i as u16)
+    }
+}
+
+pub struct SipHasher;
+
+impl Hasher for SipHasher {
+    #[inline]
+    fn write(&mut self, msg: &[u8]) {}
+
+    #[inline]
+    fn finish(&self) -> u64 {
+        0
+    }
+}
diff --git a/gcc/testsuite/rust/compile/issue-1131.rs b/gcc/testsuite/rust/compile/issue-1131.rs
new file mode 100644
index 00000000000..fd158abc700
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issue-1131.rs
@@ -0,0 +1,4 @@
+extern "rust-intrinsic" {
+    fn size_of<T>() -> usize;
+    fn offset<T>(dst: *const T, offset: isize) -> *const T;
+}
diff --git a/gcc/testsuite/rust/compile/issue-1152.rs b/gcc/testsuite/rust/compile/issue-1152.rs
new file mode 100644
index 00000000000..18eee9e6b4a
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issue-1152.rs
@@ -0,0 +1,8 @@
+fn test() {
+    let f = [0; -4_isize];
+    // { dg-error "expected .usize. got .isize." "" { target *-*-* } .-1 }
+    // { dg-error "failed to type resolve expression" "" { target *-*-* } .-2 }
+    let f = [0_usize; -1_isize];
+    // { dg-error "expected .usize. got .isize." "" { target *-*-* } .-1 }
+    // { dg-error "failed to type resolve expression" "" { target *-*-* } .-2 }
+}
diff --git a/gcc/testsuite/rust/compile/issue-1165.rs b/gcc/testsuite/rust/compile/issue-1165.rs
new file mode 100644
index 00000000000..f5889698d70
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issue-1165.rs
@@ -0,0 +1,5 @@
+struct Foo<T>(T);
+
+fn main() {
+    &Foo(123);
+}
diff --git a/gcc/testsuite/rust/compile/issue-1173.rs b/gcc/testsuite/rust/compile/issue-1173.rs
new file mode 100644
index 00000000000..5c2a9173241
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issue-1173.rs
@@ -0,0 +1,23 @@
+// { dg-additional-options "-w" }
+
+#![feature(intrinsics)]
+
+mod mem {
+    extern "rust-intrinsic" {
+        pub fn transmute<U, V>(_: U) -> V;
+    }
+}
+
+pub trait Hasher {
+    fn write(&mut self, bytes: &[u8]);
+    fn write_u16(&mut self, i: u16) {
+        self.write(unsafe { &mem::transmute::<_, [u8; 2]>(i) })
+    }
+}
+
+pub struct SipHasher;
+
+impl Hasher for SipHasher {
+    #[inline]
+    fn write(&mut self, msg: &[u8]) {}
+}
diff --git a/gcc/testsuite/rust/compile/issue-1226.rs b/gcc/testsuite/rust/compile/issue-1226.rs
new file mode 100644
index 00000000000..f5f9e5ff08d
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issue-1226.rs
@@ -0,0 +1,6 @@
+// { dg-additional-options "-w" }
+const TEST: *mut u8 = 123 as *mut u8;
+
+fn test() {
+    let a = TEST;
+}
diff --git a/gcc/testsuite/rust/compile/issue-1234.rs b/gcc/testsuite/rust/compile/issue-1234.rs
new file mode 100644
index 00000000000..c6d5932c004
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issue-1234.rs
@@ -0,0 +1,4 @@
+fn foo() -> u8 {
+    // { dg-warning "function is never used" "" { target *-*-* } .-1 }
+    1u8 << 2u32
+}
diff --git a/gcc/testsuite/rust/compile/issue-1235.rs b/gcc/testsuite/rust/compile/issue-1235.rs
new file mode 100644
index 00000000000..098b337455f
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issue-1235.rs
@@ -0,0 +1,21 @@
+// { dg-additional-options "-w" }
+struct FatPtr<T> {
+    data: *const T,
+    len: usize,
+}
+
+pub union Repr<T> {
+    rust: *const [T],
+    rust_mut: *mut [T],
+    raw: FatPtr<T>,
+}
+
+impl<T> [T] {
+    pub const fn is_empty(&self) -> bool {
+        self.len() == 0
+    }
+
+    pub const fn len(&self) -> usize {
+        unsafe { Repr { rust: self }.raw.len }
+    }
+}
diff --git a/gcc/testsuite/rust/compile/issue-1237.rs b/gcc/testsuite/rust/compile/issue-1237.rs
new file mode 100644
index 00000000000..542be897949
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issue-1237.rs
@@ -0,0 +1,23 @@
+// { dg-additional-options "-w" }
+mod intrinsics {
+    extern "rust-intrinsic" {
+        pub fn offset<T>(ptr: *const T, count: isize) -> *const T;
+    }
+}
+
+impl<T> *const T {
+    pub unsafe fn offset(self, count: isize) -> *const T {
+        unsafe { intrinsics::offset(self, count) }
+    }
+}
+
+impl<T> [T] {
+    pub unsafe fn get_unchecked(&self, index: usize) -> &T {
+        unsafe { &*(self as *const [T] as *const T).offset(index as isize) }
+    }
+}
+
+#[inline]
+unsafe fn u8to64_le(buf: &[u8], start: usize, len: usize) -> u64 {
+    (unsafe { *buf.get_unchecked(start) } as u64)
+}
diff --git a/gcc/testsuite/rust/compile/issue-1251.rs b/gcc/testsuite/rust/compile/issue-1251.rs
new file mode 100644
index 00000000000..b16e1e0b0d9
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issue-1251.rs
@@ -0,0 +1,14 @@
+// { dg-additional-options "-w" }
+mod a {
+    pub mod b {
+        pub mod a {
+            pub fn foo() {}
+        }
+    }
+
+    pub fn bidule() {
+        crate::a::b::a::foo()
+    }
+}
+
+fn main() {}
diff --git a/gcc/testsuite/rust/compile/issue-1271.rs b/gcc/testsuite/rust/compile/issue-1271.rs
new file mode 100644
index 00000000000..5dd6418de4c
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issue-1271.rs
@@ -0,0 +1,5 @@
+// { dg-additional-options "-w" }
+fn test() {
+    let a: &str = "TEST 1";
+    let b: &str = &"TEST 2";
+}
diff --git a/gcc/testsuite/rust/compile/issue-1289.rs b/gcc/testsuite/rust/compile/issue-1289.rs
new file mode 100644
index 00000000000..343aaab078b
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issue-1289.rs
@@ -0,0 +1,43 @@
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+mod intrinsics {
+    extern "rust-intrinsic" {
+        #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
+        pub fn offset<T>(dst: *const T, offset: isize) -> *const T;
+    }
+}
+
+#[lang = "mut_ptr"]
+impl<T> *mut T {
+    pub const unsafe fn offset(self, count: isize) -> *mut T {
+        unsafe { intrinsics::offset(self, count) as *mut T }
+    }
+
+    pub const unsafe fn add(self, count: usize) -> Self {
+        unsafe { self.offset(count as isize) }
+    }
+}
+
+#[lang = "const_ptr"]
+impl<T> *const T {
+    pub const unsafe fn offset(self, count: isize) -> *mut T {
+        // { dg-warning "associated function is never used" "" { target *-*-* } .-1 }
+        unsafe { intrinsics::offset(self, count) as *mut T }
+    }
+
+    pub const unsafe fn add(self, count: usize) -> Self {
+        // { dg-warning "associated function is never used" "" { target *-*-* } .-1 }
+        unsafe { self.offset(count as isize) }
+    }
+}
+
+fn main() -> i32 {
+    let a: *mut _ = &mut 123;
+    unsafe {
+        let _b = a.add(123);
+    }
+
+    0
+}
diff --git a/gcc/testsuite/rust/compile/issue-1323-1.rs b/gcc/testsuite/rust/compile/issue-1323-1.rs
new file mode 100644
index 00000000000..a6174253a21
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issue-1323-1.rs
@@ -0,0 +1,18 @@
+fn main() {
+    let mut x = [1, 2, 3];
+    let y: i32 = x[0];
+    print_int(y);
+}
+
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+fn print_int(value: i32) {
+    let s = "%d\n\0";
+    let s_p = s as *const str;
+    let c_p = s_p as *const i8;
+    unsafe {
+        printf(c_p, value as isize);
+    }
+}
diff --git a/gcc/testsuite/rust/compile/issue-1323-2.rs b/gcc/testsuite/rust/compile/issue-1323-2.rs
new file mode 100644
index 00000000000..45168b22fa7
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issue-1323-2.rs
@@ -0,0 +1,16 @@
+fn print_int(value: i32) {
+    let s = "%d\n\0";
+    let s_p = s as *const str;
+    let c_p = s_p as *const i8;
+    unsafe {
+        printf(c_p, value as isize);
+    }
+}
+
+fn main() {
+    print_int(5);
+}
+
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
diff --git a/gcc/testsuite/rust/compile/issue-1383.rs b/gcc/testsuite/rust/compile/issue-1383.rs
new file mode 100644
index 00000000000..cca12e8fc71
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issue-1383.rs
@@ -0,0 +1,8 @@
+pub fn generic_function<X>(a: X) -> X {
+    a
+}
+
+fn main() -> i32 {
+    let a = generic_function(123);
+    a - 123
+}
diff --git a/gcc/testsuite/rust/compile/issue-1393.rs b/gcc/testsuite/rust/compile/issue-1393.rs
new file mode 100644
index 00000000000..e09f01b62e5
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issue-1393.rs
@@ -0,0 +1,13 @@
+fn tst() {
+    let a = 123;
+    let b = 0;
+    let _c = if b == 0 {
+        (a & 0x7fffff) << 1
+    } else {
+        (a & 0x7fffff) | 0x800000
+    };
+}
+
+fn main() {
+    tst()
+}
diff --git a/gcc/testsuite/rust/compile/issue-1447.rs b/gcc/testsuite/rust/compile/issue-1447.rs
new file mode 100644
index 00000000000..e0543e6247c
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issue-1447.rs
@@ -0,0 +1,28 @@
+// { dg-options "-w" }
+struct PhantomData<T>;
+
+struct Hasher<S> {
+    _marker: PhantomData<S>,
+}
+
+struct Sip24Rounds;
+
+struct SipHasher24 {
+    hasher: Hasher<Sip24Rounds>,
+}
+
+impl SipHasher24 {
+    pub fn new_with_keys(key0: u64, key1: u64) -> SipHasher24 {
+        SipHasher24 {
+            hasher: Hasher::new_with_keys(),
+        }
+    }
+}
+
+impl<S> Hasher<S> {
+    fn new_with_keys() -> Hasher<S> {
+        Hasher {
+            _marker: PhantomData,
+        }
+    }
+}
diff --git a/gcc/testsuite/rust/compile/issue-407-2.rs b/gcc/testsuite/rust/compile/issue-407-2.rs
new file mode 100644
index 00000000000..cb8027b9c7f
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issue-407-2.rs
@@ -0,0 +1,21 @@
+// #407
+pub fn loopy()  {
+    let mut a = 1;
+    // { dg-error {failed to parse expr with block in parsing expr statement} "" { target *-*-* } .+2 }
+    // { dg-error {failed to parse statement or expression without block in block expression} "" { target *-*-* } .+1 }
+    loop {
+        // { dg-error {failed to parse expr with block in parsing expr statement} "" { target *-*-* } .+2 }
+        // { dg-error {failed to parse statement or expression without block in block expression} "" { target *-*-* } .+1 }
+	if a < 40 {
+	    a + = 1; // { dg-error "found unexpected token '=' in null denotation" }
+            // { dg-error {failed to parse expression for expression without block \(pratt-parsed expression is null\)} "" { target *-*-* } .-1 }
+            // { dg-error {failed to parse statement or expression without block in block expression} "" { target *-*-* } .-2 }
+            // { dg-error {failed to parse if body block expression in if expression} "" { target *-*-* } .-3 }
+            // { dg-error {could not parse loop body in \(infinite\) loop expression} "" { target *-*-* } .+1 }
+	} else {
+	    break;
+	}
+    }
+}
+// { dg-error {unrecognised token '\}' for start of item} "" { target *-*-* } .-1 }
+// { dg-error {failed to parse item in crate} "" { target *-*-* } .-2 }
diff --git a/gcc/testsuite/rust/compile/issue-407.rs b/gcc/testsuite/rust/compile/issue-407.rs
new file mode 100644
index 00000000000..530b7ddfc12
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issue-407.rs
@@ -0,0 +1,9 @@
+// This already worked before the #409 code changes.
+fn test()  {
+    let mut a = 1;
+    a + = 1; // { dg-error "found unexpected token '=' in null denotation" }
+    // { dg-error {failed to parse expression for expression without block \(pratt-parsed expression is null\)} "" { target *-*-* } .-1 }
+    // { dg-error {failed to parse statement or expression without block in block expression} "" { target *-*-* } .-2 }
+    // { dg-error {unrecognised token 'integer literal' for start of item} "" { target *-*-* } .-3 }
+    // { dg-error {failed to parse item in crate} "" { target *-*-* } .-4 }
+}
diff --git a/gcc/testsuite/rust/compile/issue-557.rs b/gcc/testsuite/rust/compile/issue-557.rs
new file mode 100644
index 00000000000..aeb5ba6755b
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issue-557.rs
@@ -0,0 +1,4 @@
+// { dg-additional-options "-w" }
+fn test(a: i32, _: i32) {
+    let _ = 42 + a;
+}
diff --git a/gcc/testsuite/rust/compile/issue-635-1.rs b/gcc/testsuite/rust/compile/issue-635-1.rs
new file mode 100644
index 00000000000..dc6a4c2eece
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issue-635-1.rs
@@ -0,0 +1,5 @@
+// { dg-additional-options "-w" }
+fn test() -> i32 {
+    return 10000000000000000000000000000000000000000000;
+    // { dg-error "integer overflows the respective type .i32." "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/issue-635-2.rs b/gcc/testsuite/rust/compile/issue-635-2.rs
new file mode 100644
index 00000000000..335218aa52c
--- /dev/null
+++ b/gcc/testsuite/rust/compile/issue-635-2.rs
@@ -0,0 +1,5 @@
+// { dg-additional-options "-w" }
+fn test() -> f32 {
+    return 10000000000000000000000000000000000000000000.0f32;
+    // { dg-error "decimal overflows the respective type .f32." "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/lookup_err1.rs b/gcc/testsuite/rust/compile/lookup_err1.rs
new file mode 100644
index 00000000000..4a96f9ff140
--- /dev/null
+++ b/gcc/testsuite/rust/compile/lookup_err1.rs
@@ -0,0 +1,7 @@
+fn test() {
+    fn nested() {}
+}
+
+fn main() {
+    nested(); // { dg-error "Cannot find path .nested. in this scope" }
+}
diff --git a/gcc/testsuite/rust/compile/macro-issue1053-2.rs b/gcc/testsuite/rust/compile/macro-issue1053-2.rs
new file mode 100644
index 00000000000..31459907c08
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro-issue1053-2.rs
@@ -0,0 +1,5 @@
+macro_rules! m {
+    ($e:expr $(forbidden)*) => {{}}; // { dg-error "token .identifier. is not allowed after .expr. fragment" }
+                                     // { dg-error "required first macro rule in macro rules definition could not be parsed" "" { target *-*-* } .-1 }
+                                     // { dg-error "failed to parse item in crate" "" { target *-*-* } .-2 }
+}
diff --git a/gcc/testsuite/rust/compile/macro-issue1053.rs b/gcc/testsuite/rust/compile/macro-issue1053.rs
new file mode 100644
index 00000000000..1e968496e0c
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro-issue1053.rs
@@ -0,0 +1,3 @@
+macro_rules! m {
+    ($e:expr $(,)*) => {{}};
+}
diff --git a/gcc/testsuite/rust/compile/macro-issue1224.rs b/gcc/testsuite/rust/compile/macro-issue1224.rs
new file mode 100644
index 00000000000..003bbcd5067
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro-issue1224.rs
@@ -0,0 +1,9 @@
+macro_rules! impl_uint {
+    ($($ty:ident),*) => {
+        impl $ty {} // { dg-error "metavariable is still repeating at this depth" }
+                    // { dg-error "unrecognised token" "" { target *-*-* } .-1 } // Spurious
+                    // { dg-error "could not parse type" "" { target *-*-* } .-2 } // Spurious
+    };
+}
+
+impl_uint!(u8, u16, u32, u64, u128);
diff --git a/gcc/testsuite/rust/compile/macro-issue1233.rs b/gcc/testsuite/rust/compile/macro-issue1233.rs
new file mode 100644
index 00000000000..7fab787b9e8
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro-issue1233.rs
@@ -0,0 +1,22 @@
+// { dg-additional-options "-frust-cfg=A -w" }
+
+macro_rules! impl_uint {
+    ($($ty:ident = $lang:literal),*) => {
+        $(
+            impl $ty {
+                pub fn to_le(self) -> Self {
+                    #[cfg(not(A))]
+                    {
+                        self
+                    }
+                    #[cfg(A)]
+                    {
+                        self
+                    }
+                }
+            }
+        )*
+    }
+}
+
+impl_uint!(u8 = "u8", u16 = "u16", u32 = "u32");
diff --git a/gcc/testsuite/rust/compile/macro-issue1395-2.rs b/gcc/testsuite/rust/compile/macro-issue1395-2.rs
new file mode 100644
index 00000000000..1df6a3a0038
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro-issue1395-2.rs
@@ -0,0 +1,7 @@
+// { dg-additional-options "-frust-edition=2018" }
+
+macro_rules! try {
+    // { dg-error "expecting .identifier. but .try. found" "" { target *-*-* } .-1 }
+    // { dg-error "failed to parse item in crate" "" { target *-*-* } .-2 }
+    () => {};
+}
diff --git a/gcc/testsuite/rust/compile/macro-issue1395.rs b/gcc/testsuite/rust/compile/macro-issue1395.rs
new file mode 100644
index 00000000000..b0368c1610f
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro-issue1395.rs
@@ -0,0 +1,5 @@
+// Default edition is 2015 - this is valid
+
+macro_rules! try {
+    () => {};
+}
diff --git a/gcc/testsuite/rust/compile/macro-issue1400-2.rs b/gcc/testsuite/rust/compile/macro-issue1400-2.rs
new file mode 100644
index 00000000000..ba7b61b0b16
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro-issue1400-2.rs
@@ -0,0 +1,32 @@
+macro_rules! foo {
+    ( ( $( $Trait: ident ),+ ) for $($Ty: ident)* ) => {
+        $(
+            impl $Trait for $Ty {
+    // { dg-error "different amount of matches used in merged repetitions: expected 4, got 1" "" { target *-*-* } .-1 }
+                fn bar() -> i32 {
+                    14
+                }
+            }
+        )+
+    }
+}
+
+trait Foo {
+    fn bar() -> i32;
+}
+
+trait Bar {
+    fn bar() -> i32;
+}
+
+trait Baz {
+    fn bar() -> i32;
+}
+
+trait Qux {
+    fn bar() -> i32;
+}
+
+struct S;
+
+foo! {(Foo, Bar, Baz, Qux) for S}
diff --git a/gcc/testsuite/rust/compile/macro-issue1400.rs b/gcc/testsuite/rust/compile/macro-issue1400.rs
new file mode 100644
index 00000000000..971bd778054
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro-issue1400.rs
@@ -0,0 +1,33 @@
+// { dg-additional-options "-w" }
+
+macro_rules! foo {
+    ( ( $( $Trait: ident ),+ ) for $Ty: ident ) => {
+        $(
+            impl $Trait for $Ty {
+                fn bar() -> i32 {
+                    14
+                }
+            }
+        )+
+    }
+}
+
+trait Foo {
+    fn bar() -> i32;
+}
+
+trait Bar {
+    fn bar() -> i32;
+}
+
+trait Baz {
+    fn bar() -> i32;
+}
+
+trait Qux {
+    fn bar() -> i32;
+}
+
+struct S;
+
+foo! {(Foo, Bar, Baz, Qux) for S}
diff --git a/gcc/testsuite/rust/compile/macro1.rs b/gcc/testsuite/rust/compile/macro1.rs
new file mode 100644
index 00000000000..8cd941891d0
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro1.rs
@@ -0,0 +1,3 @@
+macro_rules! empty_parens {
+    () => ();
+}
diff --git a/gcc/testsuite/rust/compile/macro10.rs b/gcc/testsuite/rust/compile/macro10.rs
new file mode 100644
index 00000000000..3f1453e2eda
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro10.rs
@@ -0,0 +1,11 @@
+// { dg-additional-options "-w" }
+macro_rules! foo {
+    {} => {
+        15
+    };
+}
+
+fn main() {
+    let a = foo!();
+    let b = foo![];
+}
diff --git a/gcc/testsuite/rust/compile/macro11.rs b/gcc/testsuite/rust/compile/macro11.rs
new file mode 100644
index 00000000000..97b89a12d84
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro11.rs
@@ -0,0 +1,11 @@
+macro_rules! call_f {
+    ($($f:ident)*) => { $($f();)* }
+}
+
+fn f() {}
+
+// This is valid and should parse items
+fn main() {
+    call_f!(f f f f);
+}
+
diff --git a/gcc/testsuite/rust/compile/macro12.rs b/gcc/testsuite/rust/compile/macro12.rs
new file mode 100644
index 00000000000..b75fbad2c2f
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro12.rs
@@ -0,0 +1,8 @@
+// { dg-additional-options "-w" }
+macro_rules! define_vars {
+    ($($v:ident)*) => { $(let $v = 15;)* }
+}
+
+fn main() {
+    define_vars!(a0 b f __some_identifier);
+}
diff --git a/gcc/testsuite/rust/compile/macro13.rs b/gcc/testsuite/rust/compile/macro13.rs
new file mode 100644
index 00000000000..eb8dfbbf393
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro13.rs
@@ -0,0 +1,12 @@
+// { dg-additional-options "-w" }
+macro_rules! create_type {
+    ($s:ident) => {
+        struct $s;
+    };
+}
+
+fn main() {
+    create_type!(A);
+
+    let a = A;
+}
diff --git a/gcc/testsuite/rust/compile/macro14.rs b/gcc/testsuite/rust/compile/macro14.rs
new file mode 100644
index 00000000000..b18c56eefc8
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro14.rs
@@ -0,0 +1,10 @@
+// { dg-additional-options "-w" }
+macro_rules! define_vars {
+    ($($v:ident)*) => { $(let $v = 15;)* }
+}
+
+fn main() -> i32 {
+    define_vars!(a0 b f __some_identifier);
+
+    b
+}
diff --git a/gcc/testsuite/rust/compile/macro15.rs b/gcc/testsuite/rust/compile/macro15.rs
new file mode 100644
index 00000000000..02c739e415e
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro15.rs
@@ -0,0 +1,12 @@
+// { dg-additional-options "-w" }
+macro_rules! create_type {
+    ($s:ident) => {
+        struct $s;
+    };
+}
+
+create_type!(SomeOuterType);
+
+fn main() {
+    let a = SomeOuterType;
+}
diff --git a/gcc/testsuite/rust/compile/macro16.rs b/gcc/testsuite/rust/compile/macro16.rs
new file mode 100644
index 00000000000..e5e56ed3f03
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro16.rs
@@ -0,0 +1,11 @@
+fn main() {
+    macro_rules! create_type {
+        ($s:ident) => {
+            struct $s(i32);
+        };
+    }
+
+    create_type!(Wrapper);
+
+    let _ = Wrapper(15);
+}
diff --git a/gcc/testsuite/rust/compile/macro17.rs b/gcc/testsuite/rust/compile/macro17.rs
new file mode 100644
index 00000000000..743216529b7
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro17.rs
@@ -0,0 +1,10 @@
+macro_rules! rep {
+    ($a:literal) => { $a }; // { dg-error "reached recursion limit" }
+    ($a:literal $(, $e:literal)*) => { // { dg-error "reached recursion limit" }
+        $a + rep!(0 $(, $e)*) // { dg-error "Failed to match" }
+    }
+}
+
+fn main() -> i32 {
+    rep!(1, 2)
+}
diff --git a/gcc/testsuite/rust/compile/macro18.rs b/gcc/testsuite/rust/compile/macro18.rs
new file mode 100644
index 00000000000..5418725b619
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro18.rs
@@ -0,0 +1,14 @@
+// { dg-additional-options "-w" }
+
+macro_rules! take_stmt {
+    ($s:stmt) => {
+        $s;
+    };
+}
+
+fn main() -> i32 {
+    take_stmt!(let complete = 15;); // { dg-error "Failed to match any rule within macro" }
+    take_stmt!(let lacking = 14);
+
+    0
+}
diff --git a/gcc/testsuite/rust/compile/macro19.rs b/gcc/testsuite/rust/compile/macro19.rs
new file mode 100644
index 00000000000..1bf9a2bfa9d
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro19.rs
@@ -0,0 +1,19 @@
+// { dg-additional-options "-w" }
+
+macro_rules! call_without_semi {
+    () => {
+        f()
+    };
+    (block) => {{
+        f()
+    }};
+}
+
+fn f() {}
+
+fn main() -> i32 {
+    call_without_semi!();
+    call_without_semi!(block);
+
+    0
+}
diff --git a/gcc/testsuite/rust/compile/macro2.rs b/gcc/testsuite/rust/compile/macro2.rs
new file mode 100644
index 00000000000..a437655ef70
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro2.rs
@@ -0,0 +1,3 @@
+macro_rules! empty_brackets {
+    [] => [];
+}
diff --git a/gcc/testsuite/rust/compile/macro20.rs b/gcc/testsuite/rust/compile/macro20.rs
new file mode 100644
index 00000000000..9f3cbca012c
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro20.rs
@@ -0,0 +1,16 @@
+macro_rules! define_trait {
+    ($assoc:ident, $i:item) => {
+        type $assoc;
+
+        $i
+    };
+}
+
+trait DefinedThroughMacros {
+    define_trait!(
+        Inner,
+        fn takes_inner(i: Self::Inner) -> Self::Inner {
+            i
+        }
+    );
+}
diff --git a/gcc/testsuite/rust/compile/macro21.rs b/gcc/testsuite/rust/compile/macro21.rs
new file mode 100644
index 00000000000..9a1d773ec4b
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro21.rs
@@ -0,0 +1,9 @@
+macro_rules! c_fn {
+    {$name:ident ($($arg_name:ident $arg_ty:ty),*) -> $ret_ty:ty} => {
+        fn $name($($arg_name: $arg_ty)*) -> $ret_ty;
+    };
+}
+
+extern "C" {
+    c_fn! {puts (s *const i8) -> i64}
+}
diff --git a/gcc/testsuite/rust/compile/macro22.rs b/gcc/testsuite/rust/compile/macro22.rs
new file mode 100644
index 00000000000..bdc4bada270
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro22.rs
@@ -0,0 +1,10 @@
+macro_rules! print {
+    () => {
+        fn puts(s: *const i8);
+        fn printf(fmt: *const i8, ...);
+    };
+}
+
+extern "C" {
+    print! {}
+}
diff --git a/gcc/testsuite/rust/compile/macro23.rs b/gcc/testsuite/rust/compile/macro23.rs
new file mode 100644
index 00000000000..afaca9bc96b
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro23.rs
@@ -0,0 +1,25 @@
+macro_rules! maybe_impl {
+    ($left:ident, $right:ident, $l_fn:ident, $r_fn:ident) => {
+        fn $l_fn(value: T) -> Maybe<T> {
+            Maybe::$left(value)
+        }
+
+        fn $r_fn() -> Maybe<T> {
+            Maybe::$right
+        }
+    };
+}
+
+enum Maybe<T> {
+    Just(T),
+    Nothing,
+}
+
+impl<T> Maybe<T> {
+    maybe_impl!(Just, Nothing, just, nothing);
+}
+
+fn main() {
+    let _ = Maybe::just(14);
+    let _: Maybe<i32> = Maybe::nothing();
+}
diff --git a/gcc/testsuite/rust/compile/macro25.rs b/gcc/testsuite/rust/compile/macro25.rs
new file mode 100644
index 00000000000..d92534c0747
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro25.rs
@@ -0,0 +1,9 @@
+macro_rules! valid {
+    ($($a:literal)* $i:ident) => {{}};
+}
+
+fn main() {
+    valid!(1 one_lit);
+    valid!(identifier_only);
+    valid!(1 2 two_lits);
+}
diff --git a/gcc/testsuite/rust/compile/macro26.rs b/gcc/testsuite/rust/compile/macro26.rs
new file mode 100644
index 00000000000..f6588e75eb0
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro26.rs
@@ -0,0 +1,10 @@
+macro_rules! repeat {
+    ( $( $i:literal ),* ; $( $j:literal ),* ) => (( $( ($i,$j) ),* ))
+    // { dg-error "different amount of matches used in merged repetitions" "" { target *-*-* } .-1 }
+}
+
+fn main() -> i32 {
+    let _ = repeat!(1, 2, 3; 2, 3);
+
+    0
+}
diff --git a/gcc/testsuite/rust/compile/macro27.rs b/gcc/testsuite/rust/compile/macro27.rs
new file mode 100644
index 00000000000..ee7833be0a6
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro27.rs
@@ -0,0 +1,8 @@
+macro_rules! m {
+    ($a:expr tok) => {
+        // { dg-error "token .identifier. is not allowed after .expr. fragment" "" { target *-*-* } .-1 }
+        // { dg-error "required first macro rule in macro rules definition could not be parsed" "" { target *-*-* } .-2 }
+        // { dg-error "failed to parse item in crate" "" { target *-*-* } .-3 }
+        $a
+    };
+}
diff --git a/gcc/testsuite/rust/compile/macro28.rs b/gcc/testsuite/rust/compile/macro28.rs
new file mode 100644
index 00000000000..8002f284ecf
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro28.rs
@@ -0,0 +1,8 @@
+macro_rules! m {
+    ($a:expr $(tok $es:expr)*) => {
+        // { dg-error "token .identifier. is not allowed after .expr. fragment" "" { target *-*-* } .-1 }
+        // { dg-error "required first macro rule in macro rules definition could not be parsed" "" { target *-*-* } .-2 }
+        // { dg-error "failed to parse item in crate" "" { target *-*-* } .-3 }
+        $a
+    };
+}
diff --git a/gcc/testsuite/rust/compile/macro29.rs b/gcc/testsuite/rust/compile/macro29.rs
new file mode 100644
index 00000000000..39f5021b74f
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro29.rs
@@ -0,0 +1,8 @@
+macro_rules! m {
+    ($($es:expr)* tok) => {
+        // { dg-error "token .identifier. is not allowed after .expr. fragment" "" { target *-*-* } .-1 }
+        // { dg-error "required first macro rule in macro rules definition could not be parsed" "" { target *-*-* } .-2 }
+        // { dg-error "failed to parse item in crate" "" { target *-*-* } .-3 }
+        $a
+    };
+}
diff --git a/gcc/testsuite/rust/compile/macro3.rs b/gcc/testsuite/rust/compile/macro3.rs
new file mode 100644
index 00000000000..e5d3e93e07b
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro3.rs
@@ -0,0 +1,3 @@
+macro_rules! empty_curlies {
+    {} => {};
+}
diff --git a/gcc/testsuite/rust/compile/macro30.rs b/gcc/testsuite/rust/compile/macro30.rs
new file mode 100644
index 00000000000..35064bc0ee5
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro30.rs
@@ -0,0 +1,8 @@
+macro_rules! m {
+    ($e:expr $f:expr) => {
+        // { dg-error "fragment is not allowed after .expr. fragment" "" { target *-*-* } .-1 }
+        // { dg-error "required first macro rule in macro rules definition could not be parsed" "" { target *-*-* } .-2 }
+        // { dg-error "failed to parse item in crate" "" { target *-*-* } .-3 }
+        $e
+    };
+}
diff --git a/gcc/testsuite/rust/compile/macro31.rs b/gcc/testsuite/rust/compile/macro31.rs
new file mode 100644
index 00000000000..6674a5fe554
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro31.rs
@@ -0,0 +1,8 @@
+macro_rules! m {
+    ($($e:expr)* $($f:expr)*) => {
+        // { dg-error "fragment is not allowed after .expr. fragment" "" { target *-*-* } .-1 }
+        // { dg-error "required first macro rule in macro rules definition could not be parsed" "" { target *-*-* } .-2 }
+        // { dg-error "failed to parse item in crate" "" { target *-*-* } .-3 }
+        $e
+    };
+}
diff --git a/gcc/testsuite/rust/compile/macro32.rs b/gcc/testsuite/rust/compile/macro32.rs
new file mode 100644
index 00000000000..d1d6305e6bd
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro32.rs
@@ -0,0 +1,19 @@
+macro_rules! s {
+    ($s:stmt) => {{}};
+}
+
+macro_rules! multi_s {
+    ($($s:stmt)+) => {{}};
+}
+
+fn main() -> i32 {
+    s!(let a = 15);
+    s!(;); // Empty statement
+    s!(let a = 15;); // { dg-error "Failed to match any rule within macro" }
+    multi_s!(let a = 15;);
+    // ^ this actually gets parsed as two statements - one LetStmt and one
+    // empty statement. This is the same behavior as rustc, which you can
+    // see using a count!() macro
+
+    32
+}
diff --git a/gcc/testsuite/rust/compile/macro33.rs b/gcc/testsuite/rust/compile/macro33.rs
new file mode 100644
index 00000000000..2ccd33e50d3
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro33.rs
@@ -0,0 +1,5 @@
+macro_rules! forbidden_frag {
+    ($t:ty $not_block:ident) => {{}}; // { dg-error "fragment specifier .ident. is not allowed after .ty. fragments" }
+                                      // { dg-error "required first macro rule in macro rules definition could not be parsed" "" { target *-*-* } .-1 }
+                                      // { dg-error "failed to parse item in crate" "" { target *-*-* } .-2 }
+}
diff --git a/gcc/testsuite/rust/compile/macro34.rs b/gcc/testsuite/rust/compile/macro34.rs
new file mode 100644
index 00000000000..105d042fd50
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro34.rs
@@ -0,0 +1,3 @@
+macro_rules! allowed_after_expr_matcher {
+    (($t:expr) bok) => {{}}; // follow-set restrictions do not apply after a matcher, but they do apply inside the matcher
+}
diff --git a/gcc/testsuite/rust/compile/macro35.rs b/gcc/testsuite/rust/compile/macro35.rs
new file mode 100644
index 00000000000..07b157b53c2
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro35.rs
@@ -0,0 +1,7 @@
+macro_rules! inside_matcher {
+    (($e:expr tok) tok) => {{}}; // { dg-error "token .identifier. is not allowed after .expr. fragment" }
+                                 // { dg-error "failed to parse macro matcher" "" { target *-*-* } .-1 }
+                                 // { dg-error "failed to parse macro match" "" { target *-*-* } .-2 }
+                                 // { dg-error "required first macro rule" "" { target *-*-* } .-3 }
+                                 // { dg-error "failed to parse item in crate" "" { target *-*-* } .-4 }
+}
diff --git a/gcc/testsuite/rust/compile/macro36.rs b/gcc/testsuite/rust/compile/macro36.rs
new file mode 100644
index 00000000000..e5d66b22b7b
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro36.rs
@@ -0,0 +1,3 @@
+macro_rules! ty_allowed {
+    ($t:ty $b:block) => {{}};
+}
diff --git a/gcc/testsuite/rust/compile/macro37.rs b/gcc/testsuite/rust/compile/macro37.rs
new file mode 100644
index 00000000000..5713d90130a
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro37.rs
@@ -0,0 +1,5 @@
+macro_rules! invalid_after_zeroable {
+    ($e:expr $(,)* forbidden) => {{}}; // { dg-error "token .identifier. is not allowed after .expr. fragment" }
+                                       // { dg-error "required first macro rule" "" { target *-*-* } .-1 }
+                                       // { dg-error "failed to parse item in crate" "" { target *-*-* } .-2 }
+}
diff --git a/gcc/testsuite/rust/compile/macro38.rs b/gcc/testsuite/rust/compile/macro38.rs
new file mode 100644
index 00000000000..eb294aec83b
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro38.rs
@@ -0,0 +1,5 @@
+macro_rules! invalid_after_zeroable_multi {
+    ($e:expr $(,)? $(;)* $(=>)? forbidden) => {{}}; // { dg-error "token .identifier. is not allowed after .expr. fragment" }
+                                                    // { dg-error "required first macro rule" "" { target *-*-* } .-1 }
+                                                    // { dg-error "failed to parse item in crate" "" { target *-*-* } .-2 }
+}
diff --git a/gcc/testsuite/rust/compile/macro39.rs b/gcc/testsuite/rust/compile/macro39.rs
new file mode 100644
index 00000000000..f5c498cc8ef
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro39.rs
@@ -0,0 +1,5 @@
+macro_rules! m {
+    ($e:expr (, parenthesis_forbidden)) => {{}}; // { dg-error "token .\\(. at start of matcher is not allowed after .expr. fragment" }
+                                                 // { dg-error "required first macro rule" "" { target *-*-* } .-1 }
+                                                 // { dg-error "failed to parse item in crate" "" { target *-*-* } .-2 }
+}
diff --git a/gcc/testsuite/rust/compile/macro4.rs b/gcc/testsuite/rust/compile/macro4.rs
new file mode 100644
index 00000000000..47ff6c93d87
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro4.rs
@@ -0,0 +1,3 @@
+macro_rules! one_keyword {
+    (kw) => {};
+}
diff --git a/gcc/testsuite/rust/compile/macro40.rs b/gcc/testsuite/rust/compile/macro40.rs
new file mode 100644
index 00000000000..7151f3a83bc
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro40.rs
@@ -0,0 +1,48 @@
+// { dg-additional-options "-w" }
+
+macro_rules! t {
+    () => {
+        i32
+    };
+}
+
+macro_rules! s {
+    () => {
+        *const i8
+    };
+}
+
+extern "C" {
+    fn printf(s: s!(), ...);
+}
+
+fn square(arg: t!()) -> t!() {
+    let input: t!() = arg;
+
+    input * input
+}
+
+trait Trait {
+    fn f() -> t!();
+    fn g(arg: t!());
+}
+
+struct Wrapper {
+    inner: t!(),
+}
+
+impl Trait for Wrapper {
+    fn f() -> t!() {
+        1
+    }
+
+    fn g(arg: t!()) {}
+}
+
+fn id<T>(arg: T) -> T {
+    arg
+}
+
+fn main() {
+    id::<t!()>(15);
+}
diff --git a/gcc/testsuite/rust/compile/macro41.rs b/gcc/testsuite/rust/compile/macro41.rs
new file mode 100644
index 00000000000..38244222924
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro41.rs
@@ -0,0 +1,13 @@
+macro_rules! empty {
+    ($($t:tt)*) => {};
+}
+
+empty! {nothing}
+empty! {struct OuterItem;}
+empty! {}
+
+fn main() {
+    empty! {as statement};
+    empty! {any child item};
+    empty! {};
+}
diff --git a/gcc/testsuite/rust/compile/macro42.rs b/gcc/testsuite/rust/compile/macro42.rs
new file mode 100644
index 00000000000..52d150b82ba
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro42.rs
@@ -0,0 +1,32 @@
+// { dg-additional-options "-w -frust-cfg=A" }
+#[rustc_builtin_macro]
+macro_rules! cfg {
+    () => {{}};
+}
+
+fn main() -> i32 {
+    let mut res = 0;
+    if cfg!(A) {
+        res = 1;
+    }
+
+    if cfg!(A) {
+        res = 2;
+    } else {
+        res = 3;
+    }
+
+    if cfg!(A) {
+        res = 4;
+    } else if cfg!(A) {
+        res = 5;
+    }
+
+    let res = if cfg!(A) {
+        6
+    } else {
+        7
+    };
+
+    return res;
+}
diff --git a/gcc/testsuite/rust/compile/macro5.rs b/gcc/testsuite/rust/compile/macro5.rs
new file mode 100644
index 00000000000..a5d80952e28
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro5.rs
@@ -0,0 +1,3 @@
+macro_rules! rust_keyword {
+    (fn) => {};
+}
diff --git a/gcc/testsuite/rust/compile/macro6.rs b/gcc/testsuite/rust/compile/macro6.rs
new file mode 100644
index 00000000000..0ca35ba6888
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro6.rs
@@ -0,0 +1,11 @@
+macro_rules! zero_or_one {
+    ($($a:literal)?) => { // { dg-error "invalid amount of matches for macro invocation. Expected between 0 and 1, got 2" }
+        f();
+    }
+}
+
+fn main() {
+    zero_or_one!();
+    zero_or_one!(14);
+    zero_or_one!(125 12 "gcc"); // { dg-error "Failed to match any rule within macro" }
+}
diff --git a/gcc/testsuite/rust/compile/macro7.rs b/gcc/testsuite/rust/compile/macro7.rs
new file mode 100644
index 00000000000..abc48057c54
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro7.rs
@@ -0,0 +1,13 @@
+fn f() {}
+
+macro_rules! one_or_more {
+    ($($a:literal)+) => { // { dg-error "invalid amount of matches for macro invocation" }
+        f();
+    };
+}
+
+fn main() {
+    one_or_more!(1 1 1 1 1 1 1 1 1 1 1 "rust" 'c');
+    one_or_more!(1);
+    one_or_more!(); // { dg-error "Failed to match any rule within macro" }
+}
diff --git a/gcc/testsuite/rust/compile/macro8.rs b/gcc/testsuite/rust/compile/macro8.rs
new file mode 100644
index 00000000000..d3e8af93a6e
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro8.rs
@@ -0,0 +1,12 @@
+fn f() {}
+
+macro_rules! expr {
+    ($($a:expr)?) => {
+        f();
+    };
+}
+
+fn main() {
+    expr!();
+    expr!(14);
+}
diff --git a/gcc/testsuite/rust/compile/macro9.rs b/gcc/testsuite/rust/compile/macro9.rs
new file mode 100644
index 00000000000..9a59089b1e4
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro9.rs
@@ -0,0 +1,17 @@
+macro_rules! add {
+    ($e:expr, $($es:expr),*) => {
+        $e + add!($($es),*)
+    };
+    ($e:expr) => {
+        $e
+    };
+}
+
+fn main() -> i32 {
+    let a = add!(15 2 9); // { dg-error "Failed to match any rule within macro" }
+    let b = add!(15);
+    let b = add!(15 14); // { dg-error "Failed to match any rule within macro" }
+    let b = add!(15, 14,); // { dg-error "Failed to match any rule within macro" }
+
+    0
+}
diff --git a/gcc/testsuite/rust/compile/macro_return.rs b/gcc/testsuite/rust/compile/macro_return.rs
new file mode 100644
index 00000000000..8b06f875cc0
--- /dev/null
+++ b/gcc/testsuite/rust/compile/macro_return.rs
@@ -0,0 +1,10 @@
+// { dg-additional-options "-w" }
+
+macro_rules! add {
+    ($a:expr) => { $a };
+    ($a:expr, $($b:expr),+) => { $a + add!($($b),*) }
+}
+
+fn main() -> i32 {
+    add!(add!(1, 2))
+}
diff --git a/gcc/testsuite/rust/compile/match1.rs b/gcc/testsuite/rust/compile/match1.rs
new file mode 100644
index 00000000000..f649f3a1931
--- /dev/null
+++ b/gcc/testsuite/rust/compile/match1.rs
@@ -0,0 +1,16 @@
+enum Foo {
+    A,
+    B,
+    C(char),
+    D { x: i64, y: i64 },
+}
+
+fn inspect(f: Foo) {
+    match f {
+        Foo::A => {}
+        Foo::B => {}
+        Foo::C(a, b) => {}
+        // { dg-error "this pattern has 2 fields but the corresponding tuple variant has 1 field" "" { target *-*-* } .-1 }
+        Foo::D { x, y } => {}
+    }
+}
diff --git a/gcc/testsuite/rust/compile/match2.rs b/gcc/testsuite/rust/compile/match2.rs
new file mode 100644
index 00000000000..359936a187c
--- /dev/null
+++ b/gcc/testsuite/rust/compile/match2.rs
@@ -0,0 +1,15 @@
+enum Foo {
+    A,
+    B,
+    C(char),
+    D { x: i64, y: i64 },
+}
+
+fn inspect(f: Foo) {
+    match f {
+        Foo::A => {}
+        Foo::B => {}
+        Foo::C(x) => {}
+        Foo::D { y } => {} // { dg-error "pattern does not mention fields x" }
+    }
+}
diff --git a/gcc/testsuite/rust/compile/match3.rs b/gcc/testsuite/rust/compile/match3.rs
new file mode 100644
index 00000000000..98181e85197
--- /dev/null
+++ b/gcc/testsuite/rust/compile/match3.rs
@@ -0,0 +1,16 @@
+enum Foo {
+    A,
+    B,
+    C(char),
+    D { x: i64, y: i64 },
+}
+
+fn inspect(f: Foo) {
+    match f {
+        Foo::A => {}
+        Foo::B => {}
+        Foo::C(x) => {}
+        Foo::D { z } => {} // { dg-error "variant D does not have a field named z" }
+                           // { dg-error "pattern does not mention fields x, y" "" { target *-*-* } .-1 }
+    }
+}
diff --git a/gcc/testsuite/rust/compile/match4.rs b/gcc/testsuite/rust/compile/match4.rs
new file mode 100644
index 00000000000..35b90a64fa5
--- /dev/null
+++ b/gcc/testsuite/rust/compile/match4.rs
@@ -0,0 +1,16 @@
+enum Foo {
+    A,
+    B,
+    C(char),
+    D { x: i64, y: i64 },
+}
+
+fn inspect(f: Foo) {
+    match f {
+        Foo::A => {}
+        Foo::B => {}
+        Foo::C { a } => {}
+        // { dg-error "expected struct variant, found tuple variant C" "" { target *-*-* } .-1 }
+        Foo::D { x, y } => {}
+    }
+}
diff --git a/gcc/testsuite/rust/compile/match5.rs b/gcc/testsuite/rust/compile/match5.rs
new file mode 100644
index 00000000000..a5f934d6aeb
--- /dev/null
+++ b/gcc/testsuite/rust/compile/match5.rs
@@ -0,0 +1,15 @@
+enum Foo {
+    A,
+    B,
+    C(char),
+    D { x: i64, y: i64 },
+}
+
+fn inspect(f: Foo) {
+    match f {
+        Foo::A => {}
+        Foo::B => {}
+        Foo::C(a) => {}
+        Foo::D(x, y) => {} // { dg-error "expected tuple struct or tuple variant, found struct variant 'Foo::D'" }
+    }
+}
diff --git a/gcc/testsuite/rust/compile/match6.rs b/gcc/testsuite/rust/compile/match6.rs
new file mode 100644
index 00000000000..8fe06f7c116
--- /dev/null
+++ b/gcc/testsuite/rust/compile/match6.rs
@@ -0,0 +1,18 @@
+fn foo() -> bool {
+    true
+}
+
+fn int32() -> i32 {
+    1
+}
+
+fn bar() -> i32 {
+    match foo() {
+        true => int32(),
+        false => 0
+    }
+}
+
+fn main() -> () {
+    bar();
+}
\ No newline at end of file
diff --git a/gcc/testsuite/rust/compile/match7.rs b/gcc/testsuite/rust/compile/match7.rs
new file mode 100644
index 00000000000..b16a1883522
--- /dev/null
+++ b/gcc/testsuite/rust/compile/match7.rs
@@ -0,0 +1,12 @@
+fn bar (x: u8, y: u8) -> i32 {
+    match (x, y) {
+        (1, 1) => { return 1; }
+        (1, _) => { return -1; }
+    }
+
+    return 0;
+}
+
+fn main () -> () {
+    bar (1, 2);
+}
diff --git a/gcc/testsuite/rust/compile/method1.rs b/gcc/testsuite/rust/compile/method1.rs
new file mode 100644
index 00000000000..18652406085
--- /dev/null
+++ b/gcc/testsuite/rust/compile/method1.rs
@@ -0,0 +1,13 @@
+struct Foo(i32);
+impl Foo {
+    fn test() {}
+}
+
+pub fn main() {
+    let a;
+    a = Foo(123);
+
+    a.test();
+    // { dg-error "failed to resolve method for .test." "" { target *-*-* } .-1 }
+    // { dg-error {failed to type resolve expression} "" { target *-*-* } .-2 }
+}
diff --git a/gcc/testsuite/rust/compile/method2.rs b/gcc/testsuite/rust/compile/method2.rs
new file mode 100644
index 00000000000..c8699f77c6a
--- /dev/null
+++ b/gcc/testsuite/rust/compile/method2.rs
@@ -0,0 +1,16 @@
+struct Foo<A, B>(A, B);
+
+impl Foo<i32, f32> {
+    fn test<X>(self, a: X) -> X {
+        a
+    }
+}
+
+fn main() {
+    let a;
+    a = Foo(123, 456f32);
+
+    let b;
+    b = a.test::<asfasfr>(false);
+    // { dg-error "failed to resolve TypePath: asfasfr" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/mismatch-crate-name.rs b/gcc/testsuite/rust/compile/mismatch-crate-name.rs
new file mode 100644
index 00000000000..e259b9e46cc
--- /dev/null
+++ b/gcc/testsuite/rust/compile/mismatch-crate-name.rs
@@ -0,0 +1,4 @@
+// { dg-additional-options "-frust-crate=another_name" }
+#![crate_name = "legit_name"]
+// { dg-error ".-frust-crate-name. and .#.crate_name.. are required to match, but .another_name. does not match .legit_name." "" { target *-*-* } .-1 }
+fn main() {}
diff --git a/gcc/testsuite/rust/compile/missing_middle/both_path.rs b/gcc/testsuite/rust/compile/missing_middle/both_path.rs
new file mode 100644
index 00000000000..5e5ad15457a
--- /dev/null
+++ b/gcc/testsuite/rust/compile/missing_middle/both_path.rs
@@ -0,0 +1,3 @@
+pub fn f() -> u32 {
+    5
+}
diff --git a/gcc/testsuite/rust/compile/missing_middle/explicit.not.rs b/gcc/testsuite/rust/compile/missing_middle/explicit.not.rs
new file mode 100644
index 00000000000..e28288b0f99
--- /dev/null
+++ b/gcc/testsuite/rust/compile/missing_middle/explicit.not.rs
@@ -0,0 +1 @@
+mod other;
diff --git a/gcc/testsuite/rust/compile/missing_middle/inner_path.rs b/gcc/testsuite/rust/compile/missing_middle/inner_path.rs
new file mode 100644
index 00000000000..daf4e3cc25a
--- /dev/null
+++ b/gcc/testsuite/rust/compile/missing_middle/inner_path.rs
@@ -0,0 +1,3 @@
+pub fn f() -> u32 {
+    4
+}
diff --git a/gcc/testsuite/rust/compile/missing_middle/other.rs b/gcc/testsuite/rust/compile/missing_middle/other.rs
new file mode 100644
index 00000000000..0c0884ec968
--- /dev/null
+++ b/gcc/testsuite/rust/compile/missing_middle/other.rs
@@ -0,0 +1,3 @@
+pub fn f() -> u32 {
+    2
+}
diff --git a/gcc/testsuite/rust/compile/missing_middle/outer_path.rs b/gcc/testsuite/rust/compile/missing_middle/outer_path.rs
new file mode 100644
index 00000000000..fbe5074191b
--- /dev/null
+++ b/gcc/testsuite/rust/compile/missing_middle/outer_path.rs
@@ -0,0 +1,3 @@
+pub fn f() -> u32 {
+    3
+}
diff --git a/gcc/testsuite/rust/compile/missing_middle/sub/mod.rs b/gcc/testsuite/rust/compile/missing_middle/sub/mod.rs
new file mode 100644
index 00000000000..f099d61e04a
--- /dev/null
+++ b/gcc/testsuite/rust/compile/missing_middle/sub/mod.rs
@@ -0,0 +1,3 @@
+pub fn f() -> u32 {
+    1
+}
diff --git a/gcc/testsuite/rust/compile/missing_return1.rs b/gcc/testsuite/rust/compile/missing_return1.rs
new file mode 100644
index 00000000000..00bf393dbb9
--- /dev/null
+++ b/gcc/testsuite/rust/compile/missing_return1.rs
@@ -0,0 +1,6 @@
+// { dg-error "expected .* got .*" "" { target *-*-* } 0 }
+fn test1() -> i32 {}
+
+fn main() {
+    let call1 = test1();
+}
diff --git a/gcc/testsuite/rust/compile/mod_missing_middle.rs b/gcc/testsuite/rust/compile/mod_missing_middle.rs
new file mode 100644
index 00000000000..79633407671
--- /dev/null
+++ b/gcc/testsuite/rust/compile/mod_missing_middle.rs
@@ -0,0 +1,29 @@
+// { dg-additional-options "-w" }
+
+mod missing_middle {
+    mod sub;
+
+    #[path = "explicit.not.rs"]
+    mod explicit;
+}
+
+#[path = "missing_middle"]
+mod with_outer_path_attr {
+    #[path = "outer_path.rs"]
+    mod inner;
+}
+
+mod with_inner_path_attr {
+    #![path = "missing_middle"]
+
+    #[path = "inner_path.rs"]
+    mod inner;
+}
+
+#[path = "missing_middle"]
+mod with_both_path_attr {
+    #![path = "this_is_ignored"]
+
+    #[path = "both_path.rs"]
+    mod inner;
+}
diff --git a/gcc/testsuite/rust/compile/never_type_err1.rs b/gcc/testsuite/rust/compile/never_type_err1.rs
new file mode 100644
index 00000000000..52b1283fadf
--- /dev/null
+++ b/gcc/testsuite/rust/compile/never_type_err1.rs
@@ -0,0 +1,14 @@
+fn test() {
+    let a;
+
+    // FIXME: Unimplemented features
+    a = if true { // { dg-error "expected .T.. got .!." }
+        return;
+    } else {
+        return;
+    };
+}
+
+fn main() {
+    test();
+}
diff --git a/gcc/testsuite/rust/compile/privacy1.rs b/gcc/testsuite/rust/compile/privacy1.rs
new file mode 100644
index 00000000000..1cc83c04abe
--- /dev/null
+++ b/gcc/testsuite/rust/compile/privacy1.rs
@@ -0,0 +1,11 @@
+mod orange {
+    mod green {
+        fn sain() {}
+        pub fn doux() {}
+    }
+
+    fn brown() {
+        green::sain(); // { dg-error "definition is private in this context" }
+        green::doux();
+    }
+}
diff --git a/gcc/testsuite/rust/compile/privacy2.rs b/gcc/testsuite/rust/compile/privacy2.rs
new file mode 100644
index 00000000000..3c0744928b1
--- /dev/null
+++ b/gcc/testsuite/rust/compile/privacy2.rs
@@ -0,0 +1,13 @@
+// { dg-additional-options "-w" }
+
+mod orange {
+    fn tangerine() {}
+
+    mod green {
+        mod blue {
+            fn berry() {
+                tangerine();
+            }
+        }
+    }
+}
diff --git a/gcc/testsuite/rust/compile/privacy3.rs b/gcc/testsuite/rust/compile/privacy3.rs
new file mode 100644
index 00000000000..d48acea4786
--- /dev/null
+++ b/gcc/testsuite/rust/compile/privacy3.rs
@@ -0,0 +1,28 @@
+mod orange {
+    mod green {
+        fn sain_void() {}
+        fn sain() -> bool {
+            false
+        }
+        pub fn doux() {}
+    }
+
+    fn brown() {
+        if green::sain() {
+            // { dg-error "definition is private in this context" "" { target *-*-* } .-1 }
+            green::doux();
+        }
+
+        {
+            green::sain();
+            // { dg-error "definition is private in this context" "" { target *-*-* } .-1 }
+            green::sain();
+            // { dg-error "definition is private in this context" "" { target *-*-* } .-1 }
+            green::sain_void()
+            // { dg-error "definition is private in this context" "" { target *-*-* } .-1 }
+        }
+
+        let a = green::sain();
+        // { dg-error "definition is private in this context" "" { target *-*-* } .-1 }
+    }
+}
diff --git a/gcc/testsuite/rust/compile/privacy4.rs b/gcc/testsuite/rust/compile/privacy4.rs
new file mode 100644
index 00000000000..d1ce0afd654
--- /dev/null
+++ b/gcc/testsuite/rust/compile/privacy4.rs
@@ -0,0 +1,19 @@
+mod orange {
+    mod green {
+        fn bean<T>(value: T) -> T {
+            value
+        }
+    }
+
+    fn brown() {
+        green::bean::<bool>(false);
+        // { dg-error "definition is private in this context" "" { target *-*-* } .-1 }
+        let a = green::bean::<i32>(15);
+        // { dg-error "definition is private in this context" "" { target *-*-* } .-1 }
+
+        struct S;
+
+        let s = green::bean(S);
+        // { dg-error "definition is private in this context" "" { target *-*-* } .-1 }
+    }
+}
diff --git a/gcc/testsuite/rust/compile/privacy5.rs b/gcc/testsuite/rust/compile/privacy5.rs
new file mode 100644
index 00000000000..0e0e496dde2
--- /dev/null
+++ b/gcc/testsuite/rust/compile/privacy5.rs
@@ -0,0 +1,17 @@
+mod orange {
+    mod green {
+        struct Foo;
+        pub(in orange) struct Bar;
+        pub struct Baz;
+    }
+
+    fn brown() {
+        let _ = green::Foo; // { dg-error "definition is private in this context" }
+        let _ = green::Bar;
+        let _ = green::Baz;
+
+        let _: green::Foo; // { dg-error "definition is private in this context" }
+
+        fn any(a0: green::Foo, a1: green::Bar) {} // { dg-error "20:definition is private in this context" }
+    }
+}
diff --git a/gcc/testsuite/rust/compile/privacy6.rs b/gcc/testsuite/rust/compile/privacy6.rs
new file mode 100644
index 00000000000..487ed024209
--- /dev/null
+++ b/gcc/testsuite/rust/compile/privacy6.rs
@@ -0,0 +1,39 @@
+// { dg-additional-options "-w" }
+
+struct Adt;
+enum EAdt {
+    V0,
+    V1,
+}
+struct Registers {
+    r0: i64,
+    r1: i64,
+    r2: i64,
+    r3: i64,
+}
+trait Foo {}
+
+fn foo1(value: bool) {}
+fn foo2(value: char) {}
+fn foo3(value: i32) {}
+fn foo4(value: u16) {}
+fn foo5(value: f64) {}
+fn foo6(value: usize) {}
+fn foo7(value: isize) {}
+fn foo8(value: Adt) {}
+fn foo9(value: EAdt) {}
+fn foo10(value: &str) {}
+fn foo11(value: *const i8) {}
+fn foo12<T>(value: T) {}
+fn foo13(value: [i32; 5]) {}
+fn foo14(value: [Adt]) {}
+fn foo15(value: fn(i32) -> i32) {}
+fn foo16(value: (i32, Adt)) {}
+fn foo17(value: (i32, [f64; 5])) {}
+fn foo18(value: Registers) {}
+fn foo19(value: &dyn Foo) {}
+fn foo20(value: &[Adt]) {}
+// FIXME: Uncomment once #1257 is fixed
+// fn foo21(value: fn(i32)) {}
+// fn foo22(value: fn()) {}
+fn foo23(value: fn() -> i32) {}
diff --git a/gcc/testsuite/rust/compile/pub_restricted_1.rs b/gcc/testsuite/rust/compile/pub_restricted_1.rs
new file mode 100644
index 00000000000..9bda9682403
--- /dev/null
+++ b/gcc/testsuite/rust/compile/pub_restricted_1.rs
@@ -0,0 +1,13 @@
+pub mod foo {
+    pub mod bar {
+        pub fn baz() {}
+
+        pub(in foo::bar) struct A0;
+    }
+}
+
+pub(in foo::fah::baz) struct A1; // { dg-error "cannot find simple path segment .fah." }
+pub(in fro::bulator::saindoux) struct A2; // { dg-error "cannot find simple path segment .fro." }
+pub(in foo::bar::saindoux) struct A3; // { dg-error "cannot find simple path segment .saindoux." }
+
+fn main() {}
diff --git a/gcc/testsuite/rust/compile/pub_restricted_2.rs b/gcc/testsuite/rust/compile/pub_restricted_2.rs
new file mode 100644
index 00000000000..8588f2775ca
--- /dev/null
+++ b/gcc/testsuite/rust/compile/pub_restricted_2.rs
@@ -0,0 +1,18 @@
+// { dg-additional-options "-w" }
+
+mod foo {
+    mod bar {
+        mod baz {
+            pub(in baz) struct A0;
+            pub(in bar::baz) struct A1;
+            pub(in foo::bar::baz) struct A2;
+
+            mod sain {
+                mod doux {}
+            }
+
+            pub(in sain) struct A3; // { dg-error "restricted path is not an ancestor of the current module" }
+            pub(in sain::doux) struct A4; // { dg-error "restricted path is not an ancestor of the current module" }
+        }
+    }
+}
diff --git a/gcc/testsuite/rust/compile/pub_restricted_3.rs b/gcc/testsuite/rust/compile/pub_restricted_3.rs
new file mode 100644
index 00000000000..d477385d761
--- /dev/null
+++ b/gcc/testsuite/rust/compile/pub_restricted_3.rs
@@ -0,0 +1,11 @@
+// { dg-additional-options "-w" }
+
+mod foo {
+    mod bar {
+        pub(in foo) fn baz() {}
+    }
+
+    fn baz() {
+        bar::baz(); // no error, foo::bar::baz is public in foo
+    }
+}
diff --git a/gcc/testsuite/rust/compile/raw_identifiers_bad_keywords.rs b/gcc/testsuite/rust/compile/raw_identifiers_bad_keywords.rs
new file mode 100644
index 00000000000..854d7e6edee
--- /dev/null
+++ b/gcc/testsuite/rust/compile/raw_identifiers_bad_keywords.rs
@@ -0,0 +1,3 @@
+pub fn plus(n: i32, m: i32) -> i32 {
+    r#crate /* { dg-error "forbidden raw identifier" } */
+}
diff --git a/gcc/testsuite/rust/compile/raw_identifiers_underscore.rs b/gcc/testsuite/rust/compile/raw_identifiers_underscore.rs
new file mode 100644
index 00000000000..86e9013a50b
--- /dev/null
+++ b/gcc/testsuite/rust/compile/raw_identifiers_underscore.rs
@@ -0,0 +1,3 @@
+pub fn s(num: i32) -> i32 {
+    r#_ * num /* { dg-error "not a valid raw identifier" } */
+}
diff --git a/gcc/testsuite/rust/compile/rawbytestring.rs b/gcc/testsuite/rust/compile/rawbytestring.rs
new file mode 100644
index 0000000000000000000000000000000000000000..9c6b762a7fd378206a3bfe21db5b708890f5466f
GIT binary patch
literal 3234
zcmbVOO>fgc5amjL#mG4T6)1rVl`5`1a^M^Zc^f;m2zI;c($Wfvf5=~A-pqd4PU65Z
z<9Tmp-n`vS-O~56Y3cQwv*$CS<&wUX59E5=v|Go4UDeZ9>ni$0wkR&M$OnWLi=tR8
zvhYe0>#n0kp8Z~u3yqU0ZIOclm4066_dMaIdKBLE<JDDhNy~HEHGO5v9U=0T+ODUv
zC8SmEy1cFEe3@FkZM6EI->90VG(Y=lGOCeT&0tuLp+z$p*Er0}$>V{I!^8|YFtTxx
z@X*l4>D0{rUt=35bE5|t9J_s{&GuboZD*<I?tAKLvSqui3i{=Bxx4RJ6csV<pY3qx
zc%EYYDlHYkjRXh2@TuH1=aM+;Gqyvv;&mx;T8-!69@lIn-t3a5*&*G8KFpvI38NDZ
zXRR0;(@$zf^Mz-&9d5I9*G<Ew+mP5OSua;jH^}=FDaIRU+8^bv*+6`M(70oU!0U_=
z&}eCgAm&LiE1Ztuo)19+f+2(Qu2!m#_4vb;|G-CZh`7Kh;Epf$lpotHpVZa9RxPJ`
z*!LJ1N@A_5D-K2!c50h=c|}nH2&&HIi=qJdndb5#r=*{jFDfG+GVizjpnnJPCErUm
z(~py#01%ck2asI=5SB3ogcab#=?eJBr4{72%hYuq1akuw_HYtNmI2frgB`4tK#Ur;
zF6x6XH@P+_Ld&Pj=Khmtif_<#%m^#v8|1@fDley8DqbpRJ8##35S;)CLQU5(s-g1&
zGH1b11D=)VWpyG#bwi0++xi+Rry%Bx8xX28AhXsD5b>@|GH+hjmxkvqUg|FRiNY&&
zP6(%e4anlh3W@7JWKOd9E)p`E*!Jfr70=|kILq??taYC%vd4tW9O052<zlNPu3_&s
zQXTCpCkvg$z92;~u`^Cz|8;Ub$4U{W)axrh#`o>FwtHy(W1l^5)-!Q6rkeY2pcOb5
zC5~Q^#`Cf!S&N9GM~?nelacMDHe;2ejYcV-D%(M~7r|5FJ&7hOIQ$Oo!_o8>9i>^x
zV>X-Uc!7Jfq5+jI?0J=nn!sj`v1wMcU}PH?O>D8bJ*^GcSU|ak{Lxs!g8aAiFN}Pz
A0RR91

literal 0
HcmV?d00001

diff --git a/gcc/testsuite/rust/compile/redef_error1.rs b/gcc/testsuite/rust/compile/redef_error1.rs
new file mode 100644
index 00000000000..ae51e36c87f
--- /dev/null
+++ b/gcc/testsuite/rust/compile/redef_error1.rs
@@ -0,0 +1,8 @@
+struct S1 {
+    x: f64,
+    y: f64,
+}
+
+struct S1(i32, bool); // { dg-error "redefined multiple times" }
+
+fn main() {}
diff --git a/gcc/testsuite/rust/compile/redef_error2.rs b/gcc/testsuite/rust/compile/redef_error2.rs
new file mode 100644
index 00000000000..65793bcda8a
--- /dev/null
+++ b/gcc/testsuite/rust/compile/redef_error2.rs
@@ -0,0 +1,4 @@
+const TEST: i32 = 2;
+const TEST: f32 = 3.0; // { dg-error "redefined multiple times" }
+
+fn main() {}
diff --git a/gcc/testsuite/rust/compile/redef_error3.rs b/gcc/testsuite/rust/compile/redef_error3.rs
new file mode 100644
index 00000000000..a4bf1ed3d8c
--- /dev/null
+++ b/gcc/testsuite/rust/compile/redef_error3.rs
@@ -0,0 +1,9 @@
+fn test() -> bool {
+    true
+}
+
+fn test() -> i32 { // { dg-error "redefined multiple times" }
+    123
+}
+
+fn main() {}
diff --git a/gcc/testsuite/rust/compile/redef_error4.rs b/gcc/testsuite/rust/compile/redef_error4.rs
new file mode 100644
index 00000000000..a250c0ac00e
--- /dev/null
+++ b/gcc/testsuite/rust/compile/redef_error4.rs
@@ -0,0 +1,27 @@
+struct Foo(i32, bool);
+
+impl Foo {
+    fn new(a: i32, b: bool) -> Foo {
+        Foo(a, b)
+    }
+
+    fn test() -> i32 {
+        test()
+    }
+
+    fn test() -> bool { // { dg-error "redefined multiple times" }
+        true
+    }
+}
+
+fn test() -> i32 {
+    123
+}
+
+fn main() {
+    let a;
+    a = Foo::new(1, true);
+
+    let b;
+    b = Foo::test();
+}
diff --git a/gcc/testsuite/rust/compile/redef_error5.rs b/gcc/testsuite/rust/compile/redef_error5.rs
new file mode 100644
index 00000000000..dc6ad50e104
--- /dev/null
+++ b/gcc/testsuite/rust/compile/redef_error5.rs
@@ -0,0 +1,8 @@
+struct Foo(i32, bool);
+
+impl Foo {
+    const TEST: i32 = 123;
+    const TEST: bool = false; // { dg-error "redefined multiple times"  }
+}
+
+fn main() {}
diff --git a/gcc/testsuite/rust/compile/redef_error6.rs b/gcc/testsuite/rust/compile/redef_error6.rs
new file mode 100644
index 00000000000..664c6ae9894
--- /dev/null
+++ b/gcc/testsuite/rust/compile/redef_error6.rs
@@ -0,0 +1,13 @@
+struct Foo<T>(T, usize);
+
+impl Foo<i32> {
+    fn test() -> i32 {
+        123
+    }
+
+    fn test(self) -> i32 { // { dg-error "redefined multiple times" }
+        self.0
+    }
+}
+
+fn main() {}
diff --git a/gcc/testsuite/rust/compile/reference1.rs b/gcc/testsuite/rust/compile/reference1.rs
new file mode 100644
index 00000000000..ff791533754
--- /dev/null
+++ b/gcc/testsuite/rust/compile/reference1.rs
@@ -0,0 +1,6 @@
+fn main() {
+    let a = &123;
+    let b: &mut i32 = a;
+    // { dg-error "mismatched mutability" "" { target *-*-* } .-1 }
+    // { dg-error "expected .&mut i32. got .& i32." "" { target *-*-* } .-2 }
+}
diff --git a/gcc/testsuite/rust/compile/self-path1.rs b/gcc/testsuite/rust/compile/self-path1.rs
new file mode 100644
index 00000000000..425ba848fc0
--- /dev/null
+++ b/gcc/testsuite/rust/compile/self-path1.rs
@@ -0,0 +1,12 @@
+// { dg-additional-options "-w" }
+struct foo;
+
+fn bar() -> self::foo {
+    crate::foo
+}
+
+fn baz() {
+    let a: foo = self::bar();
+
+    crate::bar();
+}
diff --git a/gcc/testsuite/rust/compile/self-path2.rs b/gcc/testsuite/rust/compile/self-path2.rs
new file mode 100644
index 00000000000..b9b82cae5a6
--- /dev/null
+++ b/gcc/testsuite/rust/compile/self-path2.rs
@@ -0,0 +1,21 @@
+// { dg-additional-options "-w" }
+struct foo;
+
+fn bar() -> self::foo {
+    crate::foo
+}
+
+fn baz() {
+    let a: foo = self::bar();
+
+    crate::bar();
+
+    crate::self::foo();
+    // { dg-error "failed to resolve: .self. in paths can only be used in start position" "" { target *-*-* } .-1 }
+}
+
+type a = foo;
+type b = crate::foo;
+type c = self::foo;
+type d = crate::self::foo;
+// { dg-error "failed to resolve: .self. in paths can only be used in start position" "" { target *-*-* } .-1 }
diff --git a/gcc/testsuite/rust/compile/shadow1.rs b/gcc/testsuite/rust/compile/shadow1.rs
new file mode 100644
index 00000000000..77410e932da
--- /dev/null
+++ b/gcc/testsuite/rust/compile/shadow1.rs
@@ -0,0 +1,7 @@
+fn main() {
+    let mut x = 5;
+    let mut x;
+    x = true;
+    x = x + 2; // { dg-error "cannot apply this operator to types bool and <integer>"  }
+               // { dg-error {failed to type resolve expression} "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/specify-crate-name.rs b/gcc/testsuite/rust/compile/specify-crate-name.rs
new file mode 100644
index 00000000000..a8679157024
--- /dev/null
+++ b/gcc/testsuite/rust/compile/specify-crate-name.rs
@@ -0,0 +1,7 @@
+// { dg-additional-options "-frust-crate=fancy_crate_name -fdump-tree-gimple" }
+pub fn does_nothing() {}
+fn main() {
+    does_nothing()
+}
+// { dg-final { scan-tree-dump-times {fancy_crate_name::does_nothing} 2 gimple } }
+// { dg-final { scan-tree-dump-times {fancy_crate_name::main} 1 gimple } }
diff --git a/gcc/testsuite/rust/compile/static_var1.rs b/gcc/testsuite/rust/compile/static_var1.rs
new file mode 100644
index 00000000000..b3b5751c932
--- /dev/null
+++ b/gcc/testsuite/rust/compile/static_var1.rs
@@ -0,0 +1,5 @@
+static x = 3; // { dg-error "expecting ':' but '=' found" }
+
+fn main() {// { dg-error "failed to parse item in crate" }
+    let y = x +1;
+}
diff --git a/gcc/testsuite/rust/compile/stmt_with_block_err1.rs b/gcc/testsuite/rust/compile/stmt_with_block_err1.rs
new file mode 100644
index 00000000000..8780d0feeac
--- /dev/null
+++ b/gcc/testsuite/rust/compile/stmt_with_block_err1.rs
@@ -0,0 +1,17 @@
+fn test(x: i32) -> i32 {
+    if x > 1 { // { dg-error "expected .... got .<integer>." }
+        1
+    } else {
+        2
+    }
+
+    { // { dg-error "expected .... got .<integer>." }
+        3
+    }
+
+    3
+}
+
+fn main() {
+    let a = test(0);
+}
diff --git a/gcc/testsuite/rust/compile/struct_align1.rs b/gcc/testsuite/rust/compile/struct_align1.rs
new file mode 100644
index 00000000000..22eb6bc80fb
--- /dev/null
+++ b/gcc/testsuite/rust/compile/struct_align1.rs
@@ -0,0 +1,19 @@
+#[repr(align(8))]
+struct Foo {
+    x: i16,
+    // { dg-warning "field is never read" "" { target *-*-* } .-1 }
+    y: i8,
+    // { dg-warning "field is never read" "" { target *-*-* } .-1 }
+    z: i32,
+    // { dg-warning "field is never read" "" { target *-*-* } .-1 }
+}
+
+#[repr(align(8))]
+struct Bar(i8, i32);
+
+fn main () {
+    let f = Foo { x: 5, y: 2, z: 13 };
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let b = Bar (7, 262);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/struct_align2.rs b/gcc/testsuite/rust/compile/struct_align2.rs
new file mode 100644
index 00000000000..ac490643a36
--- /dev/null
+++ b/gcc/testsuite/rust/compile/struct_align2.rs
@@ -0,0 +1,18 @@
+
+fn main () {
+
+    #[repr(align(8))]
+    struct Baz {
+        x: u16,
+        y: u32,
+    };
+
+    #[repr(align(4))]
+    struct Qux (u8, i16);
+
+    let b = Baz { x: 5, y: 1984 };
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    let c = Qux (1, 2);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/struct_init1.rs b/gcc/testsuite/rust/compile/struct_init1.rs
new file mode 100644
index 00000000000..1875fb4f33e
--- /dev/null
+++ b/gcc/testsuite/rust/compile/struct_init1.rs
@@ -0,0 +1,10 @@
+struct Foo {
+    a: f32,
+    b: f32,
+}
+
+fn main() {
+    let a = Foo { 0: 10.0, 1: 20.0 }; // { dg-error "failed to resolve type for field" }
+    // { dg-error "unknown field" "" { target *-*-* } .-1 }
+    // { dg-prune-output "compilation terminated" }
+}
diff --git a/gcc/testsuite/rust/compile/struct_pack1.rs b/gcc/testsuite/rust/compile/struct_pack1.rs
new file mode 100644
index 00000000000..eb9d879c1dc
--- /dev/null
+++ b/gcc/testsuite/rust/compile/struct_pack1.rs
@@ -0,0 +1,19 @@
+#[repr(packed(2))]
+struct Foo {
+    x: i16,
+    // { dg-warning "field is never read" "" { target *-*-* } .-1 }
+    y: i8,
+    // { dg-warning "field is never read" "" { target *-*-* } .-1 }
+    z: i32,
+    // { dg-warning "field is never read" "" { target *-*-* } .-1 }
+}
+
+#[repr(packed)]
+struct Bar(i8, i32);
+
+fn main () {
+    let f = Foo { x: 5, y: 2, z: 13 };
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let b = Bar (7, 262);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/struct_pack2.rs b/gcc/testsuite/rust/compile/struct_pack2.rs
new file mode 100644
index 00000000000..e5f74c20bb0
--- /dev/null
+++ b/gcc/testsuite/rust/compile/struct_pack2.rs
@@ -0,0 +1,18 @@
+
+fn main () {
+
+    #[repr(packed(2))]
+    struct Baz {
+        x: u16,
+        y: u32,
+    };
+
+    #[repr(packed)]
+    struct Qux (u8, i16);
+
+    let b = Baz { x: 5, y: 1984 };
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    let c = Qux (1, 2);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/syntax-only.rs b/gcc/testsuite/rust/compile/syntax-only.rs
new file mode 100644
index 00000000000..cd84907bf84
--- /dev/null
+++ b/gcc/testsuite/rust/compile/syntax-only.rs
@@ -0,0 +1,6 @@
+// { dg-additional-options "-fsyntax-only" }
+
+fn main() {
+    let mut a = 15;
+    a = true;
+}
diff --git a/gcc/testsuite/rust/compile/test_mod.rs b/gcc/testsuite/rust/compile/test_mod.rs
new file mode 100644
index 00000000000..4b3c000236b
--- /dev/null
+++ b/gcc/testsuite/rust/compile/test_mod.rs
@@ -0,0 +1,6 @@
+//! test_mod inner doc comment
+//!
+//! foo bar baz cake pizza carbs
+
+pub struct Test(pub i32);
+// { dg-warning "struct is never constructed" "" { target *-*-* } .-1 }
diff --git a/gcc/testsuite/rust/compile/torture/all_doc_comment_line_blocks.rs b/gcc/testsuite/rust/compile/torture/all_doc_comment_line_blocks.rs
new file mode 100644
index 00000000000..b7368ba29ee
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/all_doc_comment_line_blocks.rs
@@ -0,0 +1,45 @@
+// comment line not a doc
+/* comment block not a doc                   */
+
+//! inner line comment for most outer crate
+/*! inner block comment for most outer crate */
+
+// comment line not a doc
+/* comment block not a doc                   */
+
+/// outer doc line for module
+/** outer doc block for module               */
+pub mod module {
+    //!  inner line doc
+    //!! inner line doc!
+    /*!  inner block doc  */
+    /*!! inner block doc! */
+
+    //   line comment
+    ///  outer line doc
+    //// line comment
+
+    /*   block comment   */
+    /**  outer block doc */
+    /*** block comment   */
+
+    mod block_doc_comments {
+        /*   /* */  /** */  /*! */  */
+        /*!  /* */  /** */  /*! */  */
+        /**  /* */  /** */  /*! */  */
+        mod item {}
+    }
+
+    pub mod empty {
+        //!
+        /*!*/
+        //
+
+        ///
+        // the following warning is issued one line earlier
+        mod doc {}
+        /**/
+        /***/
+    }
+}
+pub fn main() {}
diff --git a/gcc/testsuite/rust/compile/torture/all_doc_comment_line_blocks_crlf.rs b/gcc/testsuite/rust/compile/torture/all_doc_comment_line_blocks_crlf.rs
new file mode 100644
index 00000000000..9f2f2207397
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/all_doc_comment_line_blocks_crlf.rs
@@ -0,0 +1,48 @@
+// comment line not a doc
+/* comment block not a doc                   */
+
+//! inner line comment for most outer crate
+/*! inner block comment for most outer crate */
+
+// comment line not a doc
+/* comment block not a doc                   */
+
+/// outer doc line for module
+/** outer doc block for module               */
+pub mod module
+{
+  //!  inner line doc
+  //!! inner line doc!
+  /*!  inner block doc  */
+  /*!! inner block doc! */
+
+  //   line comment
+  ///  outer line doc
+  //// line comment
+
+  /*   block comment   */
+  /**  outer block doc */
+  /*** block comment   */
+
+  mod block_doc_comments
+  {
+    /*   /* */  /** */  /*! */  */
+    /*!  /* */  /** */  /*! */  */
+    /**  /* */  /** */  /*! */  */
+    mod item { }
+  }
+
+  pub mod empty
+  {
+    //!
+    /*!*/
+    //
+
+    ///
+    mod doc { }
+
+    /**/
+    /***/
+  }
+}
+pub fn main () { }
diff --git a/gcc/testsuite/rust/compile/torture/arithmetic_expressions1.rs b/gcc/testsuite/rust/compile/torture/arithmetic_expressions1.rs
new file mode 100644
index 00000000000..4c3ee77c835
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/arithmetic_expressions1.rs
@@ -0,0 +1,30 @@
+// { dg-prune-output "warning: unused name" } as there are many of these expected.
+
+fn main() {
+    let a: i32 = 1;
+    let b: f32 = 5f32;
+    let c: bool = true;
+
+    let a1: i32 = a + 1;
+    let a2: i32 = a - 2;
+    let a3: i32 = a * 3;
+    let a4: i32 = a / 4;
+    let a5: i32 = a % 5;
+
+    let b1: f32 = b + 1f32;
+    let b2: f32 = b - 2f32;
+    let b3: f32 = b * 3f32;
+    let b4: f32 = b / 4f32;
+    // let b5: f32 = b % 5f32;
+
+    let aa1: i32 = a & 1;
+    let aa2: i32 = a | 2;
+    let aa2: i32 = a ^ 3;
+
+    let c1: bool = c & true;
+    let c2: bool = c | false;
+    let c3: bool = c ^ true;
+
+    let aaa1: i32 = a << 1;
+    let aaa2: i32 = a >> 2;
+}
diff --git a/gcc/testsuite/rust/compile/torture/array_const_fold_1.rs b/gcc/testsuite/rust/compile/torture/array_const_fold_1.rs
new file mode 100644
index 00000000000..e45c9389c93
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/array_const_fold_1.rs
@@ -0,0 +1,2 @@
+const TEST: [i32; 16] = [2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8];
+// { dg-warning "unused name" "" { target *-*-* } .-1 }
\ No newline at end of file
diff --git a/gcc/testsuite/rust/compile/torture/array_const_fold_2.rs b/gcc/testsuite/rust/compile/torture/array_const_fold_2.rs
new file mode 100644
index 00000000000..b42a68e5ddb
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/array_const_fold_2.rs
@@ -0,0 +1,3 @@
+const SIZE: usize = 14 + 2;
+const TEST: [i32; SIZE] = [2; SIZE];
+// { dg-warning "unused name" "" { target *-*-* } .-1 }
diff --git a/gcc/testsuite/rust/compile/torture/array_function.rs b/gcc/testsuite/rust/compile/torture/array_function.rs
new file mode 100644
index 00000000000..4e2b2e03f31
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/array_function.rs
@@ -0,0 +1,8 @@
+fn foo() -> i32 {
+    1
+}
+
+
+fn main() {
+    let _a: [i32; 1] = [foo()];
+}
\ No newline at end of file
diff --git a/gcc/testsuite/rust/compile/torture/array_type_infer.rs b/gcc/testsuite/rust/compile/torture/array_type_infer.rs
new file mode 100644
index 00000000000..6f21bf2420c
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/array_type_infer.rs
@@ -0,0 +1,4 @@
+fn main() {
+    let arr: [_; 5] = [1, 2, 3, 4, 5];
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/array_zero_length.rs b/gcc/testsuite/rust/compile/torture/array_zero_length.rs
new file mode 100644
index 00000000000..3155b1c48c0
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/array_zero_length.rs
@@ -0,0 +1,4 @@
+fn main() {
+    let arr = ["Hello"; 0];
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/arrays1.rs b/gcc/testsuite/rust/compile/torture/arrays1.rs
new file mode 100644
index 00000000000..7250e0fa2af
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/arrays1.rs
@@ -0,0 +1,9 @@
+fn main() {
+    let xs: [i32; 5] = [1, 2, 3, 4, 5];
+    let xy = [6, 7, 8];
+
+    let a = xs[0];
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let b = xy[2];
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/arrays2.rs b/gcc/testsuite/rust/compile/torture/arrays2.rs
new file mode 100644
index 00000000000..55491f34524
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/arrays2.rs
@@ -0,0 +1,8 @@
+fn main() {
+    let mut array: [i32; 3] = [0; 3];
+
+    let a = array[0];
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let mut c;
+    c = array[2];
+}
diff --git a/gcc/testsuite/rust/compile/torture/arrays3.rs b/gcc/testsuite/rust/compile/torture/arrays3.rs
new file mode 100644
index 00000000000..372d969aa07
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/arrays3.rs
@@ -0,0 +1,6 @@
+const TEST: usize = 6;
+
+fn main() {
+    let a: [_; 12] = [123; TEST * 2];
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/arrays4.rs b/gcc/testsuite/rust/compile/torture/arrays4.rs
new file mode 100644
index 00000000000..ac317fedf44
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/arrays4.rs
@@ -0,0 +1,6 @@
+const TEST: usize = 4;
+
+fn main() {
+    let a: [_; TEST + 1 + 2] = [123; 7];
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/arrays5.rs b/gcc/testsuite/rust/compile/torture/arrays5.rs
new file mode 100644
index 00000000000..58950a17a15
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/arrays5.rs
@@ -0,0 +1,6 @@
+
+// Checks that we don't try to allocate a 4TB array during compilation
+fn main () {
+    let x = [0; 4 * 1024 * 1024 * 1024 * 1024];
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/arrays6.rs b/gcc/testsuite/rust/compile/torture/arrays6.rs
new file mode 100644
index 00000000000..c7212d3f183
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/arrays6.rs
@@ -0,0 +1,10 @@
+
+// Checks that we don't try to allocate a 4TB array during compilation
+fn foo() -> [u8; 4 * 1024 * 1024 * 1024 * 1024] {
+    [0; 4 * 1024 * 1024 * 1024 * 1024]
+}
+
+fn main () {
+    let x = foo ();
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/arrays_index1.rs b/gcc/testsuite/rust/compile/torture/arrays_index1.rs
new file mode 100644
index 00000000000..1fe5de91bcf
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/arrays_index1.rs
@@ -0,0 +1,9 @@
+fn main() {
+    let mut array: [i32; 3] = [0; 3];
+
+    let a = array[0];
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let x = 0;
+    let mut c;
+    c = array[x+1];
+}
diff --git a/gcc/testsuite/rust/compile/torture/arrays_index2.rs b/gcc/testsuite/rust/compile/torture/arrays_index2.rs
new file mode 100644
index 00000000000..f9bee7748ee
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/arrays_index2.rs
@@ -0,0 +1,4 @@
+fn main() {
+    let mut array: [i32; 3] = [0; 3];
+    array[0] = 1;
+}
diff --git a/gcc/testsuite/rust/compile/torture/arrays_index3.rs b/gcc/testsuite/rust/compile/torture/arrays_index3.rs
new file mode 100644
index 00000000000..8fa0a226d02
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/arrays_index3.rs
@@ -0,0 +1,15 @@
+fn foo() -> usize {
+    1
+}
+    
+fn bar() -> [i32; 1] {
+    [0]
+}
+    
+    
+        
+fn main() -> () {
+    let a = [10];
+    let _b = a[foo()];
+    let _c = bar()[foo()];
+}
diff --git a/gcc/testsuite/rust/compile/torture/as_bool_char.rs b/gcc/testsuite/rust/compile/torture/as_bool_char.rs
new file mode 100644
index 00000000000..d687499384a
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/as_bool_char.rs
@@ -0,0 +1,36 @@
+extern "C" { fn abort (); }
+
+pub fn main ()
+{
+  let t = true;
+  let f = false;
+  let one = t as u8;
+  let zero = f as u8;
+
+  if one != 1 || zero != 0 { unsafe { abort (); } }
+
+  let isizeone = true as isize;
+  let usizezero = false as usize;
+
+  if isizeone != 1 || usizezero != 0 { unsafe { abort (); } }
+
+  let i32zero = f as i32;
+  let u128one = t as u128;
+
+  if u128one != 1 || i32zero != 0 { unsafe { abort (); } }
+
+  let a = 'a';
+  let b = 'b';
+  let ua = a as u8;
+  let ib = b as i32;
+
+  if (ua + 1) as i32 != ib { unsafe { abort (); } }
+
+  let tt = ua;
+  let aa = tt as char;
+
+  let ttt = tt + 1;
+  let ab = ttt as char;
+
+  if aa != 'a' || ab != 'b' { unsafe { abort (); } }
+}
diff --git a/gcc/testsuite/rust/compile/torture/associated_types1.rs b/gcc/testsuite/rust/compile/torture/associated_types1.rs
new file mode 100644
index 00000000000..bf181df7045
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/associated_types1.rs
@@ -0,0 +1,12 @@
+pub trait Foo {
+    type A;
+
+    fn boo(&self) -> <Self as Foo>::A;
+}
+
+fn foo2<I: Foo>(x: I) {
+    // { dg-warning "function is never used: .foo2." "" { target *-*-* } .-1 }
+    x.boo();
+}
+
+pub fn main() {}
diff --git a/gcc/testsuite/rust/compile/torture/autoderef1.rs b/gcc/testsuite/rust/compile/torture/autoderef1.rs
new file mode 100644
index 00000000000..0cf070f1f37
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/autoderef1.rs
@@ -0,0 +1,15 @@
+struct Foo(i32, bool);
+struct Bar {
+    a: i32,
+    b: bool,
+}
+
+fn main() {
+    let a = &Foo(123, false);
+    let _b: i32 = a.0;
+    let _c: bool = a.1;
+
+    let a = &Bar { a: 456, b: false };
+    let _b: i32 = a.a;
+    let _c: bool = a.b;
+}
diff --git a/gcc/testsuite/rust/compile/torture/block_expr1.rs b/gcc/testsuite/rust/compile/torture/block_expr1.rs
new file mode 100644
index 00000000000..011cc1fc89d
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/block_expr1.rs
@@ -0,0 +1,29 @@
+fn test3(x: i32) -> i32 {
+    if x > 1 {
+        5
+    } else {
+        0
+    }
+}
+
+fn test5(x: i32) -> i32 {
+    if x > 1 {
+        if x == 5 {
+            7
+        } else {
+            9
+        }
+    } else {
+        0
+    }
+}
+
+fn main() {
+    let call3: i32 = { test3(3) + 2 };
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let call5 = {
+        // { dg-warning "unused name" "" { target *-*-* } .-1 }
+        let a = test5(5);
+        a + 1
+    };
+}
diff --git a/gcc/testsuite/rust/compile/torture/block_expr2.rs b/gcc/testsuite/rust/compile/torture/block_expr2.rs
new file mode 100644
index 00000000000..7c3ff698097
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/block_expr2.rs
@@ -0,0 +1,15 @@
+fn test() -> i32 {
+    123
+}
+
+fn main() {
+    let a = { test() };
+    let b = {
+        // { dg-warning "unused name" "" { target *-*-* } .-1 }
+        if a > 10 {
+            a - 1
+        } else {
+            a + 1
+        }
+    };
+}
diff --git a/gcc/testsuite/rust/compile/torture/block_expr3.rs b/gcc/testsuite/rust/compile/torture/block_expr3.rs
new file mode 100644
index 00000000000..6914b6379d7
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/block_expr3.rs
@@ -0,0 +1,14 @@
+fn main() {
+    let x = 111;
+
+    let a = {
+        // { dg-warning "unused name" "" { target *-*-* } .-1 }
+        if x == 10 {
+            123
+        } else if x < 10 {
+            456
+        } else {
+            789
+        }
+    };
+}
diff --git a/gcc/testsuite/rust/compile/torture/block_expr4.rs b/gcc/testsuite/rust/compile/torture/block_expr4.rs
new file mode 100644
index 00000000000..da033ef9ef4
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/block_expr4.rs
@@ -0,0 +1,8 @@
+fn foo() -> isize {
+    0
+}
+
+fn main() {
+    let a = foo();
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/block_expr5.rs b/gcc/testsuite/rust/compile/torture/block_expr5.rs
new file mode 100644
index 00000000000..7e164a949bb
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/block_expr5.rs
@@ -0,0 +1,40 @@
+fn foo() -> i32 {
+    0
+}
+
+fn bar() -> i32 {
+    foo();
+    foo()
+}
+
+fn baz() -> i32 {
+    {
+        bar();
+        bar();
+    }
+    {
+        bar();
+        bar()
+    };
+    {
+        bar();
+        bar()
+    }
+}
+
+fn test(ok: i32) -> i32 {
+    if ok >= 1 {
+        foo()
+    } else if ok <= -1 {
+        bar()
+    } else {
+        baz()
+    }
+}
+
+fn main() {
+    let a = foo();
+    let b = bar();
+    let c = baz();
+    test(a + b + c);
+}
diff --git a/gcc/testsuite/rust/compile/torture/block_expr_parser_bug.rs b/gcc/testsuite/rust/compile/torture/block_expr_parser_bug.rs
new file mode 100644
index 00000000000..468aace9881
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/block_expr_parser_bug.rs
@@ -0,0 +1,5 @@
+fn main() {
+    let a = 123;
+    let b = if a > 10 { a - 1 } else { a + 1 };
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/bom.rs b/gcc/testsuite/rust/compile/torture/bom.rs
new file mode 100644
index 00000000000..5edcab227ee
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/bom.rs
@@ -0,0 +1 @@
+pub fn main () { }
diff --git a/gcc/testsuite/rust/compile/torture/bom_comment.rs b/gcc/testsuite/rust/compile/torture/bom_comment.rs
new file mode 100644
index 00000000000..020e1707b55
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/bom_comment.rs
@@ -0,0 +1,2 @@
+// UTF8 BOM
+pub fn main () { }
diff --git a/gcc/testsuite/rust/compile/torture/bom_shebang.rs b/gcc/testsuite/rust/compile/torture/bom_shebang.rs
new file mode 100644
index 00000000000..4c552e8d71d
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/bom_shebang.rs
@@ -0,0 +1,2 @@
+#!/usr/bin/cat
+pub fn main () { }
diff --git a/gcc/testsuite/rust/compile/torture/bom_whitespace.rs b/gcc/testsuite/rust/compile/torture/bom_whitespace.rs
new file mode 100644
index 00000000000..b10d5654473
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/bom_whitespace.rs
@@ -0,0 +1,2 @@
+
+pub fn main () { }
diff --git a/gcc/testsuite/rust/compile/torture/bools_eq.rs b/gcc/testsuite/rust/compile/torture/bools_eq.rs
new file mode 100644
index 00000000000..965127b5d54
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/bools_eq.rs
@@ -0,0 +1,18 @@
+extern "C"
+{
+  fn abort ();
+}
+
+fn beq (a: bool, b: bool) -> bool
+{
+  let bools_eq = a == b;
+  bools_eq
+}
+
+pub fn main ()
+{
+  let a = true;
+  let b = false;
+  let r = beq (a, b);
+  if r { unsafe { abort (); } }
+}
diff --git a/gcc/testsuite/rust/compile/torture/borrow1.rs b/gcc/testsuite/rust/compile/torture/borrow1.rs
new file mode 100644
index 00000000000..8afa4746fef
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/borrow1.rs
@@ -0,0 +1,17 @@
+fn main() {
+    let a: i32;
+    a = 123;
+
+    let b: &i32;
+    b = &a;
+
+    let aa;
+    aa = 456;
+    let bb: &_;
+    bb = &a;
+
+    let aaa;
+    aaa = 123;
+    let bbb;
+    bbb = &aaa;
+}
diff --git a/gcc/testsuite/rust/compile/torture/borrow_function.rs b/gcc/testsuite/rust/compile/torture/borrow_function.rs
new file mode 100644
index 00000000000..98c6f99683e
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/borrow_function.rs
@@ -0,0 +1,5 @@
+fn foo() {}
+
+fn main() {
+    let _a = &foo;
+}
\ No newline at end of file
diff --git a/gcc/testsuite/rust/compile/torture/break_function.rs b/gcc/testsuite/rust/compile/torture/break_function.rs
new file mode 100644
index 00000000000..043e91c9502
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/break_function.rs
@@ -0,0 +1,10 @@
+fn foo() -> i32 {
+    1
+}
+    
+fn main() {
+    let _a = loop {
+        break foo();
+    };
+}
+    
\ No newline at end of file
diff --git a/gcc/testsuite/rust/compile/torture/byte_char_str.rs b/gcc/testsuite/rust/compile/torture/byte_char_str.rs
new file mode 100644
index 00000000000..bc3ec5014e8
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/byte_char_str.rs
@@ -0,0 +1,8 @@
+pub fn main ()
+{
+  let _c = 'x';
+  let _bc = b'x';
+
+  let _s = "abc";
+  let _bs = b"abc";
+}
diff --git a/gcc/testsuite/rust/compile/torture/byte_str.rs b/gcc/testsuite/rust/compile/torture/byte_str.rs
new file mode 100644
index 00000000000..28934d2581d
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/byte_str.rs
@@ -0,0 +1,4 @@
+pub fn main() {
+    let a: &[u8; 4];
+    a = b"test";
+}
diff --git a/gcc/testsuite/rust/compile/torture/cast1.rs b/gcc/testsuite/rust/compile/torture/cast1.rs
new file mode 100644
index 00000000000..845d08cea01
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/cast1.rs
@@ -0,0 +1,5 @@
+fn main() {
+    let a: *const i32 = &123;
+    let b: *mut i32 = (a as *mut i32);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/cast2.rs b/gcc/testsuite/rust/compile/torture/cast2.rs
new file mode 100644
index 00000000000..82925e93271
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/cast2.rs
@@ -0,0 +1,5 @@
+fn main() {
+    let a: i32 = 123i32;
+    let b: u8 = a as u8;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/cast3.rs b/gcc/testsuite/rust/compile/torture/cast3.rs
new file mode 100644
index 00000000000..1de95687039
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/cast3.rs
@@ -0,0 +1,6 @@
+fn main() {
+    let a = "foo\0";
+    let b = a as *const str;
+    let c = b as *const i8;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/cfg_attr.rs b/gcc/testsuite/rust/compile/torture/cfg_attr.rs
new file mode 100644
index 00000000000..d65faf2972a
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/cfg_attr.rs
@@ -0,0 +1,7 @@
+mod fake {} // Add one line so gccrs doesn't believe we're parsing a shebang
+
+#[cfg_attr(feature = "somefeature", attribute = "someattr")]
+struct Feature;
+// { dg-warning "struct is never constructed" "" { target *-*-* } .-1 }
+
+fn main() {}
diff --git a/gcc/testsuite/rust/compile/torture/char1.rs b/gcc/testsuite/rust/compile/torture/char1.rs
new file mode 100644
index 00000000000..73835c218a2
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/char1.rs
@@ -0,0 +1,4 @@
+fn main() {
+    let a;
+    a = 'c';
+}
diff --git a/gcc/testsuite/rust/compile/torture/check-doc-attr-string.rs b/gcc/testsuite/rust/compile/torture/check-doc-attr-string.rs
new file mode 100644
index 00000000000..e113120bdbc
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/check-doc-attr-string.rs
@@ -0,0 +1,18 @@
+#![crate_type = "lib"]
+
+#[doc(alias = "foo")] // ok!
+#[doc(alias("bar", "baz"))] // ok!
+pub struct Bar;
+
+#[doc(alias = "
+")] // { dg-error "invalid character used" "" { target *-*-* } .-1 }
+pub struct Foo;
+
+#[doc(alias(
+    "
+"
+))] // ko but unchecked for now
+pub struct Foo2;
+
+#[doc(whatever = "buidule")] // ko as well but unchecked for now
+struct Boo;
diff --git a/gcc/testsuite/rust/compile/torture/coercion1.rs b/gcc/testsuite/rust/compile/torture/coercion1.rs
new file mode 100644
index 00000000000..3bfa938ffdc
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/coercion1.rs
@@ -0,0 +1,11 @@
+pub fn main() {
+    let a: &i32 = &123;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let b: &mut i32 = &mut 123;
+
+    let c: &i32 = &mut 123;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    let d: &i32 = b;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/coercion2.rs b/gcc/testsuite/rust/compile/torture/coercion2.rs
new file mode 100644
index 00000000000..127f257f8b6
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/coercion2.rs
@@ -0,0 +1,20 @@
+pub fn main() {
+    let a: *const i32 = &123;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let b: &i32 = &123;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    let c: &mut i32 = &mut 123;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let d: *mut i32 = &mut 123;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    let e: &i32 = &mut 123;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let f: *const i32 = &mut 123;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    let g = &123;
+    let h: *const i32 = g;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/comparison_expr1.rs b/gcc/testsuite/rust/compile/torture/comparison_expr1.rs
new file mode 100644
index 00000000000..ed71ec89025
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/comparison_expr1.rs
@@ -0,0 +1,38 @@
+fn is_zero(x: i32) -> bool {
+    x == 0
+}
+
+fn is_not_zero(x: i32) -> bool {
+    x != 0
+}
+
+fn is_positive(x: i32) -> bool {
+    x > 0
+}
+
+fn is_negative(x: i32) -> bool {
+    x < 0
+}
+
+fn is_positive_or_zero(x: i32) -> bool {
+    x >= 0
+}
+
+fn is_negative_or_zero(x: i32) -> bool {
+    x <= 0
+}
+
+fn main() {
+    let a: bool = is_zero(1);
+    let b: bool = is_not_zero(2);
+    let c: bool = is_positive(3);
+    let d: bool = is_negative(4);
+    let e: bool = is_positive_or_zero(5);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let f: bool = is_negative_or_zero(6);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let g: bool = a || b;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let h: bool = c && d;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/compile.exp b/gcc/testsuite/rust/compile/torture/compile.exp
new file mode 100644
index 00000000000..48da264a47a
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/compile.exp
@@ -0,0 +1,33 @@
+# Copyright (C) 2021-2022 Free Software Foundation, Inc.
+
+# This program 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 of the License, or
+# (at your option) any later version.
+# 
+# This program 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/>.
+
+# Compile tests, torture testing.
+
+# Load support procs.
+load_lib rust-dg.exp
+
+# Initialize `dg'.
+dg-init
+
+# Main loop.
+set saved-dg-do-what-default ${dg-do-what-default}
+
+set dg-do-what-default "compile"
+gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.rs]] "" ""
+set dg-do-what-default ${saved-dg-do-what-default}
+
+# All done.
+dg-finish
diff --git a/gcc/testsuite/rust/compile/torture/compound_assignment_expr1.rs b/gcc/testsuite/rust/compile/torture/compound_assignment_expr1.rs
new file mode 100644
index 00000000000..1ff0d24cf8e
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/compound_assignment_expr1.rs
@@ -0,0 +1,23 @@
+fn main() {
+    let mut a = 1;
+    let mut b = 2;
+    let mut c = 3;
+    let mut d = 4;
+    let mut e = 5;
+    let mut f = 6;
+    let mut g = 7;
+    let mut h = 8;
+    let mut i = 9;
+    let mut j = 10;
+
+    a += 1;
+    b -= 2;
+    c *= 3;
+    d /= 4;
+    e %= 5;
+    f &= 6;
+    g |= 7;
+    h ^= 8;
+    i <<= 9;
+    j >>= 10;
+}
diff --git a/gcc/testsuite/rust/compile/torture/conditional.rs b/gcc/testsuite/rust/compile/torture/conditional.rs
new file mode 100644
index 00000000000..2bb3a95f033
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/conditional.rs
@@ -0,0 +1,11 @@
+fn main() {
+    let mut x = 5;
+
+    if x == 5 {
+        x = 1;
+    } else if x == 3 {
+        x = 2;
+    } else {
+        x = 3;
+    }
+}
diff --git a/gcc/testsuite/rust/compile/torture/constant1.rs b/gcc/testsuite/rust/compile/torture/constant1.rs
new file mode 100644
index 00000000000..57bcb0b4970
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/constant1.rs
@@ -0,0 +1,9 @@
+const TEST_CONST:i32 = 10;
+
+fn main() {
+    let mut x = TEST_CONST;
+    x = x + 1;
+
+    let mut y = x + TEST_CONST;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/constant2.rs b/gcc/testsuite/rust/compile/torture/constant2.rs
new file mode 100644
index 00000000000..d06324e8e65
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/constant2.rs
@@ -0,0 +1,6 @@
+fn main() {
+    const C: usize = 42;
+
+    let _a = C;
+    let _b: [i32; C] = [0; C];
+}
diff --git a/gcc/testsuite/rust/compile/torture/constant3.rs b/gcc/testsuite/rust/compile/torture/constant3.rs
new file mode 100644
index 00000000000..d2f1dd5b6db
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/constant3.rs
@@ -0,0 +1,10 @@
+fn main() {
+    const A: [i32; 3] = [1, 2, 3];
+    const B: i32 = A[1];
+    const C: usize = 42;
+    const D: i32 = 7;
+
+    let _a = C;
+    let _b: [i32; C] = [0; C];
+    let _c = B + D;
+}
diff --git a/gcc/testsuite/rust/compile/torture/deadcode1.rs b/gcc/testsuite/rust/compile/torture/deadcode1.rs
new file mode 100644
index 00000000000..1ba646f5c56
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/deadcode1.rs
@@ -0,0 +1,22 @@
+fn test1() -> i32 {
+    return 2;
+    // { dg-warning "unreachable expression" "" { target *-*-* } .+1 }
+    1
+}
+
+fn test2(x: i32) -> i32 {
+    if x > 1 {
+        return 5;
+    } else {
+        return 0;
+    }
+    // { dg-warning "unreachable statement" "" { target *-*-* } .+1 }
+    return 1;
+}
+
+fn main() {
+    let call1 = test1();
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let call2 = test2(2);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/deadcode2.rs b/gcc/testsuite/rust/compile/torture/deadcode2.rs
new file mode 100644
index 00000000000..ba7d5f015e9
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/deadcode2.rs
@@ -0,0 +1,10 @@
+fn foo() -> i32 {
+    return 1;
+
+    let a = -1; // { dg-warning "unreachable statement" }
+    a // { dg-warning "unreachable expression" }
+}
+
+fn main() {
+    foo();
+}
diff --git a/gcc/testsuite/rust/compile/torture/deref1.rs b/gcc/testsuite/rust/compile/torture/deref1.rs
new file mode 100644
index 00000000000..d715ce96c79
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/deref1.rs
@@ -0,0 +1,6 @@
+fn main() {
+    let a = 123;
+    let b = &a;
+    let c = *b;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/deref_function.rs b/gcc/testsuite/rust/compile/torture/deref_function.rs
new file mode 100644
index 00000000000..b1c5ff63423
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/deref_function.rs
@@ -0,0 +1,10 @@
+fn foo() {}
+
+
+fn main() {
+    let _c = *{
+	let _a = foo;
+	let b = &1;
+	b
+    };
+}
\ No newline at end of file
diff --git a/gcc/testsuite/rust/compile/torture/doc_comment.rs b/gcc/testsuite/rust/compile/torture/doc_comment.rs
new file mode 100644
index 00000000000..f99e41524ae
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/doc_comment.rs
@@ -0,0 +1,16 @@
+/// doc comment 1
+/// doc comment 2
+/// `blah blah` markdown
+pub struct TestStruct {}
+
+#[doc(hidden)]
+pub struct DocAttribute {}
+
+#[doc(a,b)]
+pub struct UnkAttribute {}
+
+fn main() {
+    let _ = TestStruct {};
+    let _ = DocAttribute {};
+    let _ = UnkAttribute {};
+}
diff --git a/gcc/testsuite/rust/compile/torture/enum1.rs b/gcc/testsuite/rust/compile/torture/enum1.rs
new file mode 100644
index 00000000000..7cea48f29e2
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/enum1.rs
@@ -0,0 +1,13 @@
+enum Foo {
+    A,
+    B,
+    C(char),
+    D { x: i64, y: i64 },
+}
+
+fn main() {
+    let _a = Foo::A;
+    let _b = Foo::B;
+    let _c = Foo::C('x');
+    let _d = Foo::D { x: 20, y: 80 };
+}
diff --git a/gcc/testsuite/rust/compile/torture/extern_mod1.rs b/gcc/testsuite/rust/compile/torture/extern_mod1.rs
new file mode 100644
index 00000000000..4b576e03d80
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/extern_mod1.rs
@@ -0,0 +1,6 @@
+// { dg-additional-options "-w" }
+mod modules;
+
+fn main() {
+    let twelve = modules::return_12();
+}
diff --git a/gcc/testsuite/rust/compile/torture/extern_mod2.rs b/gcc/testsuite/rust/compile/torture/extern_mod2.rs
new file mode 100644
index 00000000000..4984d5dc2c1
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/extern_mod2.rs
@@ -0,0 +1,23 @@
+// { dg-additional-options "-w" }
+
+#[path = "modules/valid_path.rs"]
+mod not_a_valid_path;
+
+#[path ="modules/valid_path.rs"]
+mod path_without_extra_equal;
+
+#[path= "modules/valid_path.rs"]
+mod no_leading_equal;
+
+#[path       =     "modules/valid_path.rs"]
+mod extra_spaces;
+
+#[path] // { dg-error "path attributes must contain a filename" }
+mod error; // { dg-error "no candidate found" }
+
+// This is "valid", and should only error out when parsing
+// the file
+#[path = "not_a_valid_file.rs"]
+mod another_error; // { dg-error "No such file or directory" }
+
+fn main() {}
diff --git a/gcc/testsuite/rust/compile/torture/float1.rs b/gcc/testsuite/rust/compile/torture/float1.rs
new file mode 100644
index 00000000000..fbe89382267
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/float1.rs
@@ -0,0 +1,9 @@
+fn test(x: f32) -> f32 {
+    return x + 1.0;
+}
+
+fn main() {
+    let a_float = 5.123;
+    let call_test = test(a_float + 1.0);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/float_types.rs b/gcc/testsuite/rust/compile/torture/float_types.rs
new file mode 100644
index 00000000000..7d3d298a1bb
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/float_types.rs
@@ -0,0 +1,13 @@
+// { dg-prune-output "warning: unused name" } as there are many of these expected.
+
+fn main() {
+    let a1: f32 = 1.0f32;
+    let a2: f64 = 2.0f64;
+    let a3: f32 = 3f32;
+    let a4: f64 = 4f64;
+
+    let b1 = 1.0f32;
+    let b2 = 2.0f64;
+    let b3 = 3f32;
+    let b4 = 4f64;
+}
diff --git a/gcc/testsuite/rust/compile/torture/forward_decl_1.rs b/gcc/testsuite/rust/compile/torture/forward_decl_1.rs
new file mode 100644
index 00000000000..b8403f9b97f
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/forward_decl_1.rs
@@ -0,0 +1,11 @@
+fn main() {
+    let mut an_integer = 5;
+    an_integer = test(1) + 3;
+
+    let call_test = test(1);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
+
+fn test(x: i32) -> i32 {
+    return x + 1;
+}
diff --git a/gcc/testsuite/rust/compile/torture/forward_decl_2.rs b/gcc/testsuite/rust/compile/torture/forward_decl_2.rs
new file mode 100644
index 00000000000..efc3b0dc565
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/forward_decl_2.rs
@@ -0,0 +1,6 @@
+fn main() {
+    let y = x + 1;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
+
+static x: i32 = 3;
diff --git a/gcc/testsuite/rust/compile/torture/forward_decl_3-unsafe.rs b/gcc/testsuite/rust/compile/torture/forward_decl_3-unsafe.rs
new file mode 100644
index 00000000000..04935864f02
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/forward_decl_3-unsafe.rs
@@ -0,0 +1,13 @@
+fn main() {
+    unsafe {
+        let struct_test = Foo { one: 1, two: 2 };
+        // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    };
+}
+
+struct Foo {
+    one: i32,
+    // { dg-warning "field is never read" "" { target *-*-* } .-1 }
+    two: i32,
+    // { dg-warning "field is never read" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/forward_decl_3.rs b/gcc/testsuite/rust/compile/torture/forward_decl_3.rs
new file mode 100644
index 00000000000..9256df5f728
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/forward_decl_3.rs
@@ -0,0 +1,11 @@
+fn main() {
+    let struct_test = Foo { one: 1, two: 2 };
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
+
+struct Foo {
+    one: i32,
+    // { dg-warning "field is never read" "" { target *-*-* } .-1 }
+    two: i32,
+    // { dg-warning "field is never read" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/forward_decl_4.rs b/gcc/testsuite/rust/compile/torture/forward_decl_4.rs
new file mode 100644
index 00000000000..e1fe51f9025
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/forward_decl_4.rs
@@ -0,0 +1,9 @@
+fn main() {
+    let mut x = TEST_CONST;
+    x = x + 1;
+
+    let mut y = x + TEST_CONST;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
+
+const TEST_CONST: i32 = 10;
diff --git a/gcc/testsuite/rust/compile/torture/forward_decl_5.rs b/gcc/testsuite/rust/compile/torture/forward_decl_5.rs
new file mode 100644
index 00000000000..73a47fe061b
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/forward_decl_5.rs
@@ -0,0 +1,19 @@
+pub fn main() {
+    let a;
+    a = foo { a: 123, b: 456f32 };
+
+    let mut a = 123;
+    a = bar(a);
+
+    let mut b = 456f32;
+    b = bar(b);
+
+    fn bar<T>(x: T) -> T {
+        x
+    }
+
+    struct foo {
+        a: i32,
+        b: f32,
+    };
+}
diff --git a/gcc/testsuite/rust/compile/torture/func1.rs b/gcc/testsuite/rust/compile/torture/func1.rs
new file mode 100644
index 00000000000..df1789e7bbe
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/func1.rs
@@ -0,0 +1,7 @@
+fn not_void() -> i32 {
+    8
+}
+
+fn main() {
+    not_void();
+}
diff --git a/gcc/testsuite/rust/compile/torture/func2.rs b/gcc/testsuite/rust/compile/torture/func2.rs
new file mode 100644
index 00000000000..f7dd556d955
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/func2.rs
@@ -0,0 +1,20 @@
+fn foo() {
+    8;
+    8;
+}
+
+fn bar() -> i32 {
+    8;
+    8
+}
+
+fn baz() -> i32 {
+    8;
+    return 8;
+}
+
+fn main() {
+    let a = foo(); // { dg-warning "unused name" }
+    let b = bar(); // { dg-warning "unused name" }
+    let c = baz(); // { dg-warning "unused name" }
+}
diff --git a/gcc/testsuite/rust/compile/torture/function_reference1.rs b/gcc/testsuite/rust/compile/torture/function_reference1.rs
new file mode 100644
index 00000000000..dfbd01bdbdc
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/function_reference1.rs
@@ -0,0 +1,9 @@
+fn test(a: i32) -> i32 {
+    a + 1
+}
+
+fn main() {
+    let a = test;
+    let b = a(1);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/function_reference2.rs b/gcc/testsuite/rust/compile/torture/function_reference2.rs
new file mode 100644
index 00000000000..3c3e7c10910
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/function_reference2.rs
@@ -0,0 +1,9 @@
+fn test(a: i32) -> i32 {
+    a + 1
+}
+
+fn main() {
+    let a: fn(i32) -> i32 = test;
+    let b = a(1);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/function_reference3.rs b/gcc/testsuite/rust/compile/torture/function_reference3.rs
new file mode 100644
index 00000000000..0cb3181f4a1
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/function_reference3.rs
@@ -0,0 +1,20 @@
+struct Foo {
+    a: fn(i32) -> i32,
+    b: i32,
+}
+
+fn test(a: i32) -> i32 {
+    a + 1
+}
+
+fn main() {
+    let a = test(1);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    let b: fn(i32) -> i32 = test;
+    let c = b(1);
+
+    let d = Foo { a: test, b: c };
+    let e = (d.a)(d.b);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/function_reference4.rs b/gcc/testsuite/rust/compile/torture/function_reference4.rs
new file mode 100644
index 00000000000..977e4c97215
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/function_reference4.rs
@@ -0,0 +1,9 @@
+fn test(a: i32) -> i32 {
+    a + 1
+}
+
+fn main() {
+    let a: fn(_) -> _ = test;
+    let b = a(1);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/generics1.rs b/gcc/testsuite/rust/compile/torture/generics1.rs
new file mode 100644
index 00000000000..87bcdc8f305
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/generics1.rs
@@ -0,0 +1,51 @@
+struct Foo {
+    a: f32,
+    b: bool,
+}
+
+struct GenericStruct<T> {
+    a: T,
+    b: usize,
+}
+
+fn main() {
+    let a1;
+    a1 = Foo { a: 1.0, b: false };
+
+    let b1: f32 = a1.a;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let c1: bool = a1.b;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    let a2: GenericStruct<i8>;
+    a2 = GenericStruct::<i8> { a: 1, b: 456 };
+
+    let b2: i8 = a2.a;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let c2: usize = a2.b;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    let a3;
+    a3 = GenericStruct::<i32> { a: 123, b: 456 };
+
+    let b3: i32 = a3.a;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let c3: usize = a3.b;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    let a4;
+    a4 = GenericStruct { a: 1.0, b: 456 };
+
+    let b4: f32 = a4.a;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let c4: usize = a4.b;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    let a5;
+    a5 = GenericStruct::<_> { a: true, b: 456 };
+
+    let b5: bool = a5.a;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let c5: usize = a5.b;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/generics10.rs b/gcc/testsuite/rust/compile/torture/generics10.rs
new file mode 100644
index 00000000000..8473d49587b
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/generics10.rs
@@ -0,0 +1,20 @@
+struct Foo<T>(T);
+
+struct Bar<T> {
+    a: Foo<T>,
+    b: bool,
+// { dg-warning "field is never read" "" { target *-*-* } .-1 }
+}
+
+fn test<T>(a: Bar<T>) -> Foo<T> {
+    a.a
+}
+
+fn main() {
+    let a: Bar<i32> = Bar::<i32> {
+        a: Foo::<i32>(123),
+        b: true,
+    };
+    let b: Foo<i32> = test(a);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/generics11.rs b/gcc/testsuite/rust/compile/torture/generics11.rs
new file mode 100644
index 00000000000..3c8f5ba0058
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/generics11.rs
@@ -0,0 +1,8 @@
+struct Foo<T>(T, u32);
+
+type TypeAlias = Foo<i32>;
+
+fn main() {
+    let a: Foo<i32>;
+    a = TypeAlias { 0: 123, 1: 456 };
+}
diff --git a/gcc/testsuite/rust/compile/torture/generics12.rs b/gcc/testsuite/rust/compile/torture/generics12.rs
new file mode 100644
index 00000000000..f31be584e09
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/generics12.rs
@@ -0,0 +1,17 @@
+struct GenericStruct<T>(T, usize);
+
+impl GenericStruct<i32> {
+    fn new(a: i32, b: usize) -> Self {
+        GenericStruct(a, b)
+    }
+
+    fn get(self) -> i32 {
+        self.0
+    }
+}
+
+fn main() {
+    let a: GenericStruct<i32> = GenericStruct::<i32>::new(123, 456);
+    let aa: i32 = a.get();
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/generics13.rs b/gcc/testsuite/rust/compile/torture/generics13.rs
new file mode 100644
index 00000000000..9eb598f02a8
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/generics13.rs
@@ -0,0 +1,41 @@
+struct Foo<A> {
+    a: A,
+    // { dg-warning "field is never read" "" { target *-*-* } .-1 }
+}
+
+struct GenericStruct<T> {
+    a: T,
+    b: usize,
+}
+
+impl Foo<isize> {
+    fn test() -> i32 {
+        123
+    }
+
+    fn bar(self) -> isize {
+        // { dg-warning "associated function is never used" "" { target *-*-* } .-1 }
+        self.a
+    }
+}
+
+fn main() {
+    let a: i32 = Foo::test();
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    let a2: GenericStruct<i8>;
+    a2 = GenericStruct::<i8> { a: 1, b: 456 };
+
+    let b2: i8 = a2.a;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let c2: usize = a2.b;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    let a4;
+    a4 = GenericStruct { a: 1.0, b: 456 };
+
+    let b4: f32 = a4.a;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let c4: usize = a4.b;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/generics14.rs b/gcc/testsuite/rust/compile/torture/generics14.rs
new file mode 100644
index 00000000000..e51a4079e30
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/generics14.rs
@@ -0,0 +1,20 @@
+struct Foo<A> {
+    a: A,
+    // { dg-warning "field is never read" "" { target *-*-* } .-1 }
+}
+
+impl Foo<isize> {
+    fn test() -> i32 {
+        123
+    }
+
+    fn bar(self) -> isize {
+        // { dg-warning "associated function is never used" "" { target *-*-* } .-1 }
+        self.a
+    }
+}
+
+fn main() {
+    let a: i32 = Foo::test();
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/generics15.rs b/gcc/testsuite/rust/compile/torture/generics15.rs
new file mode 100644
index 00000000000..c16a67c4dd5
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/generics15.rs
@@ -0,0 +1,23 @@
+struct Foo<T>(T, bool);
+
+impl Foo<i32> {
+    fn bar(self) -> i32 {
+        self.0
+    }
+}
+
+impl Foo<f32> {
+    fn bar(self) -> f32 {
+        self.0
+    }
+}
+
+fn main() {
+    let a = Foo(123, true);
+    let aa = a.bar();
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    let b = Foo(456f32, true);
+    let bb = b.bar();
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/generics16.rs b/gcc/testsuite/rust/compile/torture/generics16.rs
new file mode 100644
index 00000000000..15b9d7b55e7
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/generics16.rs
@@ -0,0 +1,31 @@
+struct Foo<T>(T, bool);
+
+impl Foo<i32> {
+    fn new() -> Self {
+        Foo(123, true)
+    }
+
+    fn bar(self) -> i32 {
+        self.0
+    }
+}
+
+impl Foo<f32> {
+    fn new() -> Self {
+        Foo(123f32, true)
+    }
+
+    fn bar(self) -> f32 {
+        self.0
+    }
+}
+
+fn main() {
+    let a = Foo::<i32>::new();
+    let aa: i32 = a.bar();
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    let b = Foo::<f32>::new();
+    let bb: f32 = b.bar();
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/generics17.rs b/gcc/testsuite/rust/compile/torture/generics17.rs
new file mode 100644
index 00000000000..d52314999b9
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/generics17.rs
@@ -0,0 +1,19 @@
+struct Foo<T>(T);
+
+impl<X> Foo<X> {
+    fn new(a: X) -> Self {
+        Self(a)
+    }
+
+    fn test(self) -> X {
+        self.0
+    }
+}
+
+fn main() {
+    let a;
+    a = Foo::new(123);
+
+    let b = a.test();
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/generics18.rs b/gcc/testsuite/rust/compile/torture/generics18.rs
new file mode 100644
index 00000000000..4c98b86a1b9
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/generics18.rs
@@ -0,0 +1,20 @@
+struct Foo<T>(T);
+
+impl<X> Foo<X> {
+    fn new(a: X) -> Self {
+        // { dg-warning "associated function is never used" "" { target *-*-* } .-1 }
+        Self(a)
+    }
+
+    fn test(self) -> X {
+        self.0
+    }
+}
+
+fn main() {
+    let a;
+    a = Foo(123);
+
+    let b = a.test();
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/generics19.rs b/gcc/testsuite/rust/compile/torture/generics19.rs
new file mode 100644
index 00000000000..9a5b4cb48dc
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/generics19.rs
@@ -0,0 +1,12 @@
+struct Foo<X, Y>(X, Y);
+
+impl<T> Foo<u32, T> {
+    fn new(a: T) -> Self {
+        Self(123, a)
+    }
+}
+
+fn main() {
+    let a;
+    a = Foo::new(false);
+}
diff --git a/gcc/testsuite/rust/compile/torture/generics2.rs b/gcc/testsuite/rust/compile/torture/generics2.rs
new file mode 100644
index 00000000000..da0ab992243
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/generics2.rs
@@ -0,0 +1,45 @@
+struct Foo(f32, bool);
+
+struct GenericStruct<T>(T, usize);
+
+fn main() {
+    let a1;
+    a1 = Foo(1.0, false);
+
+    let b1: f32 = a1.0;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let c1: bool = a1.1;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    let a2: GenericStruct<i8>;
+    a2 = GenericStruct::<i8>(1, 456);
+
+    let b2: i8 = a2.0;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let c2: usize = a2.1;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    let a3;
+    a3 = GenericStruct::<i32>(123, 456);
+
+    let b3: i32 = a3.0;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let c3: usize = a3.1;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    let a4;
+    a4 = GenericStruct(1.0, 456);
+
+    let b4: f32 = a4.0;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let c4: usize = a4.1;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    let a5;
+    a5 = GenericStruct::<_>(true, 456);
+
+    let b5: bool = a5.0;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let c5: usize = a5.1;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/generics20.rs b/gcc/testsuite/rust/compile/torture/generics20.rs
new file mode 100644
index 00000000000..8fe1cffdf7d
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/generics20.rs
@@ -0,0 +1,12 @@
+struct Foo<A, B>(A, B);
+
+impl<T> Foo<T, T> {
+    fn new(a: T, b: T) -> Self {
+        Self(a, b)
+    }
+}
+
+fn main() {
+    let a;
+    a = Foo::new(123, 456);
+}
diff --git a/gcc/testsuite/rust/compile/torture/generics21.rs b/gcc/testsuite/rust/compile/torture/generics21.rs
new file mode 100644
index 00000000000..dc4e935cac7
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/generics21.rs
@@ -0,0 +1,13 @@
+fn callee<T>(t: &T) -> i32 {
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    32
+}
+
+fn caller(t: i32) -> i32 {
+    callee(&t)
+}
+
+fn main() {
+    let a;
+    a = caller(123);
+}
diff --git a/gcc/testsuite/rust/compile/torture/generics22.rs b/gcc/testsuite/rust/compile/torture/generics22.rs
new file mode 100644
index 00000000000..465ebb0f5e1
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/generics22.rs
@@ -0,0 +1,13 @@
+fn callee<T>(t: (T, bool)) -> i32 {
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    32
+}
+
+fn caller(t: i32) -> i32 {
+    callee((t, false))
+}
+
+fn main() {
+    let a;
+    a = caller(123);
+}
diff --git a/gcc/testsuite/rust/compile/torture/generics23.rs b/gcc/testsuite/rust/compile/torture/generics23.rs
new file mode 100644
index 00000000000..2169e3649c6
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/generics23.rs
@@ -0,0 +1,6 @@
+struct Foo<A = f32>(A);
+
+fn main() {
+    let a: Foo;
+    a = Foo(123f32);
+}
diff --git a/gcc/testsuite/rust/compile/torture/generics24.rs b/gcc/testsuite/rust/compile/torture/generics24.rs
new file mode 100644
index 00000000000..0de45a8c404
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/generics24.rs
@@ -0,0 +1,34 @@
+struct Foo<A = (isize, char)> {
+    a: A,
+}
+
+impl Foo<isize> {
+    fn bar(self) -> isize {
+        self.a
+    }
+}
+
+impl Foo<char> {
+    fn bar(self) -> char {
+        // { dg-warning "associated function is never used" "" { target *-*-* } .-1 }
+        self.a
+    }
+}
+
+impl Foo {
+    fn bar(self) {
+        let a: (isize, char) = self.a;
+        let b = a.0;
+        let c = a.1;
+        // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+        let aa: Foo<isize> = Foo { a: b };
+        let bb: isize = aa.bar();
+        // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    }
+}
+
+fn main() {
+    let a = Foo { a: (123, 'a') };
+    a.bar();
+}
diff --git a/gcc/testsuite/rust/compile/torture/generics25.rs b/gcc/testsuite/rust/compile/torture/generics25.rs
new file mode 100644
index 00000000000..e7792e3efb3
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/generics25.rs
@@ -0,0 +1,9 @@
+struct Foo<A, B = (A, A)>(A, B);
+
+fn main() {
+    let a: Foo<bool>;
+    a = Foo::<bool>(true, (false, true));
+
+    let b: (bool, bool);
+    b = a.1;
+}
diff --git a/gcc/testsuite/rust/compile/torture/generics26.rs b/gcc/testsuite/rust/compile/torture/generics26.rs
new file mode 100644
index 00000000000..522e16f32f7
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/generics26.rs
@@ -0,0 +1,21 @@
+// github issue #415
+fn test<A, B>(a: A, b: B) -> (A, B) {
+    (a, b)
+}
+
+fn main() {
+    let a = test::<i32, i32>(123, 456);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    let b = test::<f32, f32>(123f32, 456f32);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    let c = test::<_, _>(123, 456f32);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    let d = test(true, 1234);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    let e = test((123, false), 123f32);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/generics27.rs b/gcc/testsuite/rust/compile/torture/generics27.rs
new file mode 100644
index 00000000000..9871638dd9f
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/generics27.rs
@@ -0,0 +1,16 @@
+// github issue #415
+fn test<A>(a: &A) -> &A {
+    a
+}
+
+fn main() {
+    let a = 123;
+    let b = &a;
+    let c = test(b);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    let a = 123f32;
+    let b = &a;
+    let c = test(b);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/generics28.rs b/gcc/testsuite/rust/compile/torture/generics28.rs
new file mode 100644
index 00000000000..8cee8b00fb2
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/generics28.rs
@@ -0,0 +1,18 @@
+struct Foo<A, B>(A, B);
+
+impl Foo<i32, f32> {
+    fn test<X>(a: X) -> X {
+        a
+    }
+}
+
+fn main() {
+    let a;
+    a = Foo::test::<_>(123);
+
+    let b;
+    b = Foo::test::<bool>(true);
+
+    let c;
+    c = Foo::test(456f32);
+}
diff --git a/gcc/testsuite/rust/compile/torture/generics29.rs b/gcc/testsuite/rust/compile/torture/generics29.rs
new file mode 100644
index 00000000000..e09a1044574
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/generics29.rs
@@ -0,0 +1,16 @@
+struct Foo<A, B>(A, B);
+
+impl Foo<i32, f32> {
+    fn test<X>(self, a: X) -> X {
+        // { dg-warning "unused name" "" { target *-*-* } .-1 }
+        a
+    }
+}
+
+fn main() {
+    let a;
+    a = Foo(123, 456f32);
+
+    let b;
+    b = a.test::<bool>(false);
+}
diff --git a/gcc/testsuite/rust/compile/torture/generics3.rs b/gcc/testsuite/rust/compile/torture/generics3.rs
new file mode 100644
index 00000000000..ceec8f7d9c6
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/generics3.rs
@@ -0,0 +1,15 @@
+fn test<T>(a: T) -> T {
+    a
+}
+
+fn main() {
+    let a;
+    a = test(123);
+    let aa: i32 = a;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    let b;
+    b = test::<u32>(456);
+    let bb: u32 = b;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/generics30.rs b/gcc/testsuite/rust/compile/torture/generics30.rs
new file mode 100644
index 00000000000..229f6d1254b
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/generics30.rs
@@ -0,0 +1,16 @@
+struct Foo<A, B>(A, B);
+
+impl<T> Foo<T, f32> {
+    fn test<X>(self, a: X) -> X {
+        // { dg-warning "unused name" "" { target *-*-* } .-1 }
+        a
+    }
+}
+
+fn main() {
+    let a;
+    a = Foo(123, 456f32);
+
+    let b;
+    b = a.test::<bool>(false);
+}
diff --git a/gcc/testsuite/rust/compile/torture/generics31.rs b/gcc/testsuite/rust/compile/torture/generics31.rs
new file mode 100644
index 00000000000..68ad4bf9a96
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/generics31.rs
@@ -0,0 +1,15 @@
+struct Foo<A, B>(A, B);
+
+impl<T> Foo<T, f32> {
+    fn test<X>(self, a: X) -> (T, X) {
+        (self.0, a)
+    }
+}
+
+fn main() {
+    let a;
+    a = Foo(123, 456f32);
+
+    let b;
+    b = a.test::<bool>(false);
+}
diff --git a/gcc/testsuite/rust/compile/torture/generics32.rs b/gcc/testsuite/rust/compile/torture/generics32.rs
new file mode 100644
index 00000000000..21b9cae7409
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/generics32.rs
@@ -0,0 +1,15 @@
+struct Foo<A, B>(A, B);
+
+impl<T> Foo<T, f32> {
+    fn test<X>(self, a: X) -> (T, X) {
+        (self.0, a)
+    }
+}
+
+fn main() {
+    let a;
+    a = Foo(123, 456f32);
+
+    let b;
+    b = a.test(false);
+}
diff --git a/gcc/testsuite/rust/compile/torture/generics4.rs b/gcc/testsuite/rust/compile/torture/generics4.rs
new file mode 100644
index 00000000000..915cc49c68b
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/generics4.rs
@@ -0,0 +1,17 @@
+struct Foo<T> {
+    a: T,
+// { dg-warning "field is never read" "" { target *-*-* } .-1 }
+    b: bool,
+// { dg-warning "field is never read" "" { target *-*-* } .-1 }
+}
+
+fn test<T>(a: T) -> Foo<T> {
+    Foo { a: a, b: true }
+}
+
+fn main() {
+    let a: Foo<i32> = test(123);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let b: Foo<u32> = test(456);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/generics5.rs b/gcc/testsuite/rust/compile/torture/generics5.rs
new file mode 100644
index 00000000000..b7f43028992
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/generics5.rs
@@ -0,0 +1,10 @@
+fn test<T>(a: T) -> T {
+    a
+}
+
+fn main() {
+    let a: i32 = test(123);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let b: i32 = test(456);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/generics6.rs b/gcc/testsuite/rust/compile/torture/generics6.rs
new file mode 100644
index 00000000000..5456b6dcb97
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/generics6.rs
@@ -0,0 +1,16 @@
+struct Foo<T>(T);
+
+struct Bar<T> {
+    a: Foo<T>,
+    b: bool,
+// { dg-warning "field is never read" "" { target *-*-* } .-1 }
+}
+
+fn main() {
+    let a: Bar<i32> = Bar::<i32> {
+        a: Foo::<i32>(123),
+        b: true,
+    };
+    let b: i32 = a.a.0;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/generics7.rs b/gcc/testsuite/rust/compile/torture/generics7.rs
new file mode 100644
index 00000000000..e8e5ca69c3d
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/generics7.rs
@@ -0,0 +1,14 @@
+struct Foo<T>(T);
+
+struct Bar {
+    a: Foo<i32>,
+    b: bool,
+// { dg-warning "field is never read" "" { target *-*-* } .-1 }
+}
+
+fn main() {
+    let a = Foo::<i32>(123);
+    let b: Bar = Bar { a: a, b: true };
+    let c: i32 = b.a.0;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/generics8.rs b/gcc/testsuite/rust/compile/torture/generics8.rs
new file mode 100644
index 00000000000..036d85568f0
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/generics8.rs
@@ -0,0 +1,18 @@
+struct GenericStruct<T>(T, usize);
+
+impl<T> GenericStruct<T> {
+    fn new(a: T, b: usize) -> Self {
+        GenericStruct(a, b)
+    }
+}
+
+fn main() {
+    let a: GenericStruct<i32> = GenericStruct::<i32>::new(123, 456);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    let b: GenericStruct<u32> = GenericStruct::<_>::new(123, 456);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    let c: GenericStruct<f32> = GenericStruct::new(123f32, 456);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/generics9.rs b/gcc/testsuite/rust/compile/torture/generics9.rs
new file mode 100644
index 00000000000..307c34f3e9b
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/generics9.rs
@@ -0,0 +1,25 @@
+struct GenericStruct<T>(T, usize);
+
+impl<T> GenericStruct<T> {
+    fn new(a: T, b: usize) -> Self {
+        GenericStruct(a, b)
+    }
+
+    fn get(self) -> T {
+        self.0
+    }
+}
+
+fn main() {
+    let a: GenericStruct<i32> = GenericStruct::<i32>::new(123, 456);
+    let aa: i32 = a.get();
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    let b: GenericStruct<u32> = GenericStruct::<_>::new(123, 456);
+    let bb: u32 = b.get();
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    let c: GenericStruct<f32> = GenericStruct::new(123f32, 456);
+    let cc: f32 = c.get();
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/grouped_expr_function.rs b/gcc/testsuite/rust/compile/torture/grouped_expr_function.rs
new file mode 100644
index 00000000000..eca7178b7f7
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/grouped_expr_function.rs
@@ -0,0 +1,6 @@
+fn foo() {}
+
+
+fn main() {
+    let _a = (foo());
+}
\ No newline at end of file
diff --git a/gcc/testsuite/rust/compile/torture/identifier-missing-impl-1.rs b/gcc/testsuite/rust/compile/torture/identifier-missing-impl-1.rs
new file mode 100644
index 00000000000..2389fa52bfd
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/identifier-missing-impl-1.rs
@@ -0,0 +1,19 @@
+struct I();
+
+impl I {
+    fn () {
+        // { dg-error {expecting 'identifier' but '\(' found} "" { target *-*-* } .-1 }
+        // { dg-error {failed to parse inherent impl item in inherent impl} "" { target *-*-* } .-2 }
+        // { dg-error {failed to parse item in crate} "" { target *-*-* } .-3 }
+    }
+}
+
+impl I {
+    unsafe fn () {
+        // { dg-error {expecting 'identifier' but '\(' found} "" { xfail *-*-* } .-1 }
+    }
+}
+
+fn main() {
+    let _i = I();
+}
diff --git a/gcc/testsuite/rust/compile/torture/if.rs b/gcc/testsuite/rust/compile/torture/if.rs
new file mode 100644
index 00000000000..bcd520f66a9
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/if.rs
@@ -0,0 +1,19 @@
+fn foo() -> bool {
+    true
+}
+
+fn bar() {}
+
+struct Foo1 {
+    one: i32
+}
+
+
+fn main() {
+    if foo() {
+        bar();
+        let a = Foo1{one: 1};
+        a.one
+    }
+
+}
\ No newline at end of file
diff --git a/gcc/testsuite/rust/compile/torture/if_elif.rs b/gcc/testsuite/rust/compile/torture/if_elif.rs
new file mode 100644
index 00000000000..a89ad5eb02f
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/if_elif.rs
@@ -0,0 +1,20 @@
+fn foo() -> bool {
+    true
+}
+
+fn bar() -> bool {
+    false
+}
+
+struct Foo1 {
+    one: i32
+}
+
+
+fn main() {
+    if foo() {
+    } else if bar() {
+        let a = Foo1{one: 1};
+        a.one;
+    }
+}
\ No newline at end of file
diff --git a/gcc/testsuite/rust/compile/torture/if_elif_else_expr1.rs b/gcc/testsuite/rust/compile/torture/if_elif_else_expr1.rs
new file mode 100644
index 00000000000..65ed7f7a23a
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/if_elif_else_expr1.rs
@@ -0,0 +1,14 @@
+fn test(x: i32) -> i32 {
+    if x == 10 {
+        123
+    } else if x < 10 {
+        456
+    } else {
+        789
+    }
+}
+
+fn main() {
+    let a = test(1);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/if_else.rs b/gcc/testsuite/rust/compile/torture/if_else.rs
new file mode 100644
index 00000000000..09aecaed4d6
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/if_else.rs
@@ -0,0 +1,19 @@
+fn foo() -> bool {
+    true
+}
+
+fn bar() {}
+
+struct Foo1 {
+    one: i32
+}
+
+
+fn main() {
+    if foo() {
+        bar();
+    } else {
+        let a = Foo1{one: 1};
+        a.one;
+    }
+}
\ No newline at end of file
diff --git a/gcc/testsuite/rust/compile/torture/ifunaryexpr.rs b/gcc/testsuite/rust/compile/torture/ifunaryexpr.rs
new file mode 100644
index 00000000000..8f0bb87f558
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/ifunaryexpr.rs
@@ -0,0 +1,22 @@
+extern "C"
+{
+  pub fn abort ();
+}
+
+struct B { b: bool }
+
+pub fn main ()
+{
+  let n = 1;
+  if 0 > -n { } else { unsafe { abort (); } }
+
+  let b = true;
+  if !b { unsafe { abort (); } }
+  if !!b { } else { unsafe { abort (); } }
+
+  let bb = B { b: false };
+
+  if !bb.b && !b { unsafe { abort (); } }
+
+  if (B { b: true }).b { } else { unsafe { abort (); } }
+}
diff --git a/gcc/testsuite/rust/compile/torture/impl_block1.rs b/gcc/testsuite/rust/compile/torture/impl_block1.rs
new file mode 100644
index 00000000000..d67afa187b1
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/impl_block1.rs
@@ -0,0 +1,23 @@
+struct Foo(i32, bool);
+
+impl Foo {
+    fn new(a: i32, b: bool) -> Foo {
+        Foo(a, b)
+    }
+
+    fn test2() -> i32 {
+        test_forward_decl()
+    }
+}
+
+fn test_forward_decl() -> i32 {
+    123
+}
+
+fn main() {
+    let a;
+    a = Foo::new(1, true);
+
+    let b;
+    b = Foo::test2();
+}
diff --git a/gcc/testsuite/rust/compile/torture/impl_block2.rs b/gcc/testsuite/rust/compile/torture/impl_block2.rs
new file mode 100644
index 00000000000..0ed592d07be
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/impl_block2.rs
@@ -0,0 +1,28 @@
+struct Foo(i32, bool);
+
+impl Foo {
+    const number: i32 = 456;
+
+    fn new(a: i32, b: bool) -> Foo {
+        Foo(a, b)
+    }
+
+    fn test2() -> i32 {
+        test_forward_decl()
+    }
+}
+
+fn test_forward_decl() -> i32 {
+    123
+}
+
+fn main() {
+    let a;
+    a = Foo::new(1, true);
+
+    let b;
+    b = Foo::test2();
+
+    let c;
+    c = Foo::new(Foo::number, true);
+}
diff --git a/gcc/testsuite/rust/compile/torture/impl_block3.rs b/gcc/testsuite/rust/compile/torture/impl_block3.rs
new file mode 100644
index 00000000000..22ce19f704d
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/impl_block3.rs
@@ -0,0 +1,36 @@
+struct Point {
+    x: f64,
+// { dg-warning "field is never read" "" { target *-*-* } .-1 }
+    y: f64,
+// { dg-warning "field is never read" "" { target *-*-* } .-1 }
+}
+
+impl Point {
+    fn origin() -> Point {
+        Point { x: 0.0, y: 0.0 }
+    }
+
+    fn new(x: f64, y: f64) -> Point {
+        Point { x: x, y: y }
+    }
+}
+
+struct Rectangle {
+    p1: Point,
+// { dg-warning "field is never read" "" { target *-*-* } .-1 }
+    p2: Point,
+// { dg-warning "field is never read" "" { target *-*-* } .-1 }
+}
+
+impl Rectangle {
+    fn from(p1: Point, p2: Point) -> Self {
+        Self { p1, p2 }
+    }
+}
+
+fn main() {
+    let p1 = Point::origin();
+    let p2 = Point::new(3.0, 4.0);
+    let rect = Rectangle::from(p1, p2);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/impl_block_unused.rs b/gcc/testsuite/rust/compile/torture/impl_block_unused.rs
new file mode 100644
index 00000000000..fea86319243
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/impl_block_unused.rs
@@ -0,0 +1,17 @@
+struct Foo(i32, bool);
+
+impl Foo {
+    fn new(a: i32, b: bool) -> Foo {
+        // { dg-warning "associated function is never used" "" { target *-*-* } .-1 }
+        Foo(a, b)
+    }
+
+    fn test2() -> i32 {
+        // { dg-warning "associated function is never used" "" { target *-*-* } .-1 }
+        1
+    }
+}
+
+fn main() {
+    let _a = Foo(1, true);
+}
diff --git a/gcc/testsuite/rust/compile/torture/implicit_returns1.rs b/gcc/testsuite/rust/compile/torture/implicit_returns1.rs
new file mode 100644
index 00000000000..54cc8b3aea8
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/implicit_returns1.rs
@@ -0,0 +1,73 @@
+fn test1() -> i32 {
+    1
+}
+
+fn test2() -> i32 {
+    return 2;
+}
+
+fn test3(x: i32) -> i32 {
+    if x > 1 {
+        5
+    } else {
+        0
+    }
+}
+
+fn test4(x: i32) -> i32 {
+    if x > 1 {
+        return 1;
+    }
+    0
+}
+
+fn test5(x: i32) -> i32 {
+    if x > 1 {
+        if x == 5 {
+            7
+        } else {
+            9
+        }
+    } else {
+        0
+    }
+}
+
+fn test6(x: i32) -> i32 {
+    if x > 1 {
+        return 5;
+    } else {
+        return 0;
+    }
+}
+
+fn test7(x: i32) -> i32 {
+    if x > 1 {
+        return 5;
+    } else {
+        return 0;
+    }
+}
+
+fn test8() -> i32 {
+    return 1;
+}
+
+fn main() {
+    let call1 = test1();
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let call2 = test2();
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let call3 = test3(3);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let call4 = test4(4);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let call5 = test5(5);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let call6 = test6(6);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let call7 = test7(7);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let call8 = test8();
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/infer_type1.rs b/gcc/testsuite/rust/compile/torture/infer_type1.rs
new file mode 100644
index 00000000000..aabfcef895b
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/infer_type1.rs
@@ -0,0 +1,4 @@
+fn main() {
+    let array: [_; 2] = [111, 222];
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/inner_attributes.rs b/gcc/testsuite/rust/compile/torture/inner_attributes.rs
new file mode 100644
index 00000000000..3410dd6ec87
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/inner_attributes.rs
@@ -0,0 +1,3 @@
+#![allow(dead_code)]
+#![allow(unused_variables)]
+pub fn main () { }
diff --git a/gcc/testsuite/rust/compile/torture/integer_inference_var1.rs b/gcc/testsuite/rust/compile/torture/integer_inference_var1.rs
new file mode 100644
index 00000000000..ccee06aad10
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/integer_inference_var1.rs
@@ -0,0 +1,6 @@
+const TEST_CONST: i32 = 10;
+
+fn main() {
+    let a;
+    a = TEST_CONST;
+}
diff --git a/gcc/testsuite/rust/compile/torture/integer_inference_var2.rs b/gcc/testsuite/rust/compile/torture/integer_inference_var2.rs
new file mode 100644
index 00000000000..2209e937479
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/integer_inference_var2.rs
@@ -0,0 +1,6 @@
+fn main() {
+    let a = 1u32;
+
+    let b;
+    b = a;
+}
diff --git a/gcc/testsuite/rust/compile/torture/integer_inference_var3.rs b/gcc/testsuite/rust/compile/torture/integer_inference_var3.rs
new file mode 100644
index 00000000000..582ae77caa4
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/integer_inference_var3.rs
@@ -0,0 +1,11 @@
+fn test(a: u32) -> u32 {
+    a + 1
+}
+
+fn main() {
+    let param;
+    param = 123;
+
+    let a = test(param);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/integer_inference_var4.rs b/gcc/testsuite/rust/compile/torture/integer_inference_var4.rs
new file mode 100644
index 00000000000..136d8183d08
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/integer_inference_var4.rs
@@ -0,0 +1,4 @@
+fn main() {
+    let a;
+    a = 1;
+}
diff --git a/gcc/testsuite/rust/compile/torture/integer_inference_var5.rs b/gcc/testsuite/rust/compile/torture/integer_inference_var5.rs
new file mode 100644
index 00000000000..051de1d6520
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/integer_inference_var5.rs
@@ -0,0 +1,25 @@
+const TEST_CONST: i32 = 10;
+
+fn test(x: u32) -> u32 {
+    x + 1
+}
+
+fn main() {
+    let x = TEST_CONST;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    let a = 1u32;
+    let b = a;
+
+    let c;
+    c = 1;
+
+    let d;
+    d = b;
+
+    let param;
+    param = 123;
+
+    let test_call = test(param);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/integer_types.rs b/gcc/testsuite/rust/compile/torture/integer_types.rs
new file mode 100644
index 00000000000..95a73780bb1
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/integer_types.rs
@@ -0,0 +1,27 @@
+// { dg-prune-output "warning: unused name" } as there are many of these expected.
+
+fn main() {
+    let a1: i8 = 1i8;
+    let a2: i16 = 2i16;
+    let a3: i32 = 3i32;
+    let a4: i64 = 4i64;
+    let a5: i128 = 5i128;
+
+    let b1 = 1i8;
+    let b2 = 2i16;
+    let b3 = 3i32;
+    let b4 = 4i64;
+    let b5 = 5i128;
+
+    let c1: u8 = 1u8;
+    let c2: u16 = 2u16;
+    let c3: u32 = 3u32;
+    let c4: u64 = 4u64;
+    let c5: u128 = 5u128;
+
+    let d1 = 1u8;
+    let d2 = 2u16;
+    let d3 = 3u32;
+    let d4 = 4u64;
+    let d5 = 5u128;
+}
diff --git a/gcc/testsuite/rust/compile/torture/intrinsics-1.rs b/gcc/testsuite/rust/compile/torture/intrinsics-1.rs
new file mode 100644
index 00000000000..6704c0210d1
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/intrinsics-1.rs
@@ -0,0 +1,22 @@
+// { dg-additional-options -fdump-tree-original }
+
+#![feature(intrinsics)]
+
+extern "rust-intrinsic" {
+    pub fn sqrtf32(x: f32) -> f32;
+    pub fn sinf32(x: f32) -> f32;
+}
+
+fn main() {
+    unsafe fn foo() {
+        let mut f32;
+
+        f32 = sqrtf32(5f32);
+        // { dg-final { scan-tree-dump-times {(?n)f32 = __builtin_sqrtf \(5\.0e\+0\);$} 1 original } }
+
+        f32 = sinf32(39f32);
+        // { dg-final { scan-tree-dump-times {(?n)f32 = __builtin_sinf \(3\.9e\+1\);$} 1 original } }
+    }
+
+    unsafe { foo() };
+}
diff --git a/gcc/testsuite/rust/compile/torture/intrinsics-2.rs b/gcc/testsuite/rust/compile/torture/intrinsics-2.rs
new file mode 100644
index 00000000000..6b2339f38f3
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/intrinsics-2.rs
@@ -0,0 +1,22 @@
+// { dg-additional-options -fdump-tree-original }
+
+#![feature(intrinsics)]
+
+extern "rust-intrinsic" {
+    pub fn size_of<T>() -> usize;
+}
+
+fn main() -> i32 {
+    unsafe fn foo() -> usize {
+        let f: f32;
+
+        let s_f32 = size_of::<f32>();
+        let s_f64 = size_of::<f64>();
+        let s_f32_again = size_of::<f32>();
+
+        s_f32 + s_f64 + s_f32_again
+    }
+
+    // useless code, just used for function compilation caching
+    unsafe { foo() as i32 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/isolated_cr_block_comment.rs b/gcc/testsuite/rust/compile/torture/isolated_cr_block_comment.rs
new file mode 100644
index 00000000000..9a1e090f330
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/isolated_cr_block_comment.rs
@@ -0,0 +1,2 @@
+/* comment cr\r is allowed */
+pub fn main () { }
diff --git a/gcc/testsuite/rust/compile/torture/isolated_cr_line_comment.rs b/gcc/testsuite/rust/compile/torture/isolated_cr_line_comment.rs
new file mode 100644
index 00000000000..4e921a225c2
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/isolated_cr_line_comment.rs
@@ -0,0 +1,2 @@
+// comment cr\r is allowed
+pub fn main () { }
diff --git a/gcc/testsuite/rust/compile/torture/issue-1024.rs b/gcc/testsuite/rust/compile/torture/issue-1024.rs
new file mode 100644
index 00000000000..109540934a8
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/issue-1024.rs
@@ -0,0 +1,11 @@
+extern "rust-intrinsic" {
+    pub fn size_of<T>() -> usize;
+}
+
+fn test() -> usize {
+    unsafe { size_of::<i32>() }
+}
+
+fn main() {
+    let _a = test();
+}
diff --git a/gcc/testsuite/rust/compile/torture/issue-1075.rs b/gcc/testsuite/rust/compile/torture/issue-1075.rs
new file mode 100644
index 00000000000..7c0a0434262
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/issue-1075.rs
@@ -0,0 +1,42 @@
+// { dg-additional-options "-w" }
+extern "rust-intrinsic" {
+    #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
+    pub fn offset<T>(dst: *const T, offset: isize) -> *const T;
+}
+
+struct FatPtr<T> {
+    data: *const T,
+    len: usize,
+}
+
+union Repr<T> {
+    rust: *const [T],
+    rust_mut: *mut [T],
+    raw: FatPtr<T>,
+}
+
+impl<T> *const [T] {
+    pub const fn len(self) -> usize {
+        // SAFETY: this is safe because `*const [T]` and `FatPtr<T>` have the same layout.
+        // Only `std` can make this guarantee.
+        unsafe { Repr { rust: self }.raw.len }
+    }
+
+    pub const fn as_ptr(self) -> *const T {
+        self as *const T
+    }
+}
+
+impl<T> *const T {
+    pub const unsafe fn offset(self, count: isize) -> *const T {
+        unsafe { offset(self, count) }
+    }
+
+    pub const unsafe fn add(self, count: usize) -> Self {
+        unsafe { self.offset(count as isize) }
+    }
+
+    pub const fn as_ptr(self) -> *const T {
+        self as *const T
+    }
+}
diff --git a/gcc/testsuite/rust/compile/torture/issue-1432.rs b/gcc/testsuite/rust/compile/torture/issue-1432.rs
new file mode 100644
index 00000000000..083a369d16f
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/issue-1432.rs
@@ -0,0 +1,77 @@
+// { dg-additional-options "-w" }
+mod intrinsics {
+    extern "rust-intrinsic" {
+        #[rustc_const_stable(feature = "const_int_wrapping", since = "1.40.0")]
+        pub fn wrapping_add<T>(a: T, b: T) -> T;
+        #[rustc_const_stable(feature = "const_int_rotate", since = "1.40.0")]
+        pub fn rotate_left<T>(a: T, b: T) -> T;
+        #[rustc_const_stable(feature = "const_int_rotate", since = "1.40.0")]
+        pub fn rotate_right<T>(a: T, b: T) -> T;
+        #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
+        pub fn offset<T>(ptr: *const T, count: isize) -> *const T;
+    }
+}
+
+mod mem {
+    extern "rust-intrinsic" {
+        #[rustc_const_stable(feature = "const_transmute", since = "1.46.0")]
+        fn transmute<T, U>(_: T) -> U;
+        #[rustc_const_stable(feature = "const_size_of", since = "1.40.0")]
+        fn size_of<T>() -> usize;
+    }
+}
+
+macro_rules! impl_uint {
+    ($($ty:ident = $lang:literal),*) => {
+        $(
+            impl $ty {
+                pub fn wrapping_add(self, rhs: Self) -> Self {
+                    // intrinsics::wrapping_add(self, rhs)
+                    self + rhs
+                }
+
+                pub fn rotate_left(self, n: u32) -> Self {
+                    unsafe {
+                        intrinsics::rotate_left(self, n as Self)
+                    }
+                }
+
+                pub fn rotate_right(self, n: u32) -> Self {
+                    unsafe {
+                        intrinsics::rotate_right(self, n as Self)
+                    }
+                }
+
+                pub fn to_le(self) -> Self {
+                    #[cfg(target_endian = "little")]
+                    {
+                        self
+                    }
+                }
+
+                pub const fn from_le_bytes(bytes: [u8; mem::size_of::<Self>()]) -> Self {
+                    Self::from_le(Self::from_ne_bytes(bytes))
+                }
+
+                pub const fn from_le(x: Self) -> Self {
+                    #[cfg(target_endian = "little")]
+                    {
+                        x
+                    }
+                }
+
+                pub const fn from_ne_bytes(bytes: [u8; mem::size_of::<Self>()]) -> Self {
+                    unsafe { mem::transmute(bytes) }
+                }
+            }
+        )*
+    }
+}
+
+impl_uint!(
+    u8 = "u8",
+    u16 = "u16",
+    u32 = "u32",
+    u64 = "u64",
+    usize = "usize"
+);
diff --git a/gcc/testsuite/rust/compile/torture/issue-1434.rs b/gcc/testsuite/rust/compile/torture/issue-1434.rs
new file mode 100644
index 00000000000..dc000e942e6
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/issue-1434.rs
@@ -0,0 +1,53 @@
+// { dg-options "-w" }
+const BLOCK_LEN: usize = 64;
+
+const IV: [u32; 8] = [
+    0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19,
+];
+
+struct ChunkState {
+    chaining_value: [u32; 8],
+    chunk_counter: u64,
+    block: [u8; BLOCK_LEN],
+    block_len: u8,
+    blocks_compressed: u8,
+    flags: u32,
+}
+
+impl ChunkState {
+    fn new(key_words: [u32; 8], chunk_counter: u64, flags: u32) -> Self {
+        Self {
+            chaining_value: key_words,
+            chunk_counter,
+            block: [0; BLOCK_LEN],
+            block_len: 0,
+            blocks_compressed: 0,
+            flags,
+        }
+    }
+}
+
+pub struct Hasher {
+    chunk_state: ChunkState,
+    key_words: [u32; 8],
+    cv_stack: [[u32; 8]; 54], // Space for 54 subtree chaining values:
+    cv_stack_len: u8,         // 2^54 * CHUNK_LEN = 2^64
+    flags: u32,
+}
+
+impl Hasher {
+    fn new_internal(key_words: [u32; 8], flags: u32) -> Self {
+        Self {
+            chunk_state: ChunkState::new(key_words, 0, flags),
+            key_words,
+            cv_stack: [[0; 8]; 54],
+            cv_stack_len: 0,
+            flags,
+        }
+    }
+
+    /// Construct a new `Hasher` for the regular hash function.
+    pub fn new() -> Self {
+        Self::new_internal(IV, 0)
+    }
+}
diff --git a/gcc/testsuite/rust/compile/torture/issue-368.rs b/gcc/testsuite/rust/compile/torture/issue-368.rs
new file mode 100644
index 00000000000..18bc9bdc62e
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/issue-368.rs
@@ -0,0 +1,9 @@
+struct S;
+
+fn foo<S>(s: S) -> S {
+    s
+}
+
+fn main() {
+    let _s: S = foo(S);
+}
diff --git a/gcc/testsuite/rust/compile/torture/issue-808.rs b/gcc/testsuite/rust/compile/torture/issue-808.rs
new file mode 100644
index 00000000000..2e5a81fe516
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/issue-808.rs
@@ -0,0 +1,20 @@
+pub trait Foo {
+    type Target;
+
+    fn bar(&self) -> &Self::Target;
+}
+
+impl<T> Foo for &T {
+    type Target = T;
+
+    fn bar(&self) -> &T {
+        *self
+    }
+}
+
+pub fn main() {
+    let a: i32 = 123;
+    let b: &i32 = &a;
+
+    b.bar();
+}
diff --git a/gcc/testsuite/rust/compile/torture/issue-862.rs b/gcc/testsuite/rust/compile/torture/issue-862.rs
new file mode 100644
index 00000000000..c1a4609ba86
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/issue-862.rs
@@ -0,0 +1,74 @@
+// { dg-additional-options "-w" }
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+#[lang = "deref"]
+pub trait Deref {
+    type Target;
+
+    fn deref(&self) -> &Self::Target;
+}
+
+impl<T> Deref for &T {
+    type Target = T;
+
+    fn deref(&self) -> &T {
+        unsafe {
+            let a = "imm_deref\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        }
+
+        *self
+    }
+}
+
+impl<T> Deref for &mut T {
+    type Target = T;
+
+    fn deref(&self) -> &T {
+        unsafe {
+            let a = "mut_deref\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        }
+
+        *self
+    }
+}
+
+struct Foo<T>(T);
+impl<T> Deref for Foo<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        unsafe {
+            let a = "foo_deref\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        }
+
+        &self.0
+    }
+}
+
+struct Bar(i32);
+impl Bar {
+    fn cake(self) -> i32 {
+        self.0 + 1
+    }
+}
+
+pub fn main() {
+    let foo: Foo<Bar> = Foo(Bar(123));
+    let bar: Bar = *foo;
+
+    let cake_result: i32 = foo.cake();
+}
diff --git a/gcc/testsuite/rust/compile/torture/issue-893-2.rs b/gcc/testsuite/rust/compile/torture/issue-893-2.rs
new file mode 100644
index 00000000000..88a865d66dc
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/issue-893-2.rs
@@ -0,0 +1,35 @@
+// { dg-additional-options "-w" }
+struct Foo<T>(T);
+impl<T> Foo<T> {
+    fn new<Y>(a: T, b: Y) -> Self {
+        Self(a)
+    }
+}
+
+struct Bar<T>(T);
+impl Bar<i32> {
+    fn baz(self) {}
+
+    fn test() -> i32 {
+        123
+    }
+}
+
+struct Baz<A, B>(A, B);
+impl Baz<i32, f32> {
+    fn test<X>(a: X) -> X {
+        a
+    }
+}
+
+pub fn main() {
+    let a = Foo::<i32>::new::<f32>(123, 456f32);
+    let b = Foo::new::<f32>(123, 456f32);
+
+    let c = Bar::<i32>(123);
+    let d = Bar::baz(c);
+
+    let e = Bar::test();
+
+    let f = Baz::test::<bool>(true);
+}
diff --git a/gcc/testsuite/rust/compile/torture/issue-893.rs b/gcc/testsuite/rust/compile/torture/issue-893.rs
new file mode 100644
index 00000000000..d8245f3e0d8
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/issue-893.rs
@@ -0,0 +1,11 @@
+// { dg-additional-options "-w" }
+struct Foo<T>(T);
+impl<T> Foo<T> {
+    fn new<Y>(a: T, b: Y) -> Self {
+        Self(a)
+    }
+}
+
+pub fn test() {
+    let a = Foo::<i32>::new::<f32>(123, 456f32);
+}
diff --git a/gcc/testsuite/rust/compile/torture/lazybooleanexpr_function.rs b/gcc/testsuite/rust/compile/torture/lazybooleanexpr_function.rs
new file mode 100644
index 00000000000..1be51274d03
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/lazybooleanexpr_function.rs
@@ -0,0 +1,14 @@
+fn foo() -> bool {
+    return true;
+}
+
+fn bar() -> bool {
+    return false;
+}
+
+
+
+fn main() {
+    let _a = true && foo();
+    let _b = true || bar();
+}
\ No newline at end of file
diff --git a/gcc/testsuite/rust/compile/torture/lifetime1.rs b/gcc/testsuite/rust/compile/torture/lifetime1.rs
new file mode 100644
index 00000000000..151fd827b5e
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/lifetime1.rs
@@ -0,0 +1,7 @@
+fn foo<'a>(t: &'a str) -> &'a str {
+    t
+}
+
+fn main() {
+    foo("hello world");
+}
diff --git a/gcc/testsuite/rust/compile/torture/literals1.rs b/gcc/testsuite/rust/compile/torture/literals1.rs
new file mode 100644
index 00000000000..cf021e295d2
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/literals1.rs
@@ -0,0 +1,11 @@
+// { dg-prune-output "warning: unused name" } as there are many of these expected.
+
+fn main() {
+    let hex: i32 = 0xFF;
+    let binary: i32 = 0b11110000;
+    let oct: i32 = 0o70;
+
+    let hex_u8: u8 = 0xFF_u8;
+    let bin_u16: u16 = 0b1111000011110000_u16;
+    let oct: u32 = 0o70_u32;
+}
diff --git a/gcc/testsuite/rust/compile/torture/loop1.rs b/gcc/testsuite/rust/compile/torture/loop1.rs
new file mode 100644
index 00000000000..a8ee2f59bb8
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/loop1.rs
@@ -0,0 +1,10 @@
+fn main() {
+    let mut a = 1;
+    let mut b = 1;
+
+    loop {
+        let c = a + b;
+        a = b;
+        b = c;
+    }
+}
diff --git a/gcc/testsuite/rust/compile/torture/loop2.rs b/gcc/testsuite/rust/compile/torture/loop2.rs
new file mode 100644
index 00000000000..3de3ea81947
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/loop2.rs
@@ -0,0 +1,14 @@
+fn main() {
+    let mut a = 1;
+    let mut b = 1;
+
+    // first number in Fibonacci sequence over 10:
+    loop {
+        if b > 10 {
+            break;
+        }
+        let c = a + b;
+        a = b;
+        b = c;
+    }
+}
diff --git a/gcc/testsuite/rust/compile/torture/loop3.rs b/gcc/testsuite/rust/compile/torture/loop3.rs
new file mode 100644
index 00000000000..76fadfb4337
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/loop3.rs
@@ -0,0 +1,14 @@
+fn main() {
+    let mut a = 1;
+    let mut b = 1;
+
+    // first number in Fibonacci sequence over 10:
+    loop {
+        if b > 10 {
+            return;
+        }
+        let c = a + b;
+        a = b;
+        b = c;
+    }
+}
diff --git a/gcc/testsuite/rust/compile/torture/loop4.rs b/gcc/testsuite/rust/compile/torture/loop4.rs
new file mode 100644
index 00000000000..f7b59357aeb
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/loop4.rs
@@ -0,0 +1,7 @@
+fn main() {
+    'outer: loop {
+        'inner: loop {
+            break 'outer;
+        }
+    }
+}
diff --git a/gcc/testsuite/rust/compile/torture/loop5.rs b/gcc/testsuite/rust/compile/torture/loop5.rs
new file mode 100644
index 00000000000..4004cd30b7b
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/loop5.rs
@@ -0,0 +1,14 @@
+fn main() {
+    let mut a = 1;
+    let mut b = 1;
+
+    // first number in Fibonacci sequence over 10:
+    let _fib = loop {
+        if b > 10 {
+            break b;
+        }
+        let c = a + b;
+        a = b;
+        b = c;
+    };
+}
diff --git a/gcc/testsuite/rust/compile/torture/loop6.rs b/gcc/testsuite/rust/compile/torture/loop6.rs
new file mode 100644
index 00000000000..ecd3ad4fd81
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/loop6.rs
@@ -0,0 +1,11 @@
+fn main() {
+    let mut a = 1;
+    let mut b = 1;
+
+    let mut c;
+    while b > 10 {
+        c = a + b;
+        a = b;
+        b = c;
+    }
+}
diff --git a/gcc/testsuite/rust/compile/torture/loop7.rs b/gcc/testsuite/rust/compile/torture/loop7.rs
new file mode 100644
index 00000000000..0cd844592b6
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/loop7.rs
@@ -0,0 +1,13 @@
+fn main() {
+    let mut a = 1;
+    let mut b = 1;
+
+    let _fib = loop {
+        if (a % 2 == 0) {
+            continue;
+        }
+        let c = a + b;
+        a = b;
+        b = c;
+    };
+}
diff --git a/gcc/testsuite/rust/compile/torture/macro-issue1403.rs b/gcc/testsuite/rust/compile/torture/macro-issue1403.rs
new file mode 100644
index 00000000000..7fe6c51053c
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/macro-issue1403.rs
@@ -0,0 +1,23 @@
+macro_rules! stmt {
+    ($s:stmt) => {
+        $s
+    };
+    ($s:stmt, $($ss:stmt),*) => {
+        $s;
+        stmt!($($ss),*);
+    };
+}
+
+fn main() {
+    stmt!(
+        struct S;
+    );
+    stmt!(
+        struct A;,
+        struct B;,
+        struct C;,
+        struct D;,
+        struct E;
+    );
+}
+
diff --git a/gcc/testsuite/rust/compile/torture/macro-issue1426.rs b/gcc/testsuite/rust/compile/torture/macro-issue1426.rs
new file mode 100644
index 00000000000..1b558cfa83d
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/macro-issue1426.rs
@@ -0,0 +1,32 @@
+// { dg-additional-options -fdump-tree-ccp1-raw }
+
+macro_rules! stmt {
+    ($s:stmt) => {
+        $s
+    };
+    ($s:stmt, $($ss:stmt),*) => {
+        $s;
+        stmt!($($ss),*);
+    };
+}
+
+pub fn test() -> i32 {
+    stmt!(
+        let a = 1
+	// { dg-warning {unused name 'a'} {} { target *-*-* } .-1 }
+    );
+    stmt!(
+        let b = 2,
+        let c = 3,
+        let d = 4,
+        let e = 5,
+        let f = b + c + d + e
+    );
+    f
+    // { dg-final { scan-tree-dump-times {gimple_return <14>} 1 ccp1 { target __OPTIMIZE__ } } }
+}
+
+fn main() {
+    let _ = test();
+}
+
diff --git a/gcc/testsuite/rust/compile/torture/macro_as_expr.rs b/gcc/testsuite/rust/compile/torture/macro_as_expr.rs
new file mode 100644
index 00000000000..b0084e7b466
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/macro_as_expr.rs
@@ -0,0 +1,14 @@
+// { dg-additional-options "-w" }
+
+macro_rules! add {
+    ($a:expr) => { $a };
+    ($a:expr, $($b:expr),+) => { $a + add!($($b),*) }
+}
+
+fn main() -> i32 {
+    if add!(add!(1, 2)) > add!(5) {
+        add!(1, add!(2, 3), add!(4))
+    } else {
+        add!(5, add!(6, 7), add!(8), 9) + 10
+    }
+}
diff --git a/gcc/testsuite/rust/compile/torture/match1.rs b/gcc/testsuite/rust/compile/torture/match1.rs
new file mode 100644
index 00000000000..916b11a3194
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/match1.rs
@@ -0,0 +1,16 @@
+// { dg-additional-options "-w" }
+enum Foo {
+    A,
+    B,
+    C(char),
+    D { x: i64, y: i64 },
+}
+
+fn inspect(f: Foo) {
+    match f {
+        Foo::A => {}
+        Foo::B => {}
+        Foo::C(x) => {}
+        Foo::D { x, y } => {}
+    }
+}
diff --git a/gcc/testsuite/rust/compile/torture/methods1.rs b/gcc/testsuite/rust/compile/torture/methods1.rs
new file mode 100644
index 00000000000..a8e384dabea
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/methods1.rs
@@ -0,0 +1,41 @@
+struct Point {
+    x: f64,
+    y: f64,
+// { dg-warning "field is never read" "" { target *-*-* } .-1 }
+}
+
+impl Point {
+    fn origin() -> Point {
+        Point { x: 0.0, y: 0.0 }
+    }
+
+    fn new(x: f64, y: f64) -> Point {
+        Point { x: x, y: y }
+    }
+}
+
+struct Rectangle {
+    p1: Point,
+    p2: Point,
+}
+
+impl Rectangle {
+    fn from(p1: Point, p2: Point) -> Self {
+        Self { p1, p2 }
+    }
+
+    fn sum_x(self) -> f64 {
+        let p1 = self.p1;
+        let p2 = self.p2;
+        p1.x + p2.x
+    }
+}
+
+fn main() {
+    let p1 = Point::origin();
+    let p2 = Point::new(3.0, 4.0);
+    let rect = Rectangle::from(p1, p2);
+
+    let sum = rect.sum_x();
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/methods2.rs b/gcc/testsuite/rust/compile/torture/methods2.rs
new file mode 100644
index 00000000000..d63211bdf8a
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/methods2.rs
@@ -0,0 +1,43 @@
+struct Point {
+    x: f64,
+    y: f64,
+// { dg-warning "field is never read" "" { target *-*-* } .-1 }
+}
+
+impl Point {
+    fn origin() -> Point {
+        Point { x: 0.0, y: 0.0 }
+    }
+
+    fn new(x: f64, y: f64) -> Point {
+        Point { x: x, y: y }
+    }
+}
+
+struct Rectangle {
+    p1: Point,
+    p2: Point,
+}
+
+impl Rectangle {
+    fn from(p1: Point, p2: Point) -> Self {
+        Self { p1, p2 }
+    }
+}
+
+fn main() {
+    let p1 = Point::origin();
+    let p2 = Point::new(3.0, 4.0);
+    let rect = Rectangle::from(p1, p2);
+
+    let sum = rect.sum_x();
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
+
+impl Rectangle {
+    fn sum_x(self) -> f64 {
+        let p1 = self.p1;
+        let p2 = self.p2;
+        p1.x + p2.x
+    }
+}
diff --git a/gcc/testsuite/rust/compile/torture/methods3.rs b/gcc/testsuite/rust/compile/torture/methods3.rs
new file mode 100644
index 00000000000..55426f4fcf7
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/methods3.rs
@@ -0,0 +1,44 @@
+struct Point {
+    x: f64,
+    y: f64,
+// { dg-warning "field is never read" "" { target *-*-* } .-1 }
+}
+
+impl Point {
+    fn origin() -> Point {
+        Point { x: 0.0, y: 0.0 }
+    }
+
+    fn new(x: f64, y: f64) -> Point {
+        Point { x: x, y: y }
+    }
+}
+
+struct Rectangle {
+    p1: Point,
+    p2: Point,
+}
+
+impl Rectangle {
+    fn from(p1: Point, p2: Point) -> Self {
+        Self { p1, p2 }
+    }
+
+    fn sum_x(self) -> f64 {
+        let p1 = self.p1;
+        let p2 = self.p2;
+        p1.x + p2.x
+    }
+}
+
+fn main() {
+    let p1 = Point::origin();
+    let p2 = Point::new(3.0, 4.0);
+    let rect = Rectangle::from(p1, p2);
+
+    let sum = rect.sum_x();
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    // multiple MethodCallExpr were causing issue #310
+    let sum = rect.sum_x();
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/mod-nameresolve.rs b/gcc/testsuite/rust/compile/torture/mod-nameresolve.rs
new file mode 100644
index 00000000000..09a722681b0
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/mod-nameresolve.rs
@@ -0,0 +1,5 @@
+mod foo {
+    struct A; // { dg-warning "struct is never constructed" }
+}
+
+fn main() {}
diff --git a/gcc/testsuite/rust/compile/torture/mod1.rs b/gcc/testsuite/rust/compile/torture/mod1.rs
new file mode 100644
index 00000000000..651678c6a34
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/mod1.rs
@@ -0,0 +1,11 @@
+// This is testing name resolution
+
+mod _foo {
+    struct _A;
+}
+
+mod _bar {
+    mod _barbis {
+        struct _B;
+    }
+}
diff --git a/gcc/testsuite/rust/compile/torture/mod2.rs b/gcc/testsuite/rust/compile/torture/mod2.rs
new file mode 100644
index 00000000000..04722a94bb1
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/mod2.rs
@@ -0,0 +1,13 @@
+mod foomod {
+    pub struct Foo {}
+}
+
+impl foomod::Foo {
+    pub fn new() -> Self {
+        foomod::Foo {}
+    }
+}
+
+fn main() {
+    let _a = foomod::Foo::new();
+}
diff --git a/gcc/testsuite/rust/compile/torture/mod3.rs b/gcc/testsuite/rust/compile/torture/mod3.rs
new file mode 100644
index 00000000000..2ace8c064d8
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/mod3.rs
@@ -0,0 +1,22 @@
+// { dg-additional-options "-w" }
+mod A {
+    pub mod B {
+        pub mod C {
+            pub struct Foo {
+                pub f: i32,
+            }
+            impl Foo {
+                pub fn new() -> Self {
+                    Foo { f: 23i32 }
+                }
+            }
+        }
+    }
+}
+
+fn main() -> i32 {
+    let a = A::B::C::Foo::new();
+    let b = A::B::C::Foo { f: -23i32 };
+
+    a.f - b.f
+}
diff --git a/gcc/testsuite/rust/compile/torture/modules/mod.rs b/gcc/testsuite/rust/compile/torture/modules/mod.rs
new file mode 100644
index 00000000000..3d65176b6c3
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/modules/mod.rs
@@ -0,0 +1,3 @@
+pub fn return_12() -> i32 {
+    12
+}
diff --git a/gcc/testsuite/rust/compile/torture/modules/valid_path.rs b/gcc/testsuite/rust/compile/torture/modules/valid_path.rs
new file mode 100644
index 00000000000..6a1519c3fc4
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/modules/valid_path.rs
@@ -0,0 +1 @@
+fn unused() {}
diff --git a/gcc/testsuite/rust/compile/torture/must_use1.rs b/gcc/testsuite/rust/compile/torture/must_use1.rs
new file mode 100644
index 00000000000..95a6657c8c1
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/must_use1.rs
@@ -0,0 +1,16 @@
+#[must_use = "TEST 1"]
+fn test1() -> i32 {
+    123
+}
+
+#[must_use = "TEST 2"]
+fn test2() -> i32 {
+    456
+}
+
+fn main() {
+    let _a = test1();
+
+    test2();
+    // { dg-warning "ignoring return value of" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/must_use2.rs b/gcc/testsuite/rust/compile/torture/must_use2.rs
new file mode 100644
index 00000000000..466f7ee7a14
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/must_use2.rs
@@ -0,0 +1,16 @@
+trait A {
+    #[must_use]
+    fn test() -> i32;
+}
+
+struct S;
+impl A for S {
+    fn test() -> i32 {
+        123
+    }
+}
+
+fn main() {
+    S::test();
+    // { dg-warning "ignoring return value of" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/name_resolve1.rs b/gcc/testsuite/rust/compile/torture/name_resolve1.rs
new file mode 100644
index 00000000000..817f48b60ee
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/name_resolve1.rs
@@ -0,0 +1,23 @@
+struct Foo(i32, bool);
+
+impl Foo {
+    fn new(a: i32, b: bool) -> Foo {
+        Foo(a, b)
+    }
+
+    fn test() -> i32 {
+        test()
+    }
+}
+
+fn test() -> i32 {
+    123
+}
+
+fn main() {
+    let a;
+    a = Foo::new(1, true);
+
+    let b;
+    b = Foo::test();
+}
diff --git a/gcc/testsuite/rust/compile/torture/negation_function.rs b/gcc/testsuite/rust/compile/torture/negation_function.rs
new file mode 100644
index 00000000000..b592f9c00ef
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/negation_function.rs
@@ -0,0 +1,7 @@
+fn ret1() -> i32 {
+    return 1;
+}
+
+fn main() {
+    let _a = -ret1();
+}
\ No newline at end of file
diff --git a/gcc/testsuite/rust/compile/torture/nested_fn1.rs b/gcc/testsuite/rust/compile/torture/nested_fn1.rs
new file mode 100644
index 00000000000..075b5dba8e0
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/nested_fn1.rs
@@ -0,0 +1,10 @@
+pub fn main() {
+    let a = 123;
+
+    fn test(x: i32) -> i32 {
+        x + 456
+    }
+
+    let b;
+    b = test(a);
+}
diff --git a/gcc/testsuite/rust/compile/torture/nested_fn2.rs b/gcc/testsuite/rust/compile/torture/nested_fn2.rs
new file mode 100644
index 00000000000..7040c862e75
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/nested_fn2.rs
@@ -0,0 +1,11 @@
+pub fn main() {
+    fn test<T>(x: T) -> T {
+        x
+    }
+
+    let mut a = 123;
+    a = test(a);
+
+    let mut b = 456f32;
+    b = test(b);
+}
diff --git a/gcc/testsuite/rust/compile/torture/nested_struct1.rs b/gcc/testsuite/rust/compile/torture/nested_struct1.rs
new file mode 100644
index 00000000000..2bd5eadd4c4
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/nested_struct1.rs
@@ -0,0 +1,20 @@
+struct Point {
+    x: f64,
+    y: f64,
+// { dg-warning "field is never read" "" { target *-*-* } .-1 }
+}
+
+struct Rectangle {
+    p1: Point,
+    p2: Point,
+// { dg-warning "field is never read" "" { target *-*-* } .-1 }
+}
+
+fn main() {
+    let p1 = Point { x: 0.0, y: 0.0 };
+    let p2 = Point { x: 2.0, y: 4.0 };
+    let rect = Rectangle { p1, p2 };
+
+    let a = rect.p1.x;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/never_type1.rs b/gcc/testsuite/rust/compile/torture/never_type1.rs
new file mode 100644
index 00000000000..0f15029097d
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/never_type1.rs
@@ -0,0 +1,22 @@
+fn foo() -> i32 {
+    let c;
+    let d;
+
+    c = if false {
+        return 1;
+    } else {
+        0.0
+    };
+
+    d = if true {
+        0.0
+    } else {
+        return 1;
+    };
+
+    0
+}
+
+fn main() {
+    foo();
+}
diff --git a/gcc/testsuite/rust/compile/torture/not_shebang.rs b/gcc/testsuite/rust/compile/torture/not_shebang.rs
new file mode 100644
index 00000000000..37e01b65940
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/not_shebang.rs
@@ -0,0 +1,3 @@
+#!
+[allow(unused)]
+fn main () { }
diff --git a/gcc/testsuite/rust/compile/torture/not_shebang_block_comment.rs b/gcc/testsuite/rust/compile/torture/not_shebang_block_comment.rs
new file mode 100644
index 00000000000..662f6506749
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/not_shebang_block_comment.rs
@@ -0,0 +1 @@
+#!/*/this/is/a/comment*/[allow(unused)] fn main () { }
diff --git a/gcc/testsuite/rust/compile/torture/not_shebang_comment.rs b/gcc/testsuite/rust/compile/torture/not_shebang_comment.rs
new file mode 100644
index 00000000000..273ae4e8e2a
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/not_shebang_comment.rs
@@ -0,0 +1,3 @@
+#!//this/is/a/comment
+[allow(unused)]   
+fn main () { }
diff --git a/gcc/testsuite/rust/compile/torture/not_shebang_multiline_comment.rs b/gcc/testsuite/rust/compile/torture/not_shebang_multiline_comment.rs
new file mode 100644
index 00000000000..86800b14cb3
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/not_shebang_multiline_comment.rs
@@ -0,0 +1,7 @@
+#!//this/is/a/comment
+
+/* Also a /* nested */
+   multiline // comment
+   with some more whitespace after, but then finally a [, so not a real #! line.  */
+
+[allow(unused)] fn main () { }
diff --git a/gcc/testsuite/rust/compile/torture/not_shebang_spaces.rs b/gcc/testsuite/rust/compile/torture/not_shebang_spaces.rs
new file mode 100644
index 00000000000..6b94a69111a
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/not_shebang_spaces.rs
@@ -0,0 +1,6 @@
+#!   
+
+    [allow(unused)]   
+
+        fn main () { }
+    
diff --git a/gcc/testsuite/rust/compile/torture/parameter_usage1.rs b/gcc/testsuite/rust/compile/torture/parameter_usage1.rs
new file mode 100644
index 00000000000..448e9603a94
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/parameter_usage1.rs
@@ -0,0 +1,8 @@
+fn test(a: i32, b: i32) -> i32 {
+    a + b
+}
+
+fn main() {
+    let a = test(1, 4);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/parens1.rs b/gcc/testsuite/rust/compile/torture/parens1.rs
new file mode 100644
index 00000000000..795eb960805
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/parens1.rs
@@ -0,0 +1,5 @@
+fn main() {
+    let a = 123;
+    let b = a + (a * 2);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/pointer1.rs b/gcc/testsuite/rust/compile/torture/pointer1.rs
new file mode 100644
index 00000000000..f283411abe2
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/pointer1.rs
@@ -0,0 +1,9 @@
+pub fn main() {
+    let mut num = 2;
+    let r1: *const i32 = &num;
+    let r2 = unsafe { *r1 } + unsafe { *r1 };
+    let r3 = num;
+    num = 4;
+    let r4 = num + unsafe { *r1 } * r3;
+    let _eightteen = r2 + r3 + r4;
+}
diff --git a/gcc/testsuite/rust/compile/torture/primconsts.rs b/gcc/testsuite/rust/compile/torture/primconsts.rs
new file mode 100644
index 00000000000..bcf9456d059
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/primconsts.rs
@@ -0,0 +1,72 @@
+const TRUE: bool = true;
+const FALSE: bool = !TRUE;
+
+const U8ZERO: u8 = 0;
+const U8ONE: u8 = U8ZERO + 1;
+const U16ZERO: u16 = 0;
+const U16ONE: u16 = U16ZERO + 1;
+const U32ZERO: u32 = 0;
+const U32ONE: u32 = U32ZERO + 1;
+const U64ZERO: u64 = 0;
+const U64ONE: u64 = U64ZERO + 1;
+const U128ZERO: u128 = 0;
+const U128ONE: u128 = U128ZERO + 1;
+
+const I8ZERO: i8 = 0;
+const I8ONE: i8 = I8ZERO + 1;
+const I16ZERO: i16 = 0;
+const I16ONE: i16 = I16ZERO + 1;
+const I32ZERO: i32 = 0;
+const I32ONE: i32 = I32ZERO + 1;
+const I64ZERO: i64 = 0;
+const I64ONE: i64 = I64ZERO + 1;
+const I128ZERO: i128 = 0;
+const I128ONE: i128 = I128ZERO + 1;
+
+const F32ZERO: f32 = 0.0;
+const F32ONE: f32 = F32ZERO + 1.0;
+const F64ZERO: f64 = 0.0;
+const F64ONE: f64 = F64ZERO + 1.0;
+
+const USIZEZERO: usize = 0;
+const USIZEONE: usize = USIZEZERO + 1;
+const ISIZEZERO: isize = 0;
+const ISIZEONE: isize = ISIZEZERO + 1;
+
+/* Not yet supported 
+const CHARPI: char = '\u{03C0}';
+const STRHELLO: &str = "Hello World!";
+*/
+
+extern "C" { fn abort (); }
+
+pub fn main ()
+{
+  if TRUE == FALSE { unsafe { abort (); } }
+  if U8ZERO > U8ONE { unsafe { abort (); } }
+  if U16ZERO > U16ONE { unsafe { abort (); } }
+  if U32ZERO > U32ONE { unsafe { abort (); } }
+  if U64ZERO > U64ONE { unsafe { abort (); } }
+  if U128ZERO > U128ONE { unsafe { abort (); } }
+
+  if I8ONE <= I8ZERO { unsafe { abort (); } }
+  if I16ONE <= I16ZERO { unsafe { abort (); } }
+  if I32ONE <= I32ZERO { unsafe { abort (); } }
+  if I64ONE <= I64ZERO { unsafe { abort (); } }
+  if I128ONE <= I128ZERO { unsafe { abort (); } }
+
+  if F32ZERO + F32ONE != F32ONE { unsafe { abort (); } }
+  if F64ZERO + F64ONE != F64ONE { unsafe { abort (); } }
+
+  if USIZEZERO + USIZEONE - USIZEONE + USIZEZERO != USIZEZERO
+    {
+      unsafe { abort (); }
+    }
+  if ISIZEZERO + ISIZEONE - ISIZEONE + ISIZEZERO != ISIZEZERO
+    {
+      unsafe { abort (); }
+    }
+
+ // if CHARPI != '\u{03c0}'  { unsafe { abort (); } }
+ // if STRHELLO != "Hello World!" { unsafe { abort (); } }
+}
diff --git a/gcc/testsuite/rust/compile/torture/prims_struct_eq.rs b/gcc/testsuite/rust/compile/torture/prims_struct_eq.rs
new file mode 100644
index 00000000000..81ab7424627
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/prims_struct_eq.rs
@@ -0,0 +1,91 @@
+extern "C"
+{
+  fn abort ();
+}
+
+struct Prims
+{
+  b1: bool,
+  b2: bool,
+  b3: bool,
+  b4: bool,
+  c1: char,
+  c2: char,
+  u81: u8,
+  u82: u8,
+  u83: u8,
+  u84: u8,
+  i81: i8,
+  i82: i8,
+  i83: i8,
+  i84: i8,
+  u161: u16,
+  u162: u16,
+  i161: i16,
+  i162: i16,
+  u321: u32,
+  u322: u32,
+  i321: i32,
+  i322: i32,
+  u641: u64,
+  i641: i64,
+  u1281: u128,
+  i1281: i128,
+  usize1: usize,
+  isize1: isize,
+}
+
+fn prims_eq (p1: Prims, p2: Prims) -> bool
+{
+  return p1.b1 == p2.b1
+         && p1.b2 == p2.b2
+         && p1.b3 == p2.b3
+         && p1.b4 == p2.b4
+         && p1.c1 == p2.c1
+         && p1.c2 == p2.c2
+         && p1.u81 == p2.u81
+         && p1.u82 == p2.u82
+         && p1.u83 == p2.u83
+         && p1.u84 == p2.u84
+         && p1.i81 == p2.i81
+         && p1.i82 == p2.i82
+         && p1.i83 == p2.i83
+         && p1.i84 == p2.i84
+         && p1.u161 == p2.u161
+         && p1.u162 == p2.u162
+         && p1.i161 == p2.i161
+         && p1.i162 == p2.i162
+         && p1.u321 == p2.u321
+         && p1.u322 == p2.u322
+         && p1.i321 == p2.i321
+         && p1.i322 == p2.i322
+         && p1.u641 == p2.u641
+         && p1.i641 == p2.i641
+         && p1.u1281 == p2.u1281
+         && p1.i1281 == p2.i1281
+         && p1.usize1 == p2.usize1
+         && p1.isize1 == p2.isize1;
+}
+
+pub fn main ()
+{
+  let p1 = Prims { b1: true, b2: false, b3: false, b4: true,
+                   c1: 'a', c2: 'b',
+                   u81: 1, u82: 2, u83: 3, u84: 4,
+                   i81: -1, i82: -2, i83: -3, i84: -4,
+                   u161: 1, u162: 2,
+                   i161: -1, i162: -2,
+                   u321: 1, u322: 2,
+                   i321: -1, i322: -2,
+                   u641: 1,
+                   i641: -1,
+                   u1281: 1,
+                   i1281: -1,
+                   usize1: 1,
+                   isize1: -1 };
+  let p2 = Prims { usize1: 1, .. p1 };
+  let p3 = Prims { u1281: 0, .. p2 };
+  let p4 = Prims { i1281: 0, .. p3 };
+  if !prims_eq (p1, p2) { unsafe { abort (); } }
+  if prims_eq (p3, p4) { unsafe { abort (); } }
+}
diff --git a/gcc/testsuite/rust/compile/torture/range-lang-item1.rs b/gcc/testsuite/rust/compile/torture/range-lang-item1.rs
new file mode 100644
index 00000000000..86946162276
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/range-lang-item1.rs
@@ -0,0 +1,32 @@
+// { dg-additional-options "-w" }
+#[lang = "RangeFull"]
+pub struct RangeFull;
+
+#[lang = "Range"]
+pub struct Range<Idx> {
+    pub start: Idx,
+    pub end: Idx,
+}
+
+#[lang = "RangeFrom"]
+pub struct RangeFrom<Idx> {
+    pub start: Idx,
+}
+
+#[lang = "RangeTo"]
+pub struct RangeTo<Idx> {
+    pub end: Idx,
+}
+
+#[lang = "RangeInclusive"]
+pub struct RangeInclusive<Idx> {
+    pub start: Idx,
+    pub end: Idx,
+}
+
+fn test() {
+    let a = 1..2; // range
+    let b = 1..; // range from
+    let c = ..3; // range to
+    let d = 0..=2; // range inclusive
+}
diff --git a/gcc/testsuite/rust/compile/torture/raw_identifiers.rs b/gcc/testsuite/rust/compile/torture/raw_identifiers.rs
new file mode 100644
index 00000000000..8746f337048
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/raw_identifiers.rs
@@ -0,0 +1,3 @@
+pub fn square(num: i32) -> i32 { /* { dg-warning "used" } */
+    r#num * num
+}
\ No newline at end of file
diff --git a/gcc/testsuite/rust/compile/torture/raw_identifiers_keywords.rs b/gcc/testsuite/rust/compile/torture/raw_identifiers_keywords.rs
new file mode 100644
index 00000000000..c9aa3cf4938
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/raw_identifiers_keywords.rs
@@ -0,0 +1,3 @@
+pub fn plus(r#break: i32, r#unsafe: i32) -> i32 { /* { dg-warning "used" } */
+    r#break + r#unsafe
+}
\ No newline at end of file
diff --git a/gcc/testsuite/rust/compile/torture/recursive_fn1.rs b/gcc/testsuite/rust/compile/torture/recursive_fn1.rs
new file mode 100644
index 00000000000..e13b41f70bd
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/recursive_fn1.rs
@@ -0,0 +1,12 @@
+fn gcd(x: i32, y: i32) -> i32 {
+    if y == 0 {
+        x
+    } else {
+        gcd(y, x % y)
+    }
+}
+
+fn main() {
+    let a = gcd(100, 5);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/return_function.rs b/gcc/testsuite/rust/compile/torture/return_function.rs
new file mode 100644
index 00000000000..084adaf7f24
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/return_function.rs
@@ -0,0 +1,5 @@
+fn foo() {}
+
+fn main() {
+    return foo();
+}
diff --git a/gcc/testsuite/rust/compile/torture/scoping1.rs b/gcc/testsuite/rust/compile/torture/scoping1.rs
new file mode 100644
index 00000000000..8bc8ede5f7c
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/scoping1.rs
@@ -0,0 +1,11 @@
+fn main() {
+    let x = 1;
+    {
+        let mut x = true;
+        {
+            x = false;
+        }
+    }
+    let x = x + 1;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/self_type1.rs b/gcc/testsuite/rust/compile/torture/self_type1.rs
new file mode 100644
index 00000000000..373d6dd5a60
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/self_type1.rs
@@ -0,0 +1,12 @@
+struct Foo(i32, bool);
+
+impl Foo {
+    fn new(a: i32, b: bool) -> Self {
+        Self(a, b)
+    }
+}
+
+fn main() {
+    let a;
+    a = Foo::new(1, true);
+}
diff --git a/gcc/testsuite/rust/compile/torture/shadow1.rs b/gcc/testsuite/rust/compile/torture/shadow1.rs
new file mode 100644
index 00000000000..b60e7936de9
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/shadow1.rs
@@ -0,0 +1,6 @@
+fn main() {
+    let mut x = 5;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let mut x;
+    x = true;
+}
diff --git a/gcc/testsuite/rust/compile/torture/shadow2.rs b/gcc/testsuite/rust/compile/torture/shadow2.rs
new file mode 100644
index 00000000000..161dc380a07
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/shadow2.rs
@@ -0,0 +1,5 @@
+fn main() {
+    let x = 1;
+    let x = x + 1;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/shebang.rs b/gcc/testsuite/rust/compile/torture/shebang.rs
new file mode 100755
index 00000000000..1c8b9c9a955
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/shebang.rs
@@ -0,0 +1,3 @@
+#!/usr/bin/env cat 
+
+fn main () { }
diff --git a/gcc/testsuite/rust/compile/torture/shebang_plus_attr.rs b/gcc/testsuite/rust/compile/torture/shebang_plus_attr.rs
new file mode 100755
index 00000000000..075bc6cf594
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/shebang_plus_attr.rs
@@ -0,0 +1,3 @@
+#!/usr/bin/env cat 
+#![allow(unused)]
+fn main () { }
diff --git a/gcc/testsuite/rust/compile/torture/shebang_plus_attr2.rs b/gcc/testsuite/rust/compile/torture/shebang_plus_attr2.rs
new file mode 100755
index 00000000000..ece8a52381c
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/shebang_plus_attr2.rs
@@ -0,0 +1,3 @@
+#!//usr/bin/env cat 
+#![allow(unused)]
+fn main () { }
diff --git a/gcc/testsuite/rust/compile/torture/static_function.rs b/gcc/testsuite/rust/compile/torture/static_function.rs
new file mode 100644
index 00000000000..8e3a3795023
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/static_function.rs
@@ -0,0 +1,8 @@
+fn test(x: i32) -> i32 {
+    return x + 1;
+}
+
+fn main() {
+    let call_test = test(1);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/static_var1.rs b/gcc/testsuite/rust/compile/torture/static_var1.rs
new file mode 100644
index 00000000000..5be0e75ce72
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/static_var1.rs
@@ -0,0 +1,6 @@
+static x:i32 = 3;
+
+fn main() {
+    let y = x +1;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/stmt_with_block1.rs b/gcc/testsuite/rust/compile/torture/stmt_with_block1.rs
new file mode 100644
index 00000000000..b6aa56cc2e6
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/stmt_with_block1.rs
@@ -0,0 +1,13 @@
+fn test(x: i32) -> i32 {
+    if x > 1 { 1 } else { 2 };
+    if x > 1 { 1; } else { 2; }
+
+    { 3; }
+    { 3 };
+
+    { 3 }
+}
+
+fn main() {
+    let a = test(0); // { dg-warning "unused name" }
+}
diff --git a/gcc/testsuite/rust/compile/torture/str1.rs b/gcc/testsuite/rust/compile/torture/str1.rs
new file mode 100644
index 00000000000..088827853d8
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/str1.rs
@@ -0,0 +1,7 @@
+fn main() {
+    let a;
+    a = "hello world infer";
+
+    let b: &str;
+    b = "hello world specified";
+}
diff --git a/gcc/testsuite/rust/compile/torture/struct_access1.rs b/gcc/testsuite/rust/compile/torture/struct_access1.rs
new file mode 100644
index 00000000000..ec85d289fb5
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/struct_access1.rs
@@ -0,0 +1,12 @@
+struct Foo {
+    one: i32,
+    two: i32,
+}
+
+fn main() {
+    let struct_test = Foo { one: 1, two: 2 };
+    let a = struct_test.one;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let b = struct_test.two;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/struct_base_init_1.rs b/gcc/testsuite/rust/compile/torture/struct_base_init_1.rs
new file mode 100644
index 00000000000..ee00c2c468b
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/struct_base_init_1.rs
@@ -0,0 +1,13 @@
+struct Foo {
+    a: i32,
+// { dg-warning "field is never read" "" { target *-*-* } .-1 }
+    b: i32,
+}
+
+fn foo() -> Foo {
+    Foo { a: 42, b: 32 }
+}
+
+fn main() {
+    let _f = Foo { a: 10, ..foo() };
+}
diff --git a/gcc/testsuite/rust/compile/torture/struct_decl.rs b/gcc/testsuite/rust/compile/torture/struct_decl.rs
new file mode 100644
index 00000000000..9e8ea6b100b
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/struct_decl.rs
@@ -0,0 +1,14 @@
+// { dg-additional-options "-fdump-tree-gimple -frust-crate=example" }
+
+struct Foo {
+    a: u16,
+    // { dg-warning "field is never read" "" { target *-*-* } .-1 }
+    b: u8,
+    // { dg-warning "field is never read" "" { target *-*-* } .-1 }
+}
+
+fn main() {
+    let my_foo = Foo { a: 1, b: 2 };
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    // { dg-final { scan-tree-dump-times {(?n)const struct example::Foo my_foo;$} 1 gimple } }
+}
diff --git a/gcc/testsuite/rust/compile/torture/struct_init.rs b/gcc/testsuite/rust/compile/torture/struct_init.rs
new file mode 100644
index 00000000000..1926f73b21d
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/struct_init.rs
@@ -0,0 +1,11 @@
+struct Foo {
+    one: i32,
+// { dg-warning "field is never read" "" { target *-*-* } .-1 }
+    two: i32,
+// { dg-warning "field is never read" "" { target *-*-* } .-1 }
+}
+
+fn main() {
+    let struct_test = Foo { one: 1, two: 2 };
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/struct_init_10.rs b/gcc/testsuite/rust/compile/torture/struct_init_10.rs
new file mode 100644
index 00000000000..7fbceb03974
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/struct_init_10.rs
@@ -0,0 +1,9 @@
+fn main() {
+    struct foo {
+        a: i32,
+        b: f32,
+    };
+
+    let a;
+    a = foo { a: 123, b: 456f32 };
+}
diff --git a/gcc/testsuite/rust/compile/torture/struct_init_11.rs b/gcc/testsuite/rust/compile/torture/struct_init_11.rs
new file mode 100644
index 00000000000..16a2f072566
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/struct_init_11.rs
@@ -0,0 +1,34 @@
+pub fn main() {
+    struct O(i32);
+    struct T(i32, i32);
+    struct M(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32);
+
+    // tuples
+    let z = ();
+    let o = (0,);
+    let f = o.0;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    let t = (0, 1);
+    let s = t.1;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    let m = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
+    let l = m.10;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    // tuple structs
+    let so = O(0);
+    let sf = so.0;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    let st = T(0, 1);
+    let fs = st.1;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    let sm = M(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
+    let sl = sm.10;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    z
+}
diff --git a/gcc/testsuite/rust/compile/torture/struct_init_2.rs b/gcc/testsuite/rust/compile/torture/struct_init_2.rs
new file mode 100644
index 00000000000..d7040d3d96d
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/struct_init_2.rs
@@ -0,0 +1,6 @@
+struct Foo(f32, f32);
+
+fn main() {
+    let a = Foo { 0: 10.0, 1: 20.0 };
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/struct_init_3.rs b/gcc/testsuite/rust/compile/torture/struct_init_3.rs
new file mode 100644
index 00000000000..1398f8e7b86
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/struct_init_3.rs
@@ -0,0 +1,13 @@
+struct Foo {
+    a: i32,
+// { dg-warning "field is never read" "" { target *-*-* } .-1 }
+    b: i32,
+// { dg-warning "field is never read" "" { target *-*-* } .-1 }
+}
+
+fn main() {
+    let a = 1;
+    let b = 2;
+    let c = Foo { a, b };
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/struct_init_4.rs b/gcc/testsuite/rust/compile/torture/struct_init_4.rs
new file mode 100644
index 00000000000..2b2746aba7a
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/struct_init_4.rs
@@ -0,0 +1,13 @@
+struct Foo {
+    a: i32,
+    // { dg-warning "field is never read" "" { target *-*-* } .-1 }
+    b: i32,
+    // { dg-warning "field is never read" "" { target *-*-* } .-1 }
+}
+
+fn main() {
+    let a = Foo { a: 1, b: 2 };
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let b = Foo { a: 3, b: 4, ..a };
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/struct_init_5.rs b/gcc/testsuite/rust/compile/torture/struct_init_5.rs
new file mode 100644
index 00000000000..891f64540fe
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/struct_init_5.rs
@@ -0,0 +1,10 @@
+struct Foo {
+    a: i32,
+    b: i32,
+}
+
+fn main() {
+    let a = Foo { a: 1, b: 2 };
+    let b = Foo { ..a };
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/struct_init_6.rs b/gcc/testsuite/rust/compile/torture/struct_init_6.rs
new file mode 100644
index 00000000000..9fc52ed5c50
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/struct_init_6.rs
@@ -0,0 +1,11 @@
+struct Foo {
+    a: i32,
+    // { dg-warning "field is never read" "" { target *-*-* } .-1 }
+    b: i32,
+}
+
+fn main() {
+    let a = Foo { a: 1, b: 2 };
+    let b = Foo { a: 1, ..a };
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/struct_init_7.rs b/gcc/testsuite/rust/compile/torture/struct_init_7.rs
new file mode 100644
index 00000000000..36dc00aca60
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/struct_init_7.rs
@@ -0,0 +1,11 @@
+struct Foo {
+    a: i32,
+    b: f32,
+// { dg-warning "field is never read" "" { target *-*-* } .-1 }
+}
+
+fn main() {
+    let c = Foo { a: 1, b: 2f32 };
+    let b = Foo { b: 4f32, ..c };
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/struct_init_8.rs b/gcc/testsuite/rust/compile/torture/struct_init_8.rs
new file mode 100644
index 00000000000..411ff7d1c6f
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/struct_init_8.rs
@@ -0,0 +1,7 @@
+struct Foo(f32, i32);
+
+fn main() {
+    let a = Foo { 1: 1, 0: 2f32 };
+    let b = Foo { ..a };
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/struct_init_9.rs b/gcc/testsuite/rust/compile/torture/struct_init_9.rs
new file mode 100644
index 00000000000..2daa078d5b3
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/struct_init_9.rs
@@ -0,0 +1,6 @@
+fn main() {
+    struct foo(i32, f32);
+
+    let a;
+    a = foo(123, 456f32);
+}
diff --git a/gcc/testsuite/rust/compile/torture/top_attr.rs b/gcc/testsuite/rust/compile/torture/top_attr.rs
new file mode 100644
index 00000000000..0671369cb12
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/top_attr.rs
@@ -0,0 +1,5 @@
+#![crate_name = "name"]
+
+
+#[allow(dead_code)]
+fn main() {}
\ No newline at end of file
diff --git a/gcc/testsuite/rust/compile/torture/traits1.rs b/gcc/testsuite/rust/compile/torture/traits1.rs
new file mode 100644
index 00000000000..90357738a81
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/traits1.rs
@@ -0,0 +1,16 @@
+trait Foo {
+    fn bar() -> i32;
+}
+
+struct Test(i32, f32);
+
+impl Foo for Test {
+    fn bar() -> i32 {
+        123
+    }
+}
+
+fn main() {
+    let a: i32;
+    a = Test::bar();
+}
diff --git a/gcc/testsuite/rust/compile/torture/traits10.rs b/gcc/testsuite/rust/compile/torture/traits10.rs
new file mode 100644
index 00000000000..a02927007b3
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/traits10.rs
@@ -0,0 +1,30 @@
+trait Foo // where
+//     Self: Sized,
+{
+    fn get(self) -> i32;
+
+    fn test(self) -> i32 {
+        self.get()
+    }
+}
+
+struct Bar(i32);
+impl Foo for Bar {
+    fn get(self) -> i32 {
+        self.0
+    }
+}
+
+fn main() {
+    let a;
+    a = Bar(123);
+
+    let b;
+    b = Bar::get(a);
+
+    let a;
+    a = Bar(123);
+
+    let b;
+    b = a.test();
+}
diff --git a/gcc/testsuite/rust/compile/torture/traits11.rs b/gcc/testsuite/rust/compile/torture/traits11.rs
new file mode 100644
index 00000000000..41c82f01b6d
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/traits11.rs
@@ -0,0 +1,31 @@
+trait Foo {
+    type A;
+
+    fn test(a: Self::A) -> Self::A {
+        a
+    }
+}
+
+struct Bar(i32);
+impl Foo for Bar {
+    type A = i32;
+}
+
+struct Baz(f32);
+impl Foo for Baz {
+    type A = f32;
+}
+
+fn main() {
+    let a;
+    a = Bar(123);
+
+    let b;
+    b = Bar::test(a.0);
+
+    let c;
+    c = Baz(123f32);
+
+    let d;
+    d = Baz::test(c.0);
+}
diff --git a/gcc/testsuite/rust/compile/torture/traits12.rs b/gcc/testsuite/rust/compile/torture/traits12.rs
new file mode 100644
index 00000000000..a55b965baf0
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/traits12.rs
@@ -0,0 +1,29 @@
+trait Foo {
+    type A;
+
+    fn test(a: Self::A) -> Self::A {
+        a
+    }
+}
+
+struct Bar(i32);
+// { dg-warning "struct is never constructed" "" { target *-*-* } .-1 }
+
+impl Foo for Bar {
+    type A = i32;
+}
+
+struct Baz(f32);
+// { dg-warning "struct is never constructed" "" { target *-*-* } .-1 }
+
+impl Foo for Baz {
+    type A = f32;
+}
+
+fn main() {
+    let a: <Baz as Foo>::A;
+    a = 123f32;
+
+    let b;
+    b = <Baz as Foo>::test(a);
+}
diff --git a/gcc/testsuite/rust/compile/torture/traits13.rs b/gcc/testsuite/rust/compile/torture/traits13.rs
new file mode 100644
index 00000000000..326f0390756
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/traits13.rs
@@ -0,0 +1,17 @@
+trait Trait {
+    const FOO: usize;
+    type Target;
+}
+
+struct S;
+// { dg-warning "struct is never constructed" "" { target *-*-* } .-1 }
+
+impl Trait for S {
+    const FOO: usize = 0;
+    type Target = usize;
+}
+
+fn main() {
+    let a: <S as Trait>::Target;
+    a = <S as Trait>::FOO;
+}
diff --git a/gcc/testsuite/rust/compile/torture/traits14.rs b/gcc/testsuite/rust/compile/torture/traits14.rs
new file mode 100644
index 00000000000..8bca0d5c1f6
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/traits14.rs
@@ -0,0 +1,23 @@
+trait Foo<T> {
+    type A;
+
+    fn test(a: T) -> T {
+        a
+    }
+}
+
+struct Bar<T>(T);
+impl<T> Foo<T> for Bar<T> {
+    type A = T;
+}
+
+pub fn main() {
+    let a;
+    a = Bar(123);
+
+    let b: <Bar<i32> as Foo<i32>>::A;
+    b = 456;
+
+    let c: <Bar<i32> as Foo<i32>>::A;
+    c = <Bar<i32> as Foo<i32>>::test(a.0);
+}
diff --git a/gcc/testsuite/rust/compile/torture/traits15.rs b/gcc/testsuite/rust/compile/torture/traits15.rs
new file mode 100644
index 00000000000..c8c40b78b45
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/traits15.rs
@@ -0,0 +1,23 @@
+trait Foo<T> {
+    type A;
+
+    fn test(a: T, b: Self::A) -> (T, Self::A) {
+        (a, b)
+    }
+}
+
+struct Bar<T>(T);
+impl<T> Foo<T> for Bar<T> {
+    type A = T;
+}
+
+pub fn main() {
+    let a;
+    a = Bar(123);
+
+    let b: <Bar<i32> as Foo<i32>>::A;
+    b = 456;
+
+    let c;
+    c = <Bar<i32> as Foo<i32>>::test(a.0, 123);
+}
diff --git a/gcc/testsuite/rust/compile/torture/traits16.rs b/gcc/testsuite/rust/compile/torture/traits16.rs
new file mode 100644
index 00000000000..afc4a86de6d
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/traits16.rs
@@ -0,0 +1,20 @@
+trait A {
+    fn a() -> i32 {
+        123
+    }
+
+    fn b() -> i32 {
+        Self::a() + 456
+    }
+}
+
+struct S;
+impl A for S {}
+
+fn main() {
+    let a;
+    a = S::a();
+
+    let b;
+    b = S::b();
+}
diff --git a/gcc/testsuite/rust/compile/torture/traits17.rs b/gcc/testsuite/rust/compile/torture/traits17.rs
new file mode 100644
index 00000000000..6da8bcb0082
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/traits17.rs
@@ -0,0 +1,23 @@
+trait A {
+    fn a() -> i32 {
+        123
+    }
+}
+
+trait B: A {
+    fn b() -> i32 {
+        Self::a() + 456
+    }
+}
+
+struct S;
+impl A for S {}
+impl B for S {}
+
+fn main() {
+    let a;
+    a = S::a();
+
+    let b;
+    b = S::b();
+}
diff --git a/gcc/testsuite/rust/compile/torture/traits18.rs b/gcc/testsuite/rust/compile/torture/traits18.rs
new file mode 100644
index 00000000000..63319dd2daa
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/traits18.rs
@@ -0,0 +1,5 @@
+trait Foo<'a> {}
+
+trait Bar {
+    type Item: for<'a> Foo<'a>;
+}
diff --git a/gcc/testsuite/rust/compile/torture/traits19.rs b/gcc/testsuite/rust/compile/torture/traits19.rs
new file mode 100644
index 00000000000..4412656f535
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/traits19.rs
@@ -0,0 +1,33 @@
+// { dg-additional-options "-w" }
+trait Get {
+    type Value;
+    fn get(&self) -> &<Self as Get>::Value;
+}
+
+struct Struct {
+    x: isize,
+}
+
+impl Get for Struct {
+    type Value = isize;
+    fn get(&self) -> &isize {
+        &self.x
+    }
+}
+
+trait Grab {
+    type U;
+    fn grab(&self) -> &<Self as Grab>::U;
+}
+
+impl<T: Get> Grab for T {
+    type U = <T as Get>::Value;
+    fn grab(&self) -> &<T as Get>::Value {
+        self.get()
+    }
+}
+
+fn main() {
+    let s = Struct { x: 100 };
+    let a = s.grab();
+}
diff --git a/gcc/testsuite/rust/compile/torture/traits2.rs b/gcc/testsuite/rust/compile/torture/traits2.rs
new file mode 100644
index 00000000000..fc6eb6002e0
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/traits2.rs
@@ -0,0 +1,16 @@
+trait Foo {
+    fn bar() -> i32;
+}
+
+struct Test<T>(T);
+
+impl<T> Foo for Test<T> {
+    fn bar() -> i32 {
+        123
+    }
+}
+
+fn main() {
+    let a: i32;
+    a = Test::<i32>::bar();
+}
diff --git a/gcc/testsuite/rust/compile/torture/traits3.rs b/gcc/testsuite/rust/compile/torture/traits3.rs
new file mode 100644
index 00000000000..deeb81e0946
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/traits3.rs
@@ -0,0 +1,15 @@
+pub trait Foo {
+    fn Bar(self) -> i32;
+}
+
+struct Baz;
+// { dg-warning "struct is never constructed: .Baz." "" { target *-*-* } .-1 }
+
+impl Foo for Baz {
+    fn Bar(self) -> i32 {
+        // { dg-warning "unused name .self." "" { target *-*-* } .-1 }
+        123
+    }
+}
+
+fn main() {}
diff --git a/gcc/testsuite/rust/compile/torture/traits4.rs b/gcc/testsuite/rust/compile/torture/traits4.rs
new file mode 100644
index 00000000000..67b012c11f5
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/traits4.rs
@@ -0,0 +1,21 @@
+trait Foo {
+    type A;
+    type B;
+
+    fn new(a: Self::A, b: Self::B) -> Self;
+}
+
+struct Baz(i32, f32);
+
+impl Foo for Baz {
+    type A = i32;
+    type B = f32;
+
+    fn new(a: Self::A, b: Self::B) -> Self {
+        Baz(a, b)
+    }
+}
+
+fn main() {
+    Baz::new(123, 456f32);
+}
diff --git a/gcc/testsuite/rust/compile/torture/traits5.rs b/gcc/testsuite/rust/compile/torture/traits5.rs
new file mode 100644
index 00000000000..445b0658f5c
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/traits5.rs
@@ -0,0 +1,21 @@
+trait Foo {
+    type A;
+    type B;
+
+    fn new(a: Self::A, b: Self::B) -> Self;
+}
+
+struct Baz(i32, f32);
+
+impl Foo for Baz {
+    type A = i32;
+    type B = f32;
+
+    fn new(a: i32, b: f32) -> Self {
+        Baz(a, b)
+    }
+}
+
+fn main() {
+    Baz::new(123, 456f32);
+}
diff --git a/gcc/testsuite/rust/compile/torture/traits6.rs b/gcc/testsuite/rust/compile/torture/traits6.rs
new file mode 100644
index 00000000000..260dde3f465
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/traits6.rs
@@ -0,0 +1,20 @@
+trait Foo {
+    type A;
+
+    fn baz(a: Self::A) -> Self::A;
+}
+
+struct Bar<T>(T);
+
+impl<T> Foo for Bar<T> {
+    type A = T;
+
+    fn baz(a: Self::A) -> T {
+        a
+    }
+}
+
+fn main() {
+    let a;
+    a = Bar::<i32>::baz(123);
+}
diff --git a/gcc/testsuite/rust/compile/torture/traits7.rs b/gcc/testsuite/rust/compile/torture/traits7.rs
new file mode 100644
index 00000000000..7bc3384ab41
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/traits7.rs
@@ -0,0 +1,19 @@
+trait Foo {
+    const A: i32;
+
+    fn test(self);
+}
+
+struct Bar;
+impl Foo for Bar {
+    const A: i32 = 123;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    fn test(self) {}
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
+
+fn main() {
+    let a = Bar;
+    a.test();
+}
diff --git a/gcc/testsuite/rust/compile/torture/traits8.rs b/gcc/testsuite/rust/compile/torture/traits8.rs
new file mode 100644
index 00000000000..459032fb4a2
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/traits8.rs
@@ -0,0 +1,21 @@
+trait Foo {
+    fn default() -> i32;
+}
+
+struct Bar(i32);
+// { dg-warning "struct is never constructed" "" { target *-*-* } .-1 }
+
+impl Foo for Bar {
+    fn default() -> i32 {
+        123
+    }
+}
+
+fn type_bound_test<T: Foo>() -> i32 {
+    T::default()
+}
+
+fn main() {
+    let a;
+    a = type_bound_test::<Bar>();
+}
diff --git a/gcc/testsuite/rust/compile/torture/traits9.rs b/gcc/testsuite/rust/compile/torture/traits9.rs
new file mode 100644
index 00000000000..89e4bf19b0c
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/traits9.rs
@@ -0,0 +1,27 @@
+trait Foo {
+    fn default() -> i32;
+    fn get(self) -> i32;
+}
+
+struct Bar(i32);
+impl Foo for Bar {
+    fn default() -> i32 {
+        123
+    }
+
+    fn get(self) -> i32 {
+        self.0
+    }
+}
+
+fn type_bound_test<T: Foo>(a: T) -> i32 {
+    T::default() + a.get()
+}
+
+fn main() {
+    let a;
+    a = Bar(456);
+
+    let b;
+    b = type_bound_test(a);
+}
diff --git a/gcc/testsuite/rust/compile/torture/transmute-size-check-1.rs b/gcc/testsuite/rust/compile/torture/transmute-size-check-1.rs
new file mode 100644
index 00000000000..461a35de8ef
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/transmute-size-check-1.rs
@@ -0,0 +1,11 @@
+mod mem {
+    extern "rust-intrinsic" {
+        fn size_of<T>() -> usize;
+        fn transmute<U, V>(_: U) -> V; // { dg-error "cannot transmute between types of different sizes, or dependently-sized types" }
+    }
+}
+
+fn main() {
+    let a = 123;
+    let _b: [u32; mem::size_of::<i32>()] = unsafe { mem::transmute(a) };
+}
diff --git a/gcc/testsuite/rust/compile/torture/transmute1.rs b/gcc/testsuite/rust/compile/torture/transmute1.rs
new file mode 100644
index 00000000000..af9a55d1b97
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/transmute1.rs
@@ -0,0 +1,11 @@
+mod mem {
+    extern "rust-intrinsic" {
+        fn size_of<T>() -> usize;
+        fn transmute<U, V>(_: U) -> V;
+    }
+}
+
+fn main() {
+    let a = 123;
+    let _b: [u8; mem::size_of::<i32>()] = unsafe { mem::transmute(a) };
+}
diff --git a/gcc/testsuite/rust/compile/torture/tuple1.rs b/gcc/testsuite/rust/compile/torture/tuple1.rs
new file mode 100644
index 00000000000..9e6f613f682
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/tuple1.rs
@@ -0,0 +1,6 @@
+fn main() {
+    let a: (i32, bool) = (123, true);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let b;
+    b = (456, 5f32);
+}
diff --git a/gcc/testsuite/rust/compile/torture/tuple2.rs b/gcc/testsuite/rust/compile/torture/tuple2.rs
new file mode 100644
index 00000000000..ab3d0b8715b
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/tuple2.rs
@@ -0,0 +1,5 @@
+fn main() {
+    let a = 123;
+    let b = (a,);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/tuple3.rs b/gcc/testsuite/rust/compile/torture/tuple3.rs
new file mode 100644
index 00000000000..d0fb6fc4429
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/tuple3.rs
@@ -0,0 +1,9 @@
+fn main() {
+    let a = (1, true);
+
+    let b;
+    let c;
+
+    b = a.0;
+    c = a.1;
+}
diff --git a/gcc/testsuite/rust/compile/torture/tuple_enum_variants.rs b/gcc/testsuite/rust/compile/torture/tuple_enum_variants.rs
new file mode 100644
index 00000000000..d953e3d89a1
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/tuple_enum_variants.rs
@@ -0,0 +1,23 @@
+enum E {
+    T0(),
+    T1(i32),
+    T2(i32, u32),
+}
+
+/* The following doesn't parse yet...
+fn f(e0: E, e1: E, e2: E) -> (E,E,E,())
+{
+  let e = e0;
+  let f = e1;
+  let g = e2;
+  (e,f,g,())
+}
+
+fn main()
+{
+  let e0 = E::T0();
+  let e1 = E::T1(0);
+  let e2 = E::T2(0,1);
+  f(e0, e1, e2).3
+}
+*/
diff --git a/gcc/testsuite/rust/compile/torture/tuple_field_access.rs b/gcc/testsuite/rust/compile/torture/tuple_field_access.rs
new file mode 100644
index 00000000000..8d1bbe9906c
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/tuple_field_access.rs
@@ -0,0 +1,6 @@
+struct Foo(i32, i32);
+
+fn main() {
+    let mut a = Foo(1, 2);
+    a.0 = 22;
+}
diff --git a/gcc/testsuite/rust/compile/torture/tuple_function.rs b/gcc/testsuite/rust/compile/torture/tuple_function.rs
new file mode 100644
index 00000000000..514b586cc09
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/tuple_function.rs
@@ -0,0 +1,6 @@
+fn foo() -> i32 {
+    return 1;
+}
+fn main() {
+    let _a = (foo(), 2);
+}
diff --git a/gcc/testsuite/rust/compile/torture/tuple_index.rs b/gcc/testsuite/rust/compile/torture/tuple_index.rs
new file mode 100644
index 00000000000..f904fae9b5b
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/tuple_index.rs
@@ -0,0 +1,32 @@
+fn main()
+{
+  // tuples
+  let z = ();
+
+  let o = (0,);
+  let _f = o.0;
+
+  let t = (0,1);
+  let _s = t.1;
+
+  let m = (0,1,2,3,4,5,6,7,8,9,10);
+  let _l = m.10;
+
+  // tuple structs
+  struct E();
+  let _e = E();
+
+  struct O(i32);
+  let so = O(0);
+  let _sf = so.0;
+
+  struct T(i32,i32);
+  let st = T(0,1);
+  let _fs = st.1;
+
+  struct M(i32,i32,i32,i32,i32,i32,i32,i32,i32,i32,i32);
+  let sm = M(0,1,2,3,4,5,6,7,8,9,10);
+  let _sl = sm.10;
+
+  z
+}
diff --git a/gcc/testsuite/rust/compile/torture/tuple_struct1.rs b/gcc/testsuite/rust/compile/torture/tuple_struct1.rs
new file mode 100644
index 00000000000..0ac19b179a9
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/tuple_struct1.rs
@@ -0,0 +1,6 @@
+struct Foo(i32, i32, bool);
+
+fn main() {
+    let a = Foo(1, 2, true);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/tuple_struct2.rs b/gcc/testsuite/rust/compile/torture/tuple_struct2.rs
new file mode 100644
index 00000000000..5e0a76e1de8
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/tuple_struct2.rs
@@ -0,0 +1,11 @@
+struct Foo(i32, bool);
+
+fn main() {
+    let a = Foo(1, true);
+
+    let b;
+    let c;
+
+    b = a.0;
+    c = a.1;
+}
diff --git a/gcc/testsuite/rust/compile/torture/tuple_struct_unit.rs b/gcc/testsuite/rust/compile/torture/tuple_struct_unit.rs
new file mode 100644
index 00000000000..cda19d2af0b
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/tuple_struct_unit.rs
@@ -0,0 +1,11 @@
+struct E();
+struct T(E,E,());
+
+fn main()
+{
+  let z0 = E();
+  let z1 = E();
+  let t = T(z0,z1,());
+  let z = t.2;
+  z
+}
diff --git a/gcc/testsuite/rust/compile/torture/tuple_struct_unused.rs b/gcc/testsuite/rust/compile/torture/tuple_struct_unused.rs
new file mode 100644
index 00000000000..8da0a50632b
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/tuple_struct_unused.rs
@@ -0,0 +1,4 @@
+struct Foo(i32, i32);
+// { dg-warning "struct is never constructed" "" { target *-*-* } .-1 }
+
+fn main() {}
diff --git a/gcc/testsuite/rust/compile/torture/type-alias1.rs b/gcc/testsuite/rust/compile/torture/type-alias1.rs
new file mode 100644
index 00000000000..78bf0461036
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/type-alias1.rs
@@ -0,0 +1,6 @@
+type TypeAlias = (i32, u32);
+
+fn main() {
+    let a: TypeAlias;
+    a = (123, 456);
+}
diff --git a/gcc/testsuite/rust/compile/torture/type-alias2.rs b/gcc/testsuite/rust/compile/torture/type-alias2.rs
new file mode 100644
index 00000000000..a3e3f4e6851
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/type-alias2.rs
@@ -0,0 +1,8 @@
+type x = u32;
+
+fn main() {
+    let x: x = 1;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let y: x = 2;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/type_infer1.rs b/gcc/testsuite/rust/compile/torture/type_infer1.rs
new file mode 100644
index 00000000000..3fee7282bde
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/type_infer1.rs
@@ -0,0 +1,24 @@
+struct Foo {
+    one: i32,
+// { dg-warning "field is never read" "" { target *-*-* } .-1 }
+    two: i32,
+// { dg-warning "field is never read" "" { target *-*-* } .-1 }
+}
+
+fn test(x: i32) -> i32 {
+    return x + 1;
+}
+
+fn main() {
+    let logical: bool = true;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let an_integer = 5;
+    let mut default_integer = 7;
+
+    default_integer = 1 + an_integer;
+
+    let call_test = test(1);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let struct_test = Foo { one: 1, two: 2 };
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/type_infer2.rs b/gcc/testsuite/rust/compile/torture/type_infer2.rs
new file mode 100644
index 00000000000..dcddda42729
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/type_infer2.rs
@@ -0,0 +1,9 @@
+fn test(x: i32) -> i32 {
+    return x + 1;
+}
+
+fn main() {
+    let an_integer = 5;
+    let call_test = test(an_integer + 1);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/type_infer3.rs b/gcc/testsuite/rust/compile/torture/type_infer3.rs
new file mode 100644
index 00000000000..303dd3d1f18
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/type_infer3.rs
@@ -0,0 +1,14 @@
+fn test(x: i32) -> i32 {
+    return x + 1;
+}
+
+fn main() {
+    let mut an_integer = 5;
+    an_integer = test(1) + 3;
+
+    let mut x;
+    x = 1;
+
+    let call_test = test(1);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/type_infer4.rs b/gcc/testsuite/rust/compile/torture/type_infer4.rs
new file mode 100644
index 00000000000..77a919f262a
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/type_infer4.rs
@@ -0,0 +1,11 @@
+fn main() {
+    let xs: [i32; 5] = [1, 2, 3, 4, 5];
+    let xy = [6, 7, 8];
+
+    let a = xs[0];
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let b = xy[2];
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let mut c;
+    c = xs[0];
+}
diff --git a/gcc/testsuite/rust/compile/torture/type_infer5.rs b/gcc/testsuite/rust/compile/torture/type_infer5.rs
new file mode 100644
index 00000000000..2c2602a161f
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/type_infer5.rs
@@ -0,0 +1,13 @@
+struct Foo {
+    a: i32,
+    b: i32,
+// { dg-warning "field is never read" "" { target *-*-* } .-1 }
+}
+
+fn main() {
+    let a;
+    a = Foo { a: 1, b: 2 };
+
+    let b = a.a;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/type_infer6.rs b/gcc/testsuite/rust/compile/torture/type_infer6.rs
new file mode 100644
index 00000000000..6f5863f0107
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/type_infer6.rs
@@ -0,0 +1,14 @@
+fn test(x: u32) -> u32 {
+    return x + 1;
+}
+
+fn main() {
+    let a;
+    a = 1;
+    let b = test(a);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    let c = 1;
+    let d = test(c + 1);
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/unary_operators.rs b/gcc/testsuite/rust/compile/torture/unary_operators.rs
new file mode 100644
index 00000000000..9be729c04e3
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/unary_operators.rs
@@ -0,0 +1,8 @@
+// { dg-prune-output "warning: unused name" } as there are many of these expected.
+
+fn main() {
+    let a: i32 = -1;
+    let b: i32 = 3 - -1;
+    let c: bool = !false;
+    let d: i32 = !3;
+}
diff --git a/gcc/testsuite/rust/compile/torture/undended-string-1.rs b/gcc/testsuite/rust/compile/torture/undended-string-1.rs
new file mode 100644
index 00000000000..66f0cd52269
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/undended-string-1.rs
@@ -0,0 +1,5 @@
+// { dg-excess-errors "...." }
+fn main() {
+    // { dg-error "unended string literal" "" { target *-*-* } .+1 }
+    let s = "123
+}
diff --git a/gcc/testsuite/rust/compile/torture/undended-string-2.rs b/gcc/testsuite/rust/compile/torture/undended-string-2.rs
new file mode 100644
index 00000000000..c0f424927c2
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/undended-string-2.rs
@@ -0,0 +1,5 @@
+// { dg-excess-errors "...." }
+fn main() {
+    // { dg-error "unended byte string literal" "" { target *-*-* } .+1 }
+    let s = b"123
+}
diff --git a/gcc/testsuite/rust/compile/torture/underscore_id.rs b/gcc/testsuite/rust/compile/torture/underscore_id.rs
new file mode 100644
index 00000000000..2c106c55df9
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/underscore_id.rs
@@ -0,0 +1,4 @@
+fn main() {
+    let _unused_but_fine = 12;
+    let unused = 12; // { dg-warning "unused name" }
+}
diff --git a/gcc/testsuite/rust/compile/torture/union.rs b/gcc/testsuite/rust/compile/torture/union.rs
new file mode 100644
index 00000000000..724168817ad
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/union.rs
@@ -0,0 +1,32 @@
+union U
+{
+  f1: u8
+}
+
+union V
+{
+  f1: u8,
+  f2: u16,
+  f3: i32,
+}
+
+struct S
+{
+  f1: U,
+  f2: V
+}
+
+fn main ()
+{
+  let u = U { f1: 16 };
+  let v = V { f2: 365 };
+  let s = S { f1: u, f2: v };
+  let _v125 = unsafe
+    { let mut uv: u64;
+      uv = s.f1.f1 as u64;
+      uv += s.f2.f1 as u64;
+      uv += s.f2.f2 as u64;
+      uv -= s.f2.f3 as u64;
+      uv
+    };
+}
diff --git a/gcc/testsuite/rust/compile/torture/union_union.rs b/gcc/testsuite/rust/compile/torture/union_union.rs
new file mode 100644
index 00000000000..9feb145a692
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/union_union.rs
@@ -0,0 +1,27 @@
+union union
+{
+  union: u32,
+  inion: i32,
+  u8ion: u8,
+  i64on: i64,
+  u64on: u64
+}
+
+pub fn main ()
+{
+  let union = union { union: 2 };
+  let inion = union { inion: -2 };
+  let mut mnion = union { inion: -16 };
+  let m1 = unsafe { mnion.union };
+  unsafe { mnion.union = union.union };
+  let m2 = unsafe { mnion.inion };
+  let u1 = unsafe { union.union };
+  let i1 = unsafe { union.inion };
+  let u2 = unsafe { inion.union };
+  let i2 = unsafe { inion.inion };
+  let _r1 = u2 - u1 - m1;
+  let _r2 = i1 + i2 + m2;
+  let _u8 = unsafe { union.u8ion };
+  let _i64 = unsafe { union.i64on };
+  let _u64 = unsafe { union.u64on };
+}
diff --git a/gcc/testsuite/rust/compile/torture/unit_type1.rs b/gcc/testsuite/rust/compile/torture/unit_type1.rs
new file mode 100644
index 00000000000..d5f73575745
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/unit_type1.rs
@@ -0,0 +1,7 @@
+fn main() {
+    let a: () = ();
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+
+    let b;
+    b = ();
+}
diff --git a/gcc/testsuite/rust/compile/torture/unit_type2.rs b/gcc/testsuite/rust/compile/torture/unit_type2.rs
new file mode 100644
index 00000000000..b5f925907d8
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/unit_type2.rs
@@ -0,0 +1,8 @@
+fn test(a: ()) -> () {
+    a
+}
+
+fn main() {
+    let a;
+    a = test(());
+}
diff --git a/gcc/testsuite/rust/compile/torture/unit_type3.rs b/gcc/testsuite/rust/compile/torture/unit_type3.rs
new file mode 100644
index 00000000000..d48a691046b
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/unit_type3.rs
@@ -0,0 +1,6 @@
+struct S;
+
+fn main() {
+    let s = S;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/unit_type4.rs b/gcc/testsuite/rust/compile/torture/unit_type4.rs
new file mode 100644
index 00000000000..ccbfa21818a
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/unit_type4.rs
@@ -0,0 +1,5 @@
+struct S;
+
+fn main() {
+    let _s = S {};
+}
diff --git a/gcc/testsuite/rust/compile/torture/unit_type5.rs b/gcc/testsuite/rust/compile/torture/unit_type5.rs
new file mode 100644
index 00000000000..3c4323aa73c
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/unit_type5.rs
@@ -0,0 +1,8 @@
+struct Foo;
+
+fn main() {
+    let a = Foo {};
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let b = Foo;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/unsafe1.rs b/gcc/testsuite/rust/compile/torture/unsafe1.rs
new file mode 100644
index 00000000000..f57ef52b9c0
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/unsafe1.rs
@@ -0,0 +1,12 @@
+fn test() -> i32 {
+    unsafe {
+        let a;
+        a = 123;
+        a
+    }
+}
+
+fn main() {
+    let a;
+    a = test();
+}
diff --git a/gcc/testsuite/rust/compile/torture/unsafe2.rs b/gcc/testsuite/rust/compile/torture/unsafe2.rs
new file mode 100644
index 00000000000..0b3aad53a44
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/unsafe2.rs
@@ -0,0 +1,4 @@
+fn main() {
+    unsafe {}
+    ()
+}
diff --git a/gcc/testsuite/rust/compile/torture/unsafe3.rs b/gcc/testsuite/rust/compile/torture/unsafe3.rs
new file mode 100644
index 00000000000..5f427258f60
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/unsafe3.rs
@@ -0,0 +1,9 @@
+pub fn test() -> i32 {
+    let a = unsafe { 123 };
+    a
+}
+
+pub fn main() {
+    let a = test();
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/unsafe4.rs b/gcc/testsuite/rust/compile/torture/unsafe4.rs
new file mode 100644
index 00000000000..6fe3101d49c
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/unsafe4.rs
@@ -0,0 +1,12 @@
+struct SS {
+    one: i32,
+    two: i32,
+}
+struct TS(i32, i32);
+
+fn main() {
+    unsafe {
+        let ss = SS { one: 1, two: 2 };
+        let _ts = TS(ss.one, ss.two);
+    };
+}
diff --git a/gcc/testsuite/rust/compile/torture/unused.rs b/gcc/testsuite/rust/compile/torture/unused.rs
new file mode 100644
index 00000000000..d95e6b01c00
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/unused.rs
@@ -0,0 +1,17 @@
+// { dg-warning "function is never used: 'bar'" "" { target *-*-* } .+1 }
+fn bar() {
+    foo();
+}
+
+// { dg-warning "function is never used: 'foo'" "" { target *-*-* } .+1 }
+fn foo() {
+    bar();
+}
+
+fn f() {
+
+}
+
+fn main() {
+    f();
+}
diff --git a/gcc/testsuite/rust/compile/torture/unused1.rs b/gcc/testsuite/rust/compile/torture/unused1.rs
new file mode 100644
index 00000000000..db7eb8fc1fc
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/unused1.rs
@@ -0,0 +1,15 @@
+fn test() -> i32 {
+    1
+}
+
+fn unused() -> i32 {
+    // { dg-warning "function is never used: 'unused'" "" { target *-*-* } .-1 }
+    2
+}
+
+fn main() {
+    let a = 1;
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+    let b = test();
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/unused_struct.rs b/gcc/testsuite/rust/compile/torture/unused_struct.rs
new file mode 100644
index 00000000000..ba9ec3250cf
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/unused_struct.rs
@@ -0,0 +1,7 @@
+struct Foo {
+    // { dg-warning "struct is never constructed" "" { target *-*-* } .-1 }
+    one: i32,
+    two: i32,
+}
+
+fn main() {}
diff --git a/gcc/testsuite/rust/compile/torture/unused_struct_field.rs b/gcc/testsuite/rust/compile/torture/unused_struct_field.rs
new file mode 100644
index 00000000000..429b3039da4
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/unused_struct_field.rs
@@ -0,0 +1,9 @@
+struct Foo {
+    one: i32,
+// { dg-warning "field is never read" "" { target *-*-* } .-1 }
+    two: i32
+}
+fn main() {
+    let _a = Foo {one: 1, two: 2};
+    let _b = _a.two;
+}
\ No newline at end of file
diff --git a/gcc/testsuite/rust/compile/torture/usize1.rs b/gcc/testsuite/rust/compile/torture/usize1.rs
new file mode 100644
index 00000000000..ba1d5c60320
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/usize1.rs
@@ -0,0 +1,6 @@
+fn main() {
+    let a = [1, 2, 3];
+    let b: usize = 1;
+    let c = a[b];
+    // { dg-warning "unused name" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/torture/very-broken-attr-string.rs b/gcc/testsuite/rust/compile/torture/very-broken-attr-string.rs
new file mode 100644
index 00000000000..832ba7b8ec9
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/very-broken-attr-string.rs
@@ -0,0 +1,3 @@
+// { dg-excess-errors "...." }
+// { dg-error "unended string literal" "" { target *-*-* } .+1 }
+#[doc(alias = "123
diff --git a/gcc/testsuite/rust/compile/torture/while_function.rs b/gcc/testsuite/rust/compile/torture/while_function.rs
new file mode 100644
index 00000000000..014db902769
--- /dev/null
+++ b/gcc/testsuite/rust/compile/torture/while_function.rs
@@ -0,0 +1,10 @@
+fn foo() {}
+fn bar() -> i32 { return 10; }
+
+fn main() {
+	let mut i = 1;
+	while i < bar() {
+		foo();
+		i += 1;
+	}
+}
diff --git a/gcc/testsuite/rust/compile/traits1.rs b/gcc/testsuite/rust/compile/traits1.rs
new file mode 100644
index 00000000000..355064eec1a
--- /dev/null
+++ b/gcc/testsuite/rust/compile/traits1.rs
@@ -0,0 +1,13 @@
+trait Foo {
+    fn Bar() -> i32 {}
+    // { dg-error "expected .i32. got .()." "" { target *-*-* } .-1 }
+}
+
+struct Baz;
+
+impl Foo for Baz {
+    fn Barrr() {}
+    // { dg-error "method .Barrr. is not a member of trait .Foo." "" { target *-*-* } .-1 }
+}
+
+fn main() {}
diff --git a/gcc/testsuite/rust/compile/traits10.rs b/gcc/testsuite/rust/compile/traits10.rs
new file mode 100644
index 00000000000..f8f551ba86d
--- /dev/null
+++ b/gcc/testsuite/rust/compile/traits10.rs
@@ -0,0 +1,15 @@
+struct Foo(i32);
+
+trait Bar {
+    const A: i32 = 123;
+    fn B();
+    fn C(&self);
+}
+
+pub fn main() {
+    let a;
+    a = Foo(123);
+
+    let b: &dyn Bar = &a;
+    // { dg-error "trait bound is not object safe" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/traits11.rs b/gcc/testsuite/rust/compile/traits11.rs
new file mode 100644
index 00000000000..d06e47d7e16
--- /dev/null
+++ b/gcc/testsuite/rust/compile/traits11.rs
@@ -0,0 +1,19 @@
+struct Foo(i32);
+
+trait A {
+    const A: i32 = 123;
+    fn B();
+    fn C(&self);
+}
+
+trait B: A {
+    fn test(&self);
+}
+
+pub fn main() {
+    let a;
+    a = Foo(123);
+
+    let b: &dyn B = &a;
+    // { dg-error "trait bound is not object safe" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/traits12.rs b/gcc/testsuite/rust/compile/traits12.rs
new file mode 100644
index 00000000000..25e0eb7aaa3
--- /dev/null
+++ b/gcc/testsuite/rust/compile/traits12.rs
@@ -0,0 +1,20 @@
+trait A<T> {
+    type Output;
+
+    fn test(self, a: &T) -> &Self::Output;
+}
+
+struct Foo<T> {
+    start: T,
+    end: T,
+}
+
+impl<T> A for Foo<usize> {
+    // { dg-error "generic item takes at least 1 type arguments but 0 were supplied" "" { target *-*-* } .-1 }
+    // { dg-error "unconstrained type parameter" "" { target *-*-* } .-2 }
+    type Output = T;
+
+    fn test(self, a: &T) -> &Self::Output {
+        a
+    }
+}
diff --git a/gcc/testsuite/rust/compile/traits2.rs b/gcc/testsuite/rust/compile/traits2.rs
new file mode 100644
index 00000000000..7357c22f7d6
--- /dev/null
+++ b/gcc/testsuite/rust/compile/traits2.rs
@@ -0,0 +1,14 @@
+trait Foo {
+    fn Bar() -> i32 {}
+    // { dg-error "expected .i32. got .()." "" { target *-*-* } .-1 }
+}
+
+struct Baz;
+
+impl Foo for Baz {
+    fn Bar() {}
+    // { dg-error "expected .i32. got .()." "" { target *-*-* } .-1 }
+    // { dg-error "method .Bar. has an incompatible type for trait .Foo." "" { target *-*-* } .-2 }
+}
+
+fn main() {}
diff --git a/gcc/testsuite/rust/compile/traits3.rs b/gcc/testsuite/rust/compile/traits3.rs
new file mode 100644
index 00000000000..fd3fa457cc8
--- /dev/null
+++ b/gcc/testsuite/rust/compile/traits3.rs
@@ -0,0 +1,22 @@
+trait Foo {
+    type A;
+
+    fn baz(a: Self::A) -> Self::A;
+}
+
+struct Bar<T>(T);
+
+impl<T> Foo for Bar<T> {
+    type A = i32;
+
+    fn baz(a: f32) -> f32 {
+        // { dg-error "method .baz. has an incompatible type for trait .Foo." "" { target *-*-* } .-1 }
+        a
+        // { dg-error "expected .i32. got .f32." "" { target *-*-* } .-1 }
+    }
+}
+
+fn main() {
+    let a;
+    a = Bar::<i32>::baz(123f32);
+}
diff --git a/gcc/testsuite/rust/compile/traits4.rs b/gcc/testsuite/rust/compile/traits4.rs
new file mode 100644
index 00000000000..486301d6136
--- /dev/null
+++ b/gcc/testsuite/rust/compile/traits4.rs
@@ -0,0 +1,16 @@
+trait Foo {
+    const A: i32;
+
+    fn test(self);
+}
+
+struct Bar;
+impl Foo for Bar {
+    // { dg-error "missing A in implementation of trait .Foo." "" { target *-*-* } .-1 }
+    fn test(self) {}
+}
+
+fn main() {
+    let a = Bar;
+    a.test();
+}
diff --git a/gcc/testsuite/rust/compile/traits5.rs b/gcc/testsuite/rust/compile/traits5.rs
new file mode 100644
index 00000000000..8b2fb9bc10d
--- /dev/null
+++ b/gcc/testsuite/rust/compile/traits5.rs
@@ -0,0 +1,9 @@
+trait Foo {
+    const A: i32;
+
+    fn test(self);
+}
+
+struct Bar;
+impl Foo for Bar {}
+// { dg-error "missing A, test in implementation of trait .Foo." "" { target *-*-* } .-1 }
diff --git a/gcc/testsuite/rust/compile/traits6.rs b/gcc/testsuite/rust/compile/traits6.rs
new file mode 100644
index 00000000000..3579b5a0842
--- /dev/null
+++ b/gcc/testsuite/rust/compile/traits6.rs
@@ -0,0 +1,15 @@
+trait Foo {
+    fn default() -> i32;
+}
+
+struct Bar(i32);
+
+fn type_bound_test<T: Foo>() -> i32 {
+    T::default()
+}
+
+fn main() {
+    let a;
+    a = type_bound_test::<Bar>();
+    // { dg-error "bounds not satisfied for Bar" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/traits7.rs b/gcc/testsuite/rust/compile/traits7.rs
new file mode 100644
index 00000000000..825553ce496
--- /dev/null
+++ b/gcc/testsuite/rust/compile/traits7.rs
@@ -0,0 +1,24 @@
+trait Foo {
+    fn default() -> i32;
+}
+
+trait Bar {
+    fn not_default() -> i32;
+}
+
+struct Test(i32);
+
+impl Foo for Test {
+    fn default() -> i32 {
+        1234
+    }
+}
+
+fn type_bound_test<T: Foo + Bar>() -> i32 {
+    T::default()
+}
+
+fn main() {
+    let a = type_bound_test::<Test>();
+    // { dg-error "bounds not satisfied for Test" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/traits8.rs b/gcc/testsuite/rust/compile/traits8.rs
new file mode 100644
index 00000000000..b22590a52bb
--- /dev/null
+++ b/gcc/testsuite/rust/compile/traits8.rs
@@ -0,0 +1,35 @@
+trait A {
+    fn get(self) -> f64;
+}
+
+trait B {
+    fn get(self) -> u8;
+}
+
+struct Foo(u8, f64);
+
+impl A for Foo {
+    fn get(self) -> f64 {
+        self.1
+    }
+}
+
+impl B for Foo {
+    fn get(self) -> u8 {
+        self.0
+    }
+}
+
+fn main() {
+    let _a;
+    _a = Foo(123, 456f64);
+
+    let _b: f64;
+    _b = <Foo as A>::get(_a);
+
+    let _a;
+    _a = Foo(123, 456f64);
+
+    let _c: u8;
+    _c = <Foo as B>::get(_a)
+}
diff --git a/gcc/testsuite/rust/compile/traits9.rs b/gcc/testsuite/rust/compile/traits9.rs
new file mode 100644
index 00000000000..e1aef5397fc
--- /dev/null
+++ b/gcc/testsuite/rust/compile/traits9.rs
@@ -0,0 +1,13 @@
+struct Foo(i32);
+trait Bar {
+    fn baz(&self);
+}
+
+fn main() {
+    let a;
+    a = Foo(123);
+
+    let b: &dyn Bar = &a;
+    // { dg-error "bounds not satisfied for Foo .Bar. is not satisfied" "" { target *-*-* } .-1 }
+    // { dg-error "expected" "" { target *-*-* } .-2 }
+}
diff --git a/gcc/testsuite/rust/compile/tuple1.rs b/gcc/testsuite/rust/compile/tuple1.rs
new file mode 100644
index 00000000000..84179b13727
--- /dev/null
+++ b/gcc/testsuite/rust/compile/tuple1.rs
@@ -0,0 +1,5 @@
+fn main() {
+    let a: (i32, bool) = (123, 123); // { dg-error "expected .bool. got .<integer>." }
+    let b;
+    b = (456, 5f32);
+}
diff --git a/gcc/testsuite/rust/compile/tuple_struct1.rs b/gcc/testsuite/rust/compile/tuple_struct1.rs
new file mode 100644
index 00000000000..2f4cb4a353b
--- /dev/null
+++ b/gcc/testsuite/rust/compile/tuple_struct1.rs
@@ -0,0 +1,8 @@
+struct Foo {
+    one: i32,
+    two: i32,
+}
+
+fn main() {
+    let a = Foo(1, 2); // { dg-error "expected function, tuple struct or tuple variant, found struct 'Foo'" }
+}
diff --git a/gcc/testsuite/rust/compile/tuple_struct2.rs b/gcc/testsuite/rust/compile/tuple_struct2.rs
new file mode 100644
index 00000000000..1fc18968577
--- /dev/null
+++ b/gcc/testsuite/rust/compile/tuple_struct2.rs
@@ -0,0 +1,5 @@
+struct Bar(i32, i32, bool);
+
+fn main() {
+    let a = Bar(1, 2); // { dg-error "unexpected number of arguments 2 expected 3" }
+}
diff --git a/gcc/testsuite/rust/compile/tuple_struct3.rs b/gcc/testsuite/rust/compile/tuple_struct3.rs
new file mode 100644
index 00000000000..4af66b8370e
--- /dev/null
+++ b/gcc/testsuite/rust/compile/tuple_struct3.rs
@@ -0,0 +1,6 @@
+struct Foo(i32, i32, bool);
+
+fn main() {
+    let c = Foo(1, 2f32, true);
+    // { dg-error "expected .i32. got .f32." "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/type-alias1.rs b/gcc/testsuite/rust/compile/type-alias1.rs
new file mode 100644
index 00000000000..c7d7048246a
--- /dev/null
+++ b/gcc/testsuite/rust/compile/type-alias1.rs
@@ -0,0 +1,6 @@
+type TypeAlias = (i32, u32);
+
+fn main() {
+    let a: TypeAlias;
+    a = (123, 456f32); // { dg-error "expected .u32. got .f32." }
+}
diff --git a/gcc/testsuite/rust/compile/type-bindings1.rs b/gcc/testsuite/rust/compile/type-bindings1.rs
new file mode 100644
index 00000000000..ad85ed97ecb
--- /dev/null
+++ b/gcc/testsuite/rust/compile/type-bindings1.rs
@@ -0,0 +1,10 @@
+struct Foo<A, B>(A, B);
+
+fn main() {
+    let a;
+    a = Foo::<A = i32, B = f32>(123f32);
+    // { dg-error "associated type bindings are not allowed here" "" { target *-*-* } .-1 }
+    // { dg-error {failed to type resolve expression} "" { target *-*-* } .-2 }
+    // { dg-error {Failed to resolve expression of function call} "" { target *-*-* } .-3 }
+    // { duplicate _dg-error {failed to type resolve expression} "" { target *-*-* } .-4 }
+}
diff --git a/gcc/testsuite/rust/compile/unary_negation.rs b/gcc/testsuite/rust/compile/unary_negation.rs
new file mode 100644
index 00000000000..848643f0bd2
--- /dev/null
+++ b/gcc/testsuite/rust/compile/unary_negation.rs
@@ -0,0 +1,9 @@
+fn main() {
+    let a: i32 = -1;
+    let b: i32 = 3 - -1;
+    let c: bool = !false;
+    let d: i32 = !3;
+
+    let e: f32 = -true; // // { dg-error "cannot apply unary - to bool" }
+    // { dg-error {failed to type resolve expression} "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/unary_not.rs b/gcc/testsuite/rust/compile/unary_not.rs
new file mode 100644
index 00000000000..b0a3dafbbd0
--- /dev/null
+++ b/gcc/testsuite/rust/compile/unary_not.rs
@@ -0,0 +1,9 @@
+fn main() {
+    let a: i32 = -1;
+    let b: i32 = 3 - -1;
+    let c: bool = !false;
+    let d: i32 = !3;
+
+    let e: f32 = !5f32; // { dg-error "cannot apply unary '!' to f32" }
+    // { dg-error {failed to type resolve expression} "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/unconstrained_type_param.rs b/gcc/testsuite/rust/compile/unconstrained_type_param.rs
new file mode 100644
index 00000000000..c4aaa10146f
--- /dev/null
+++ b/gcc/testsuite/rust/compile/unconstrained_type_param.rs
@@ -0,0 +1,12 @@
+struct Foo<T>(T, bool);
+
+impl<X, Y> Foo<X> {
+    // { dg-error "unconstrained type parameter" "" { target *-*-* } .-1 }
+    fn test() -> Y {
+        123
+    }
+}
+
+fn main() {
+    let a = Foo::test();
+}
diff --git a/gcc/testsuite/rust/compile/unicode_escape.rs b/gcc/testsuite/rust/compile/unicode_escape.rs
new file mode 100644
index 00000000000..39b91d8a95c
--- /dev/null
+++ b/gcc/testsuite/rust/compile/unicode_escape.rs
@@ -0,0 +1,60 @@
+fn main ()
+{
+  // Braces are required
+  let _cbl = '\u013'; // { dg-error "unicode escape" }
+  let _sbl = "\u013"; //{ dg-error "unicode escape" }
+
+  // One to six hex digits
+  let _c0 = '\u{}'; // { dg-error "unicode escape" }
+  let _c1 = '\u{0}';
+  let _c2 = '\u{00}';
+  let _c3 = '\u{000}';
+  let _c4 = '\u{0000}';
+  let _c5 = '\u{00000}';
+  let _c6 = '\u{000000}';
+  let _c7 = '\u{0000000}'; // { dg-error "unicode escape" }
+
+  let _s0 = "\u{}"; // { dg-error "unicode escape" }
+  let _s1 = "\u{0}";
+  let _s2 = "\u{00}";
+  let _s3 = "\u{000}";
+  let _s4 = "\u{0000}";
+  let _s5 = "\u{00000}";
+  let _s6 = "\u{000000}";
+  let _s7 = "\u{0000000}"; // { dg-error "unicode escape" }
+
+  // Underscores OK except for start
+  let _c_ = '\u{00___01__0_1_}';
+  let _s_ = "\u{00___01__0_1_}";
+  let _c__ = '\u{_00__01__0_}'; // { dg-error "unicode escape" }
+  let _s__ = "\u{_00__01__0_}"; // { dg-error "unicode escape" }
+
+  // Must be hex chars
+  let _chex = '\u{hex}';  // { dg-error "unicode escape" }
+  let _shex = '\u{hex}';  // { dg-error "unicode escape" }
+
+  // Only valid from 0x0 to 0xD7FF and from 0xE000 to 0x10FFF
+  let _cd7ff = '\u{D7FF}';
+  let _sd7ff = "\u{D7FF}";
+  let _cd800 = '\u{D800}'; // { dg-error "unicode escape" }
+  let _sd800 = "\u{D800}"; // { dg-error "unicode escape" }
+
+  let _cdfff = '\u{DFFF}'; // { dg-error "unicode escape" }
+  let _sdfff = "\u{DFFF}"; // { dg-error "unicode escape" }
+  let _ce000 = '\u{E000}';
+  let _se000 = "\u{E000}";
+
+  let _clast = '\u{10FFFF}';
+  let _slast = "\u{10FFFF}";
+  let _clast1 = '\u{110000}'; // { dg-error "unicode escape" }
+  let _slast1 = "\u{110000}"; // { dg-error "unicode escape" }
+
+  let _cffffff = '\u{FFFFFF}'; // { dg-error "unicode escape" }
+  let _sffffff = "\u{FFFFFF}"; // { dg-error "unicode escape" }
+
+  // unicode escapes cannot be used in bytes or byte strings.
+  // Except in raw byte strings (where they aren't escapes).
+  let _bc = b'\u{000A}'; // { dg-error "unicode escape" }
+  let _bs = b"\u{000A}"; // { dg-error "unicode escape" }
+  let _rbs = br"\u{000A}";
+}
diff --git a/gcc/testsuite/rust/compile/unsafe1.rs b/gcc/testsuite/rust/compile/unsafe1.rs
new file mode 100644
index 00000000000..9cd3f6b4bf2
--- /dev/null
+++ b/gcc/testsuite/rust/compile/unsafe1.rs
@@ -0,0 +1,14 @@
+fn foo(_a: &i32) {}
+fn bar(_a: i32) {}
+
+static mut a: i32 = 15;
+
+fn main() {
+    foo(&a); // { dg-error "use of mutable static" }
+    bar(a); // { dg-error "use of mutable static" }
+
+    unsafe {
+        foo(&a);
+        bar(a);
+    }
+}
diff --git a/gcc/testsuite/rust/compile/unsafe10.rs b/gcc/testsuite/rust/compile/unsafe10.rs
new file mode 100644
index 00000000000..6b6be06b908
--- /dev/null
+++ b/gcc/testsuite/rust/compile/unsafe10.rs
@@ -0,0 +1,12 @@
+extern "rust-intrinsic" {
+    pub fn rotate_left<T>(l: T, r: T) -> T;
+}
+
+fn main() -> i32 {
+    let a = 15;
+    let b = 15;
+
+    let _ = rotate_left(a, b);
+
+    0
+}
diff --git a/gcc/testsuite/rust/compile/unsafe2.rs b/gcc/testsuite/rust/compile/unsafe2.rs
new file mode 100644
index 00000000000..e03e4bc59f4
--- /dev/null
+++ b/gcc/testsuite/rust/compile/unsafe2.rs
@@ -0,0 +1,16 @@
+fn foo(_a: &i32) {}
+fn bar(_a: i32) {}
+
+mod inner {
+    pub static mut a: i32 = 15;
+}
+
+fn main() {
+    foo(&inner::a); // { dg-error "use of mutable static" }
+    bar(inner::a); // { dg-error "use of mutable static" }
+
+    unsafe {
+        foo(&inner::a);
+        bar(inner::a);
+    }
+}
diff --git a/gcc/testsuite/rust/compile/unsafe3.rs b/gcc/testsuite/rust/compile/unsafe3.rs
new file mode 100644
index 00000000000..56aec76008a
--- /dev/null
+++ b/gcc/testsuite/rust/compile/unsafe3.rs
@@ -0,0 +1,10 @@
+extern "C" {
+    static VALUE: char;
+}
+
+fn main() {
+    let _ = VALUE; // { dg-error "use of extern static" }
+    unsafe {
+        let _ = VALUE;
+    }
+}
diff --git a/gcc/testsuite/rust/compile/unsafe4.rs b/gcc/testsuite/rust/compile/unsafe4.rs
new file mode 100644
index 00000000000..7d1356bc3e5
--- /dev/null
+++ b/gcc/testsuite/rust/compile/unsafe4.rs
@@ -0,0 +1,29 @@
+fn foo() -> i32 {
+    let a = 15;
+    let p_a = &a as *const i32;
+
+    unsafe { *p_a }
+}
+
+unsafe fn bar() -> i32 {
+    let a = 15;
+    let p_a = &a as *const i32;
+
+    *p_a
+}
+
+fn baz() -> i32 {
+    let a = 15;
+    let p_a = &a as *const i32;
+
+    *p_a // { dg-error "dereference of raw pointer" }
+}
+
+unsafe fn qux() -> i32 {
+    let a = 15;
+    let p_a = &a as *const i32;
+
+    unsafe {}
+
+    *p_a
+}
diff --git a/gcc/testsuite/rust/compile/unsafe5.rs b/gcc/testsuite/rust/compile/unsafe5.rs
new file mode 100644
index 00000000000..35990f6ee9f
--- /dev/null
+++ b/gcc/testsuite/rust/compile/unsafe5.rs
@@ -0,0 +1,4 @@
+fn main() {
+    let b = 15;
+    let c = *(&b as *const i32); // { dg-error "dereference of raw pointer" }
+}
diff --git a/gcc/testsuite/rust/compile/unsafe6.rs b/gcc/testsuite/rust/compile/unsafe6.rs
new file mode 100644
index 00000000000..cf4b75456e4
--- /dev/null
+++ b/gcc/testsuite/rust/compile/unsafe6.rs
@@ -0,0 +1,14 @@
+unsafe fn foo() {}
+unsafe fn bar() {
+    foo();
+}
+
+fn main() {
+    foo(); // { dg-error "call to unsafe function" }
+    bar(); // { dg-error "call to unsafe function" }
+
+    unsafe {
+        foo();
+        bar();
+    }
+}
diff --git a/gcc/testsuite/rust/compile/unsafe7.rs b/gcc/testsuite/rust/compile/unsafe7.rs
new file mode 100644
index 00000000000..a6b69e1ac37
--- /dev/null
+++ b/gcc/testsuite/rust/compile/unsafe7.rs
@@ -0,0 +1,9 @@
+extern "C" {
+    fn printf(fmt: *const i8, ...);
+}
+
+fn main() {
+    let s = "hey\0";
+
+    printf(s as *const str as *const i8); // { dg-error "call to extern function" }
+}
diff --git a/gcc/testsuite/rust/compile/unsafe8.rs b/gcc/testsuite/rust/compile/unsafe8.rs
new file mode 100644
index 00000000000..03fe4910c97
--- /dev/null
+++ b/gcc/testsuite/rust/compile/unsafe8.rs
@@ -0,0 +1,14 @@
+struct S;
+
+impl S {
+    unsafe fn foo(self) {}
+}
+
+fn main() {
+    let s = S;
+    s.foo(); // { dg-error "call to unsafe method" }
+
+    unsafe {
+        s.foo();
+    }
+}
diff --git a/gcc/testsuite/rust/compile/unsafe9.rs b/gcc/testsuite/rust/compile/unsafe9.rs
new file mode 100644
index 00000000000..fb46c8ffde3
--- /dev/null
+++ b/gcc/testsuite/rust/compile/unsafe9.rs
@@ -0,0 +1,10 @@
+union U {
+    a: i32,
+    b: f32,
+    c: u8,
+}
+
+fn main() {
+    let u = U { a: 14 };
+    let _ = u.a; // { dg-error "access to union" }
+}
diff --git a/gcc/testsuite/rust/compile/unterminated_c_comment.rs b/gcc/testsuite/rust/compile/unterminated_c_comment.rs
new file mode 100644
index 00000000000..619b7b7024d
--- /dev/null
+++ b/gcc/testsuite/rust/compile/unterminated_c_comment.rs
@@ -0,0 +1,2 @@
+// { dg-error "unexpected EOF while looking for end of comment" "" { target *-*-* } .+1 }
+/* This  comment needs closure :) !
diff --git a/gcc/testsuite/rust/compile/use_1.rs b/gcc/testsuite/rust/compile/use_1.rs
new file mode 100644
index 00000000000..94b96321a63
--- /dev/null
+++ b/gcc/testsuite/rust/compile/use_1.rs
@@ -0,0 +1,16 @@
+mod frob {}
+
+use foo::bar::baz; // { dg-error "cannot find simple path segment .foo." }
+use frob::ulator; // { dg-error "cannot find simple path segment .ulator." }
+
+mod sain {
+    mod doux {}
+
+    mod dron {}
+}
+
+use not_sain::*; // { dg-error "cannot find simple path segment .not_sain." }
+
+use sain::*;
+use sain::{doux, dron};
+use sain::{doux, dron, graal}; // { dg-error "cannot find simple path segment .graal." }
diff --git a/gcc/testsuite/rust/compile/usize1.rs b/gcc/testsuite/rust/compile/usize1.rs
new file mode 100644
index 00000000000..b1c8fe86473
--- /dev/null
+++ b/gcc/testsuite/rust/compile/usize1.rs
@@ -0,0 +1,6 @@
+fn main() {
+    let a = [1, 2, 3];
+    let b: u32 = 1;
+    let c = a[b]; // { dg-error "the type ...integer..CAPACITY.. cannot be indexed by .u32." }
+                  // { dg-error {failed to type resolve expression} "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/rust/compile/xfail/lifetime_param.rs b/gcc/testsuite/rust/compile/xfail/lifetime_param.rs
new file mode 100644
index 00000000000..306fca0b3c5
--- /dev/null
+++ b/gcc/testsuite/rust/compile/xfail/lifetime_param.rs
@@ -0,0 +1,11 @@
+// { dg-excess-errors "warnings" }
+
+// { dg-error "lifetime not defined" "#359" { xfail *-*-* } .+1 }
+fn lifetime_undefined(t: &'a str) -> &'a str {
+    t
+}
+
+// { dg-error "lifetime not defined" "#359" { xfail *-*-* } .+1 }
+fn lifetime_undefined_bis<'a>(t: &'a str)-> &'b str {
+    t
+}
diff --git a/gcc/testsuite/rust/compile/xfail/struct_field_vis.rs b/gcc/testsuite/rust/compile/xfail/struct_field_vis.rs
new file mode 100644
index 00000000000..a28729e6208
--- /dev/null
+++ b/gcc/testsuite/rust/compile/xfail/struct_field_vis.rs
@@ -0,0 +1,15 @@
+// { dg-xfail-if "pub visibility not supported #432"  *-*-* }
+
+mod foomod {
+    pub struct Foo {
+        pub f: i32,
+        pub g: u32,
+    }
+}
+
+fn test() -> foomod::Foo {
+    foomod::Foo{
+        f:1,
+        g:3,
+    }
+}
diff --git a/gcc/testsuite/rust/compile/xfail/xfail.exp b/gcc/testsuite/rust/compile/xfail/xfail.exp
new file mode 100644
index 00000000000..5fb6203bb31
--- /dev/null
+++ b/gcc/testsuite/rust/compile/xfail/xfail.exp
@@ -0,0 +1,63 @@
+# Copyright (C) 2021-2022 Free Software Foundation, Inc.
+
+# This program 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 of the License, or
+# (at your option) any later version.
+# 
+# This program 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/>.
+
+# Compile tests, no torture testing.
+#
+# These tests are used to keep track of known limitations :
+# 1- tests that are incorrecly build instead of being rejected
+# 2- tests that are build to an incorrect result
+# 3- tests that are rejected instead of being correctly build
+#
+# Not currently supported here:
+# - tests that are exhibiting incorrect behavior at runtime
+#
+# Here's how to annotate tests for each cases:
+#
+# 1- test is successfuly build instead of being rejected
+#
+# Expected behavior: a specific error rejecting the test
+# Observed behavior: error not present
+# Use dg-error and mark the test xfail and add reference to corresponding issue.
+# { dg-error "lifetime not defined" "#359" { xfail *-*-* } }
+#
+# 2- test is successfuly build but result is incorrect
+#
+# Expected behavior: test is correctly build and has specific property
+# Observed behavior: test is correctly build but is missing the specific property
+# Depends on the property. For example, if the property can be checked in the assembly file, use dg-final + xfail.
+# { dg-final { scan-assembler "given_string_missing_in_assembly_" "#1234" { xfail *-*-* } } }
+#
+# 3- test is rejected instead of being correctly build
+#
+# Expected behavior: test is successfully build
+# Observed behavior: the test is rejected with an error
+# Use dg-bogus + xfail to match the bogus error message, or use dg-xfail-if if it's harder to match a specific error.
+
+# Load support procs.
+load_lib rust-dg.exp
+
+# Initialize `dg'.
+dg-init
+
+# Main loop.
+set saved-dg-do-what-default ${dg-do-what-default}
+
+set dg-do-what-default "compile"
+dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.rs]] "" ""
+set dg-do-what-default ${saved-dg-do-what-default}
+
+# All done.
+dg-finish
-- 
2.25.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [PATCH Rust front-end v2 06/37] gccrs: Add execution test cases
  2022-08-24 11:59 Rust frontend patches v2 herron.philip
                   ` (4 preceding siblings ...)
  2022-08-24 11:59 ` [PATCH Rust front-end v2 05/37] gccrs: Add general compilation test cases herron.philip
@ 2022-08-24 11:59 ` herron.philip
  2022-08-24 11:59 ` [PATCH Rust front-end v2 07/37] gccrs: Add gcc-check-target check-rust herron.philip
                   ` (31 subsequent siblings)
  37 siblings, 0 replies; 59+ messages in thread
From: herron.philip @ 2022-08-24 11:59 UTC (permalink / raw)
  To: gcc-patches
  Cc: gcc-rust, Philip Herron, Arthur Cohen, Thomas Schwinge,
	Mark Wielaard, Marc Poulhiès

From: Philip Herron <philip.herron@embecosm.com>

This is similar to the compile/torture/*.rs test cases but all of these are
dg-execute testcases so they get compiled, linked and executed by default,
all the while being compiled with the matrix of torture options.

The only caveat here is that currently gccrs does not currently support
the main shim yet so we have a C-style main function here returning zero
which is not supported in Rustc.

Co-authored-by: Arthur Cohen <arthur.cohen@embecosm.com>
Co-authored-by: Thomas Schwinge <thomas@codesourcery.com>
Co-authored-by: Mark Wielaard <mark@klomp.org>
Co-authored-by: Marc Poulhiès <dkm@kataplop.net>
---
 .../rust/execute/torture/block_expr1.rs       |   8 +
 .../rust/execute/torture/builtin_macro_cfg.rs |  32 ++++
 .../execute/torture/builtin_macro_concat.rs   |  29 +++
 .../rust/execute/torture/builtin_macro_env.rs |  31 ++++
 .../torture/builtin_macro_include_bytes.rs    |  46 +++++
 .../torture/builtin_macro_include_str.rs      |  27 +++
 .../execute/torture/builtin_macro_line.rs     |  25 +++
 .../rust/execute/torture/builtin_macros1.rs   |  21 +++
 .../rust/execute/torture/builtin_macros3.rs   |  28 +++
 gcc/testsuite/rust/execute/torture/cfg1.rs    |  32 ++++
 gcc/testsuite/rust/execute/torture/cfg2.rs    |  31 ++++
 gcc/testsuite/rust/execute/torture/cfg3.rs    |  37 ++++
 gcc/testsuite/rust/execute/torture/cfg4.rs    |  38 ++++
 gcc/testsuite/rust/execute/torture/cfg5.rs    |  13 ++
 .../rust/execute/torture/coercion1.rs         |  41 +++++
 .../rust/execute/torture/coercion2.rs         |  39 ++++
 .../rust/execute/torture/const_fold1.rs       |  13 ++
 .../rust/execute/torture/const_fold2.rs       |  16 ++
 .../execute/torture/copy_nonoverlapping1.rs   |  17 ++
 .../rust/execute/torture/empty_main.rs        |   3 +
 .../rust/execute/torture/execute.exp          |  33 ++++
 .../rust/execute/torture/exit_error.rs        |   5 +
 .../rust/execute/torture/extern_mod4.rs       |  19 ++
 gcc/testsuite/rust/execute/torture/func1.rs   |   5 +
 .../rust/execute/torture/helloworld1.rs       |  15 ++
 .../rust/execute/torture/helloworld2.rs       |  15 ++
 .../rust/execute/torture/include.txt          |   1 +
 gcc/testsuite/rust/execute/torture/index1.rs  |  28 +++
 .../rust/execute/torture/issue-1120.rs        | 123 +++++++++++++
 .../rust/execute/torture/issue-1133.rs        | 146 +++++++++++++++
 .../rust/execute/torture/issue-1198.rs        |  75 ++++++++
 .../rust/execute/torture/issue-1231.rs        |  36 ++++
 .../rust/execute/torture/issue-1232.rs        | 159 ++++++++++++++++
 .../rust/execute/torture/issue-1249.rs        |  39 ++++
 .../rust/execute/torture/issue-1436.rs        | 172 ++++++++++++++++++
 .../rust/execute/torture/issue-1496.rs        |  75 ++++++++
 .../rust/execute/torture/issue-647.rs         |  33 ++++
 .../rust/execute/torture/issue-845.rs         |  47 +++++
 .../rust/execute/torture/issue-851.rs         |  35 ++++
 .../rust/execute/torture/issue-858.rs         |  32 ++++
 .../rust/execute/torture/issue-976.rs         |  14 ++
 .../rust/execute/torture/issue-995.rs         |   9 +
 gcc/testsuite/rust/execute/torture/macros1.rs |  13 ++
 .../rust/execute/torture/macros10.rs          |  22 +++
 .../rust/execute/torture/macros11.rs          |  25 +++
 .../rust/execute/torture/macros12.rs          |  22 +++
 .../rust/execute/torture/macros13.rs          |  22 +++
 .../rust/execute/torture/macros14.rs          |  22 +++
 .../rust/execute/torture/macros16.rs          |  14 ++
 .../rust/execute/torture/macros17.rs          |  17 ++
 .../rust/execute/torture/macros18.rs          |  14 ++
 .../rust/execute/torture/macros19.rs          |  14 ++
 gcc/testsuite/rust/execute/torture/macros2.rs |  40 ++++
 .../rust/execute/torture/macros20.rs          |  14 ++
 .../rust/execute/torture/macros21.rs          |  15 ++
 .../rust/execute/torture/macros22.rs          |  27 +++
 .../rust/execute/torture/macros23.rs          |  19 ++
 .../rust/execute/torture/macros24.rs          |   9 +
 .../rust/execute/torture/macros25.rs          |  13 ++
 .../rust/execute/torture/macros26.rs          |  12 ++
 .../rust/execute/torture/macros27.rs          |  24 +++
 .../rust/execute/torture/macros28.rs          |  13 ++
 .../rust/execute/torture/macros29.rs          |  24 +++
 gcc/testsuite/rust/execute/torture/macros3.rs |  61 +++++++
 .../rust/execute/torture/macros30.rs          |  25 +++
 .../rust/execute/torture/macros31.rs          |  32 ++++
 gcc/testsuite/rust/execute/torture/macros4.rs |  15 ++
 gcc/testsuite/rust/execute/torture/macros5.rs |  13 ++
 gcc/testsuite/rust/execute/torture/macros6.rs |  12 ++
 gcc/testsuite/rust/execute/torture/macros7.rs |  28 +++
 gcc/testsuite/rust/execute/torture/macros8.rs |  27 +++
 gcc/testsuite/rust/execute/torture/macros9.rs |  28 +++
 gcc/testsuite/rust/execute/torture/match1.rs  |  58 ++++++
 gcc/testsuite/rust/execute/torture/match2.rs  |  41 +++++
 gcc/testsuite/rust/execute/torture/match3.rs  |  51 ++++++
 .../rust/execute/torture/match_bool1.rs       |  49 +++++
 .../rust/execute/torture/match_byte1.rs       |  56 ++++++
 .../rust/execute/torture/match_char1.rs       |  56 ++++++
 .../rust/execute/torture/match_int1.rs        | 109 +++++++++++
 .../rust/execute/torture/match_loop1.rs       |  56 ++++++
 .../rust/execute/torture/match_range1.rs      |  37 ++++
 .../rust/execute/torture/match_range2.rs      |  45 +++++
 .../rust/execute/torture/match_tuple1.rs      |  45 +++++
 gcc/testsuite/rust/execute/torture/method1.rs |  27 +++
 gcc/testsuite/rust/execute/torture/method2.rs |  76 ++++++++
 gcc/testsuite/rust/execute/torture/method3.rs |  78 ++++++++
 gcc/testsuite/rust/execute/torture/method4.rs |  78 ++++++++
 gcc/testsuite/rust/execute/torture/mod1.rs    |  21 +++
 .../rust/execute/torture/modules/mod.rs       |   3 +
 .../execute/torture/operator_overload_1.rs    |  36 ++++
 .../execute/torture/operator_overload_10.rs   |  75 ++++++++
 .../execute/torture/operator_overload_11.rs   |  37 ++++
 .../execute/torture/operator_overload_12.rs   |  31 ++++
 .../execute/torture/operator_overload_2.rs    |  38 ++++
 .../execute/torture/operator_overload_3.rs    |  55 ++++++
 .../execute/torture/operator_overload_4.rs    |  33 ++++
 .../execute/torture/operator_overload_5.rs    |  33 ++++
 .../execute/torture/operator_overload_6.rs    |  37 ++++
 .../execute/torture/operator_overload_7.rs    |  42 +++++
 .../execute/torture/operator_overload_8.rs    |  58 ++++++
 .../execute/torture/operator_overload_9.rs    |  58 ++++++
 .../rust/execute/torture/slice-magic.rs       | 106 +++++++++++
 .../rust/execute/torture/slice-magic2.rs      | 106 +++++++++++
 gcc/testsuite/rust/execute/torture/slice1.rs  |  27 +++
 .../rust/execute/torture/str-layout1.rs       |  57 ++++++
 .../rust/execute/torture/str-zero.rs          |  26 +++
 gcc/testsuite/rust/execute/torture/trait1.rs  |  52 ++++++
 gcc/testsuite/rust/execute/torture/trait10.rs |  41 +++++
 gcc/testsuite/rust/execute/torture/trait11.rs |  38 ++++
 gcc/testsuite/rust/execute/torture/trait12.rs |  38 ++++
 gcc/testsuite/rust/execute/torture/trait13.rs |  48 +++++
 gcc/testsuite/rust/execute/torture/trait2.rs  |  37 ++++
 gcc/testsuite/rust/execute/torture/trait3.rs  |  43 +++++
 gcc/testsuite/rust/execute/torture/trait4.rs  |  34 ++++
 gcc/testsuite/rust/execute/torture/trait5.rs  |  39 ++++
 gcc/testsuite/rust/execute/torture/trait6.rs  |  39 ++++
 gcc/testsuite/rust/execute/torture/trait7.rs  |  39 ++++
 gcc/testsuite/rust/execute/torture/trait8.rs  |  39 ++++
 gcc/testsuite/rust/execute/torture/trait9.rs  |  35 ++++
 .../rust/execute/torture/transmute1.rs        |  23 +++
 .../rust/execute/torture/wrapping_op1.rs      |  14 ++
 .../rust/execute/torture/wrapping_op2.rs      |  20 ++
 gcc/testsuite/rust/execute/xfail/macro1.rs    |  32 ++++
 123 files changed, 4631 insertions(+)
 create mode 100644 gcc/testsuite/rust/execute/torture/block_expr1.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/builtin_macro_cfg.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/builtin_macro_concat.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/builtin_macro_env.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/builtin_macro_include_bytes.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/builtin_macro_include_str.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/builtin_macro_line.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/builtin_macros1.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/builtin_macros3.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/cfg1.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/cfg2.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/cfg3.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/cfg4.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/cfg5.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/coercion1.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/coercion2.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/const_fold1.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/const_fold2.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/copy_nonoverlapping1.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/empty_main.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/execute.exp
 create mode 100644 gcc/testsuite/rust/execute/torture/exit_error.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/extern_mod4.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/func1.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/helloworld1.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/helloworld2.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/include.txt
 create mode 100644 gcc/testsuite/rust/execute/torture/index1.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/issue-1120.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/issue-1133.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/issue-1198.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/issue-1231.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/issue-1232.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/issue-1249.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/issue-1436.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/issue-1496.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/issue-647.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/issue-845.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/issue-851.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/issue-858.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/issue-976.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/issue-995.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/macros1.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/macros10.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/macros11.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/macros12.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/macros13.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/macros14.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/macros16.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/macros17.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/macros18.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/macros19.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/macros2.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/macros20.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/macros21.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/macros22.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/macros23.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/macros24.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/macros25.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/macros26.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/macros27.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/macros28.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/macros29.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/macros3.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/macros30.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/macros31.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/macros4.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/macros5.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/macros6.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/macros7.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/macros8.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/macros9.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/match1.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/match2.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/match3.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/match_bool1.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/match_byte1.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/match_char1.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/match_int1.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/match_loop1.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/match_range1.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/match_range2.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/match_tuple1.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/method1.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/method2.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/method3.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/method4.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/mod1.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/modules/mod.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/operator_overload_1.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/operator_overload_10.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/operator_overload_11.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/operator_overload_12.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/operator_overload_2.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/operator_overload_3.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/operator_overload_4.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/operator_overload_5.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/operator_overload_6.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/operator_overload_7.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/operator_overload_8.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/operator_overload_9.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/slice-magic.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/slice-magic2.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/slice1.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/str-layout1.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/str-zero.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/trait1.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/trait10.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/trait11.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/trait12.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/trait13.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/trait2.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/trait3.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/trait4.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/trait5.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/trait6.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/trait7.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/trait8.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/trait9.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/transmute1.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/wrapping_op1.rs
 create mode 100644 gcc/testsuite/rust/execute/torture/wrapping_op2.rs
 create mode 100644 gcc/testsuite/rust/execute/xfail/macro1.rs

diff --git a/gcc/testsuite/rust/execute/torture/block_expr1.rs b/gcc/testsuite/rust/execute/torture/block_expr1.rs
new file mode 100644
index 00000000000..d561f8cab59
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/block_expr1.rs
@@ -0,0 +1,8 @@
+fn main() -> i32 {
+    let ret = {
+        1;
+        2;
+        0
+    };
+    ret
+}
diff --git a/gcc/testsuite/rust/execute/torture/builtin_macro_cfg.rs b/gcc/testsuite/rust/execute/torture/builtin_macro_cfg.rs
new file mode 100644
index 00000000000..fad2daef6bc
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/builtin_macro_cfg.rs
@@ -0,0 +1,32 @@
+// { dg-additional-options "-w -frust-cfg=A" }
+// { dg-output "A\n" }
+#[rustc_builtin_macro]
+macro_rules! cfg {
+    () => {{}};
+}
+
+extern "C" {
+    fn printf(fmt: *const i8, ...);
+}
+
+fn print(s: &str) {
+    unsafe {
+        printf(
+            "%s\n" as *const str as *const i8,
+            s as *const str as *const i8,
+        );
+    }
+}
+
+fn main() -> i32 {
+    let cfg = cfg!(A);
+    if cfg {
+        print("A");
+    }
+    let cfg = cfg!(B);
+    if cfg {
+        print("B");
+    }
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/builtin_macro_concat.rs b/gcc/testsuite/rust/execute/torture/builtin_macro_concat.rs
new file mode 100644
index 00000000000..9b33924f5a1
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/builtin_macro_concat.rs
@@ -0,0 +1,29 @@
+// { dg-output "\ntest10btrue2.15\ntest10bfalse2.151\n" }
+#[rustc_builtin_macro]
+macro_rules! concat {
+    () => {{}};
+}
+
+extern "C" {
+    fn printf(fmt: *const i8, ...);
+}
+
+fn print(s: &str) {
+    unsafe {
+        printf(
+            "%s\n" as *const str as *const i8,
+            s as *const str as *const i8,
+        );
+    }
+}
+
+fn main() -> i32 {
+    let a = concat!();
+    let b = concat!("test", 10, 'b', true, 2.15);
+    let c = concat!("test", 10, 'b', false, 2.15, 1u64);
+    print(a);
+    print(b);
+    print(c);
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/builtin_macro_env.rs b/gcc/testsuite/rust/execute/torture/builtin_macro_env.rs
new file mode 100644
index 00000000000..a5c80b25728
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/builtin_macro_env.rs
@@ -0,0 +1,31 @@
+// { dg-output "VALUE\nVALUE\n" }
+// { dg-set-compiler-env-var ENV_MACRO_TEST "VALUE" }
+#[rustc_builtin_macro]
+macro_rules! env {
+    () => {{}};
+}
+
+extern "C" {
+    fn printf(fmt: *const i8, ...);
+}
+
+fn print(s: &str) {
+    unsafe {
+        printf(
+            "%s\n" as *const str as *const i8,
+            s as *const str as *const i8,
+        );
+    }
+}
+
+fn main() -> i32 {
+    let val0 = env!("ENV_MACRO_TEST");
+
+    print(val0);
+
+    let val1 = env!("ENV_MACRO_TEST",);
+
+    print(val1);
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/builtin_macro_include_bytes.rs b/gcc/testsuite/rust/execute/torture/builtin_macro_include_bytes.rs
new file mode 100644
index 00000000000..087f0220de5
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/builtin_macro_include_bytes.rs
@@ -0,0 +1,46 @@
+// { dg-output "104\n33\n1\n" }
+#[rustc_builtin_macro]
+macro_rules! include_bytes {
+    () => {{}};
+}
+
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+fn print_int(value: i32) {
+    let s = "%d\n\0" as *const str as *const i8;
+    unsafe {
+        printf(s, value);
+    }
+}
+
+fn main() -> i32 {
+    let bytes = include_bytes!("include.txt");
+
+    print_int(bytes[0] as i32);
+    print_int(bytes[14] as i32);
+
+    let the_bytes = b"hello, include!\n";
+
+    let x = bytes[0] == the_bytes[0]
+        && bytes[1] == the_bytes[1]
+        && bytes[2] == the_bytes[2]
+        && bytes[3] == the_bytes[3]
+        && bytes[4] == the_bytes[4]
+        && bytes[5] == the_bytes[5]
+        && bytes[6] == the_bytes[6]
+        && bytes[7] == the_bytes[7]
+        && bytes[8] == the_bytes[8]
+        && bytes[9] == the_bytes[9]
+        && bytes[10] == the_bytes[10]
+        && bytes[11] == the_bytes[11]
+        && bytes[12] == the_bytes[12]
+        && bytes[13] == the_bytes[13]
+        && bytes[14] == the_bytes[14]
+        && bytes[15] == the_bytes[15];
+
+    print_int(x as i32);
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/builtin_macro_include_str.rs b/gcc/testsuite/rust/execute/torture/builtin_macro_include_str.rs
new file mode 100644
index 00000000000..6f9871d379c
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/builtin_macro_include_str.rs
@@ -0,0 +1,27 @@
+// { dg-output "hello, include!\n" }
+#[rustc_builtin_macro]
+macro_rules! include_str {
+    () => {{}};
+}
+
+extern "C" {
+    fn printf(fmt: *const i8, ...);
+}
+
+fn print(s: &str) {
+    unsafe {
+        printf(
+            "%s" as *const str as *const i8,
+            s as *const str as *const i8,
+        );
+    }
+}
+
+fn main() -> i32 {
+    // include_str! (and include_bytes!) allow for an optional trailing comma.
+    let my_str = include_str!("include.txt",);
+
+    print(my_str);
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/builtin_macro_line.rs b/gcc/testsuite/rust/execute/torture/builtin_macro_line.rs
new file mode 100644
index 00000000000..02541ed52e2
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/builtin_macro_line.rs
@@ -0,0 +1,25 @@
+// { dg-output "18\n21\n" }
+extern "C" {
+    fn printf(fmt: *const i8, ...);
+}
+
+fn print(s: u32) {
+    unsafe {
+        printf("%u\n\0" as *const str as *const i8, s);
+    }
+}
+
+#[rustc_builtin_macro]
+macro_rules! line {
+    () => {{}};
+}
+
+fn main() -> i32 {
+    let a = line!();
+    print(a);
+
+    let b = line!();
+    print(b);
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/builtin_macros1.rs b/gcc/testsuite/rust/execute/torture/builtin_macros1.rs
new file mode 100644
index 00000000000..5976478e426
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/builtin_macros1.rs
@@ -0,0 +1,21 @@
+// { dg-output "rust/execute/torture/builtin_macros1.rs" }
+#[rustc_builtin_macro]
+macro_rules! file {
+    () => {{}};
+}
+
+extern "C" {
+    fn printf(fmt: *const i8, ...);
+}
+
+fn print(s: &str) {
+    unsafe {
+        printf("%s\n\0" as *const str as *const i8, s);
+    }
+}
+
+fn main() -> i32 {
+    print(file!());
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/builtin_macros3.rs b/gcc/testsuite/rust/execute/torture/builtin_macros3.rs
new file mode 100644
index 00000000000..24555cbdb8a
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/builtin_macros3.rs
@@ -0,0 +1,28 @@
+// { dg-output "14\n42\n" }
+#[rustc_builtin_macro]
+macro_rules! column {
+    () => {{}};
+}
+
+extern "C" {
+    fn printf(fmt: *const i8, ...);
+}
+
+fn print(s: u32) {
+    unsafe {
+        printf("%u\n\0" as *const str as *const i8, s);
+    }
+}
+
+fn main() -> i32 {
+    let c0 = column!();
+
+    print(c0);
+
+    let c1 =                             column!();
+
+    print(c1);
+
+    0
+}
+
diff --git a/gcc/testsuite/rust/execute/torture/cfg1.rs b/gcc/testsuite/rust/execute/torture/cfg1.rs
new file mode 100644
index 00000000000..d3c56295503
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/cfg1.rs
@@ -0,0 +1,32 @@
+// { dg-additional-options "-w -frust-cfg=A" }
+// { dg-output "test1\n" }
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+#[cfg(A)]
+fn test() {
+    unsafe {
+        let a = "test1\n\0";
+        let b = a as *const str;
+        let c = b as *const i8;
+
+        printf(c);
+    }
+}
+
+#[cfg(B)]
+fn test() {
+    unsafe {
+        let a = "test2\n\0";
+        let b = a as *const str;
+        let c = b as *const i8;
+
+        printf(c);
+    }
+}
+
+fn main() -> i32 {
+    test();
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/cfg2.rs b/gcc/testsuite/rust/execute/torture/cfg2.rs
new file mode 100644
index 00000000000..5048bcb2791
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/cfg2.rs
@@ -0,0 +1,31 @@
+// { dg-additional-options "-w -frust-cfg=A" }
+// { dg-output "test1\n" }
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+fn test() {
+    #[cfg(A)]
+    unsafe {
+        let a = "test1\n\0";
+        let b = a as *const str;
+        let c = b as *const i8;
+
+        printf(c);
+    }
+
+    #[cfg(B)]
+    unsafe {
+        let a = "test2\n\0";
+        let b = a as *const str;
+        let c = b as *const i8;
+
+        printf(c);
+    }
+}
+
+fn main() -> i32 {
+    test();
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/cfg3.rs b/gcc/testsuite/rust/execute/torture/cfg3.rs
new file mode 100644
index 00000000000..89312344b23
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/cfg3.rs
@@ -0,0 +1,37 @@
+// { dg-additional-options "-w -frust-cfg=A" }
+// { dg-output "test1\n" }
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+struct Foo(i32);
+impl Foo {
+    #[cfg(A)]
+    fn test(&self) {
+        unsafe {
+            let a = "test1\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        }
+    }
+
+    #[cfg(B)]
+    fn test(&self) {
+        unsafe {
+            let a = "test2\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        }
+    }
+}
+
+fn main() -> i32 {
+    let a = Foo(123);
+    a.test();
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/cfg4.rs b/gcc/testsuite/rust/execute/torture/cfg4.rs
new file mode 100644
index 00000000000..d1c2a22a0ff
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/cfg4.rs
@@ -0,0 +1,38 @@
+// { dg-additional-options "-w -frust-cfg=A" }
+// { dg-output "test1\ntest2\n" }
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+struct Foo(i32);
+impl Foo {
+    #[cfg(A)]
+    fn test(&self) {
+        unsafe {
+            let a = "test1\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        }
+    }
+
+    #[cfg(not(B))]
+    fn test2(&self) {
+        unsafe {
+            let a = "test2\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        }
+    }
+}
+
+fn main() -> i32 {
+    let a = Foo(123);
+    a.test();
+    a.test2();
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/cfg5.rs b/gcc/testsuite/rust/execute/torture/cfg5.rs
new file mode 100644
index 00000000000..581a29bb89d
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/cfg5.rs
@@ -0,0 +1,13 @@
+// { dg-additional-options "-w -frust-cfg=A" }
+
+fn main() -> i32 {
+    let mut a = 0;
+
+    #[cfg(A)]
+    a = 3;
+
+    #[cfg(B)]
+    a = 40;
+
+    a - 3
+}
diff --git a/gcc/testsuite/rust/execute/torture/coercion1.rs b/gcc/testsuite/rust/execute/torture/coercion1.rs
new file mode 100644
index 00000000000..2cdb9bbca38
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/coercion1.rs
@@ -0,0 +1,41 @@
+/* { dg-output "123\n123\n" } */
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+struct Foo(i32);
+trait Bar {
+    fn baz(&self);
+}
+
+impl Bar for Foo {
+    fn baz(&self) {
+        unsafe {
+            let a = "%i\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c, self.0);
+        }
+    }
+}
+
+fn static_dispatch<T: Bar>(t: &T) {
+    t.baz();
+}
+
+fn dynamic_dispatch(t: &dyn Bar) {
+    t.baz();
+}
+
+fn main() -> i32 {
+    let a;
+    a = Foo(123);
+    static_dispatch(&a);
+
+    let b: &dyn Bar;
+    b = &a;
+    dynamic_dispatch(b);
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/coercion2.rs b/gcc/testsuite/rust/execute/torture/coercion2.rs
new file mode 100644
index 00000000000..12dd68ff5f7
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/coercion2.rs
@@ -0,0 +1,39 @@
+/* { dg-output "123\n123\n" } */
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+struct Foo(i32);
+trait Bar {
+    fn baz(&self);
+}
+
+impl Bar for Foo {
+    fn baz(&self) {
+        unsafe {
+            let a = "%i\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c, self.0);
+        }
+    }
+}
+
+fn static_dispatch<T: Bar>(t: &T) {
+    t.baz();
+}
+
+fn dynamic_dispatch(t: &dyn Bar) {
+    t.baz();
+}
+
+fn main() -> i32 {
+    let a;
+    a = &Foo(123);
+
+    static_dispatch(a);
+    dynamic_dispatch(a);
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/const_fold1.rs b/gcc/testsuite/rust/execute/torture/const_fold1.rs
new file mode 100644
index 00000000000..3cd6c0c77b5
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/const_fold1.rs
@@ -0,0 +1,13 @@
+// { dg-additional-options "-w" }
+const fn const_fn() -> usize {
+    4
+}
+
+const FN_TEST: usize = const_fn();
+
+const TEST: usize = 2 + FN_TEST;
+
+fn main() -> i32 {
+    let a: [_; 12] = [5; TEST * 2];
+    a[6] - 5
+}
diff --git a/gcc/testsuite/rust/execute/torture/const_fold2.rs b/gcc/testsuite/rust/execute/torture/const_fold2.rs
new file mode 100644
index 00000000000..c525648fe0b
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/const_fold2.rs
@@ -0,0 +1,16 @@
+// { dg-additional-options "-w" }
+const A: i32 = 1;
+const B: i32 = { A + 2 };
+
+const fn test() -> i32 {
+    B
+}
+
+const C: i32 = {
+    const a: i32 = 4;
+    test() + a
+};
+
+fn main() -> i32 {
+    C - 7
+}
diff --git a/gcc/testsuite/rust/execute/torture/copy_nonoverlapping1.rs b/gcc/testsuite/rust/execute/torture/copy_nonoverlapping1.rs
new file mode 100644
index 00000000000..2ae7a0869e3
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/copy_nonoverlapping1.rs
@@ -0,0 +1,17 @@
+extern "rust-intrinsic" {
+    pub fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize);
+}
+
+fn main() -> i32 {
+    let i = 15;
+    let mut i_copy = 16;
+
+    let i = &i as *const i32;
+    let i_copy = &mut i_copy as *mut i32;
+
+    unsafe {
+        copy_nonoverlapping(i, i_copy, 1);
+
+        *i_copy - *i
+    }
+}
\ No newline at end of file
diff --git a/gcc/testsuite/rust/execute/torture/empty_main.rs b/gcc/testsuite/rust/execute/torture/empty_main.rs
new file mode 100644
index 00000000000..6442e1e4e83
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/empty_main.rs
@@ -0,0 +1,3 @@
+fn main() -> i32 {
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/execute.exp b/gcc/testsuite/rust/execute/torture/execute.exp
new file mode 100644
index 00000000000..6dfb6d2b446
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/execute.exp
@@ -0,0 +1,33 @@
+# Copyright (C) 2021-2022 Free Software Foundation, Inc.
+
+# This program 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 of the License, or
+# (at your option) any later version.
+# 
+# This program 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/>.
+
+# Execute tests, torture testing.
+
+# Load support procs.
+load_lib rust-dg.exp
+
+# Initialize `dg'.
+dg-init
+
+# Main loop.
+set saved-dg-do-what-default ${dg-do-what-default}
+
+set dg-do-what-default "run"
+gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.rs]] "" ""
+set dg-do-what-default ${saved-dg-do-what-default}
+
+# All done.
+dg-finish
diff --git a/gcc/testsuite/rust/execute/torture/exit_error.rs b/gcc/testsuite/rust/execute/torture/exit_error.rs
new file mode 100644
index 00000000000..c3d0d9f2480
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/exit_error.rs
@@ -0,0 +1,5 @@
+// { dg-xfail-run-if "" { *-*-* } }
+
+fn main() -> i32 {
+    1
+}
diff --git a/gcc/testsuite/rust/execute/torture/extern_mod4.rs b/gcc/testsuite/rust/execute/torture/extern_mod4.rs
new file mode 100644
index 00000000000..99b6fb5c9ba
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/extern_mod4.rs
@@ -0,0 +1,19 @@
+// { dg-additional-options "-w" }
+// { dg-output "12" }
+mod modules;
+
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+fn main() -> i32 {
+    unsafe {
+        let fmt_s = "%d\n\0";
+        let fmt_p = fmt_s as *const str;
+        let fmt_i8 = fmt_p as *const i8;
+
+        printf(fmt_i8, modules::return_12());
+    }
+
+    return 0;
+}
diff --git a/gcc/testsuite/rust/execute/torture/func1.rs b/gcc/testsuite/rust/execute/torture/func1.rs
new file mode 100644
index 00000000000..0a093d88587
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/func1.rs
@@ -0,0 +1,5 @@
+fn main() -> i32 {
+    1;
+    2;
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/helloworld1.rs b/gcc/testsuite/rust/execute/torture/helloworld1.rs
new file mode 100644
index 00000000000..d416efa33af
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/helloworld1.rs
@@ -0,0 +1,15 @@
+/* { dg-output "Hello World" }*/
+extern "C" {
+    fn puts(s: *const i8);
+}
+
+fn main() -> i32 {
+    unsafe {
+        let a = "Hello World";
+        let b = a as *const str;
+        let c = b as *const i8;
+
+        puts(c);
+    }
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/helloworld2.rs b/gcc/testsuite/rust/execute/torture/helloworld2.rs
new file mode 100644
index 00000000000..cc05f3798fa
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/helloworld2.rs
@@ -0,0 +1,15 @@
+/* { dg-output "Hello World 123\n" }*/
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+fn main() -> i32 {
+    unsafe {
+        let a = "Hello World %i\n";
+        let b = a as *const str;
+        let c = b as *const i8;
+
+        printf(c, 123);
+    }
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/include.txt b/gcc/testsuite/rust/execute/torture/include.txt
new file mode 100644
index 00000000000..12c368778e1
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/include.txt
@@ -0,0 +1 @@
+hello, include!
diff --git a/gcc/testsuite/rust/execute/torture/index1.rs b/gcc/testsuite/rust/execute/torture/index1.rs
new file mode 100644
index 00000000000..4682978bdd0
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/index1.rs
@@ -0,0 +1,28 @@
+// { dg-additional-options "-w" }
+#[lang = "index"]
+trait Index<Idx> {
+    type Output;
+
+    fn index(&self, index: Idx) -> &Self::Output;
+}
+
+struct Foo(i32, i32);
+impl Index<isize> for Foo {
+    type Output = i32;
+
+    fn index(&self, index: isize) -> &i32 {
+        if index == 0 {
+            &self.0
+        } else {
+            &self.1
+        }
+    }
+}
+
+fn main() -> i32 {
+    let a = Foo(1, 2);
+    let b = a[0];
+    let c = a[1];
+
+    c - b - 1
+}
diff --git a/gcc/testsuite/rust/execute/torture/issue-1120.rs b/gcc/testsuite/rust/execute/torture/issue-1120.rs
new file mode 100644
index 00000000000..242c94b5cb6
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/issue-1120.rs
@@ -0,0 +1,123 @@
+// { dg-additional-options "-w" }
+extern "rust-intrinsic" {
+    #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
+    pub fn offset<T>(dst: *const T, offset: isize) -> *const T;
+}
+
+struct FatPtr<T> {
+    data: *const T,
+    len: usize,
+}
+
+pub union Repr<T> {
+    rust: *const [T],
+    rust_mut: *mut [T],
+    raw: FatPtr<T>,
+}
+
+pub enum Option<T> {
+    None,
+    Some(T),
+}
+
+#[lang = "Range"]
+pub struct Range<Idx> {
+    pub start: Idx,
+    pub end: Idx,
+}
+
+#[lang = "const_slice_ptr"]
+impl<T> *const [T] {
+    pub const fn len(self) -> usize {
+        unsafe { Repr { rust: self }.raw.len }
+    }
+
+    pub const fn as_ptr(self) -> *const T {
+        self as *const T
+    }
+}
+
+#[lang = "const_ptr"]
+impl<T> *const T {
+    pub const unsafe fn offset(self, count: isize) -> *const T {
+        unsafe { offset(self, count) }
+    }
+
+    pub const unsafe fn add(self, count: usize) -> Self {
+        unsafe { self.offset(count as isize) }
+    }
+
+    pub const fn as_ptr(self) -> *const T {
+        self as *const T
+    }
+}
+
+const fn slice_from_raw_parts<T>(data: *const T, len: usize) -> *const [T] {
+    unsafe {
+        Repr {
+            raw: FatPtr { data, len },
+        }
+        .rust
+    }
+}
+
+#[lang = "index"]
+trait Index<Idx> {
+    type Output;
+
+    fn index(&self, index: Idx) -> &Self::Output;
+}
+
+pub unsafe trait SliceIndex<T> {
+    type Output;
+
+    fn get(self, slice: &T) -> Option<&Self::Output>;
+
+    unsafe fn get_unchecked(self, slice: *const T) -> *const Self::Output;
+
+    fn index(self, slice: &T) -> &Self::Output;
+}
+
+unsafe impl<T> SliceIndex<[T]> for Range<usize> {
+    type Output = [T];
+
+    fn get(self, slice: &[T]) -> Option<&[T]> {
+        if self.start > self.end
+        /* || self.end > slice.len() */
+        {
+            Option::None
+        } else {
+            unsafe { Option::Some(&*self.get_unchecked(slice)) }
+        }
+    }
+
+    unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] {
+        unsafe {
+            let a: *const T = slice.as_ptr();
+            let b: *const T = a.add(self.start);
+            slice_from_raw_parts(b, self.end - self.start)
+        }
+    }
+
+    fn index(self, slice: &[T]) -> &[T] {
+        unsafe { &*self.get_unchecked(slice) }
+    }
+}
+
+impl<T, I> Index<I> for [T]
+where
+    I: SliceIndex<[T]>,
+{
+    type Output = I::Output;
+
+    fn index(&self, index: I) -> &I::Output {
+        index.index(self)
+    }
+}
+
+fn main() -> i32 {
+    let a = [1, 2, 3, 4, 5];
+    let b = &a[1..3];
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/issue-1133.rs b/gcc/testsuite/rust/execute/torture/issue-1133.rs
new file mode 100644
index 00000000000..f2080a6e072
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/issue-1133.rs
@@ -0,0 +1,146 @@
+// { dg-additional-options "-w" }
+extern "rust-intrinsic" {
+    #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
+    pub fn offset<T>(dst: *const T, offset: isize) -> *const T;
+}
+
+struct FatPtr<T> {
+    data: *const T,
+    len: usize,
+}
+
+pub union Repr<T> {
+    rust: *const [T],
+    rust_mut: *mut [T],
+    raw: FatPtr<T>,
+}
+
+pub enum Option<T> {
+    None,
+    Some(T),
+}
+
+#[lang = "Range"]
+pub struct Range<Idx> {
+    pub start: Idx,
+    pub end: Idx,
+}
+
+#[lang = "const_slice_ptr"]
+impl<T> *const [T] {
+    pub const fn len(self) -> usize {
+        unsafe { Repr { rust: self }.raw.len }
+    }
+
+    pub const fn as_ptr(self) -> *const T {
+        self as *const T
+    }
+}
+
+#[lang = "const_ptr"]
+impl<T> *const T {
+    pub const unsafe fn offset(self, count: isize) -> *const T {
+        unsafe { offset(self, count) }
+    }
+
+    pub const unsafe fn add(self, count: usize) -> Self {
+        unsafe { self.offset(count as isize) }
+    }
+
+    pub const fn as_ptr(self) -> *const T {
+        self as *const T
+    }
+}
+
+const fn slice_from_raw_parts<T>(data: *const T, len: usize) -> *const [T] {
+    unsafe {
+        Repr {
+            raw: FatPtr { data, len },
+        }
+        .rust
+    }
+}
+
+#[lang = "index"]
+trait Index<Idx> {
+    type Output;
+
+    fn index(&self, index: Idx) -> &Self::Output;
+}
+
+pub unsafe trait SliceIndex<T> {
+    type Output;
+
+    fn get(self, slice: &T) -> Option<&Self::Output>;
+
+    unsafe fn get_unchecked(self, slice: *const T) -> *const Self::Output;
+
+    fn index(self, slice: &T) -> &Self::Output;
+}
+
+unsafe impl<T> SliceIndex<[T]> for usize {
+    type Output = T;
+
+    fn get(self, slice: &[T]) -> Option<&T> {
+        unsafe { Option::Some(&*self.get_unchecked(slice)) }
+    }
+
+    unsafe fn get_unchecked(self, slice: *const [T]) -> *const T {
+        // SAFETY: the caller guarantees that `slice` is not dangling, so it
+        // cannot be longer than `isize::MAX`. They also guarantee that
+        // `self` is in bounds of `slice` so `self` cannot overflow an `isize`,
+        // so the call to `add` is safe.
+        unsafe { slice.as_ptr().add(self) }
+    }
+
+    fn index(self, slice: &[T]) -> &T {
+        // N.B., use intrinsic indexing
+        // &(*slice)[self]
+        unsafe { &*self.get_unchecked(slice) }
+    }
+}
+
+unsafe impl<T> SliceIndex<[T]> for Range<usize> {
+    type Output = [T];
+
+    fn get(self, slice: &[T]) -> Option<&[T]> {
+        if self.start > self.end
+        /* || self.end > slice.len() */
+        {
+            Option::None
+        } else {
+            unsafe { Option::Some(&*self.get_unchecked(slice)) }
+        }
+    }
+
+    unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] {
+        unsafe {
+            let a: *const T = slice.as_ptr();
+            let b: *const T = a.add(self.start);
+            slice_from_raw_parts(b, self.end - self.start)
+        }
+    }
+
+    fn index(self, slice: &[T]) -> &[T] {
+        unsafe { &*self.get_unchecked(slice) }
+    }
+}
+
+impl<T, I> Index<I> for [T]
+where
+    I: SliceIndex<[T]>,
+{
+    type Output = I::Output;
+
+    fn index(&self, index: I) -> &I::Output {
+        index.index(self)
+    }
+}
+
+fn main() -> i32 {
+    let a = [1, 2, 3, 4, 5];
+    let b = &a[1..3];
+    let c = b[1];
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/issue-1198.rs b/gcc/testsuite/rust/execute/torture/issue-1198.rs
new file mode 100644
index 00000000000..fce44ad1994
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/issue-1198.rs
@@ -0,0 +1,75 @@
+/* { dg-output "foo_deref\nimm_deref\n123\n" } */
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+#[lang = "deref"]
+pub trait Deref {
+    type Target;
+
+    fn deref(&self) -> &Self::Target;
+}
+
+impl<T> Deref for &T {
+    type Target = T;
+
+    fn deref(&self) -> &T {
+        unsafe {
+            let a = "imm_deref\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        }
+
+        *self
+    }
+}
+
+impl<T> Deref for &mut T {
+    type Target = T;
+
+    fn deref(&self) -> &T {
+        unsafe {
+            let a = "mut_deref\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        }
+
+        *self
+    }
+}
+
+struct Foo<T>(T);
+impl<T> Deref for Foo<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        unsafe {
+            let a = "foo_deref\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        }
+
+        &self.0
+    }
+}
+
+fn main() -> i32 {
+    let foo: Foo<i32> = Foo(123);
+    let bar: &i32 = &foo;
+
+    unsafe {
+        let a = "%i\n\0";
+        let b = a as *const str;
+        let c = b as *const i8;
+
+        printf(c, *bar);
+    }
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/issue-1231.rs b/gcc/testsuite/rust/execute/torture/issue-1231.rs
new file mode 100644
index 00000000000..970e86f917a
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/issue-1231.rs
@@ -0,0 +1,36 @@
+// { dg-additional-options "-w" }
+// { dg-output "outer\ninner\n" }
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+fn machin() {
+    unsafe {
+        let a = "outer\n\0";
+        let b = a as *const str;
+        let c = b as *const i8;
+
+        printf(c, 123);
+    }
+}
+
+fn bidule() {
+    fn machin() {
+        unsafe {
+            let a = "inner\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c, 123);
+        }
+    }
+
+    self::machin();
+    machin();
+}
+
+fn main() -> i32 {
+    bidule();
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/issue-1232.rs b/gcc/testsuite/rust/execute/torture/issue-1232.rs
new file mode 100644
index 00000000000..c56d5c18695
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/issue-1232.rs
@@ -0,0 +1,159 @@
+// { dg-additional-options "-w" }
+// { dg-output "slice_access=3\n" }
+extern "rust-intrinsic" {
+    #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
+    fn offset<T>(dst: *const T, offset: isize) -> *const T;
+}
+
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+struct FatPtr<T> {
+    data: *const T,
+    len: usize,
+}
+
+pub union Repr<T> {
+    rust: *const [T],
+    rust_mut: *mut [T],
+    raw: FatPtr<T>,
+}
+
+pub enum Option<T> {
+    None,
+    Some(T),
+}
+
+#[lang = "Range"]
+pub struct Range<Idx> {
+    pub start: Idx,
+    pub end: Idx,
+}
+
+#[lang = "const_slice_ptr"]
+impl<T> *const [T] {
+    pub const fn len(self) -> usize {
+        unsafe { Repr { rust: self }.raw.len }
+    }
+
+    pub const fn as_ptr(self) -> *const T {
+        self as *const T
+    }
+}
+
+#[lang = "const_ptr"]
+impl<T> *const T {
+    pub const unsafe fn offset(self, count: isize) -> *const T {
+        unsafe { offset(self, count) }
+    }
+
+    pub const unsafe fn add(self, count: usize) -> Self {
+        unsafe { self.offset(count as isize) }
+    }
+
+    pub const fn as_ptr(self) -> *const T {
+        self as *const T
+    }
+}
+
+const fn slice_from_raw_parts<T>(data: *const T, len: usize) -> *const [T] {
+    unsafe {
+        Repr {
+            raw: FatPtr { data, len },
+        }
+        .rust
+    }
+}
+
+#[lang = "index"]
+trait Index<Idx> {
+    type Output;
+
+    fn index(&self, index: Idx) -> &Self::Output;
+}
+
+pub unsafe trait SliceIndex<T> {
+    type Output;
+
+    fn get(self, slice: &T) -> Option<&Self::Output>;
+
+    unsafe fn get_unchecked(self, slice: *const T) -> *const Self::Output;
+
+    fn index(self, slice: &T) -> &Self::Output;
+}
+
+unsafe impl<T> SliceIndex<[T]> for usize {
+    type Output = T;
+
+    fn get(self, slice: &[T]) -> Option<&T> {
+        unsafe { Option::Some(&*self.get_unchecked(slice)) }
+    }
+
+    unsafe fn get_unchecked(self, slice: *const [T]) -> *const T {
+        // SAFETY: the caller guarantees that `slice` is not dangling, so it
+        // cannot be longer than `isize::MAX`. They also guarantee that
+        // `self` is in bounds of `slice` so `self` cannot overflow an `isize`,
+        // so the call to `add` is safe.
+        unsafe { slice.as_ptr().add(self) }
+    }
+
+    fn index(self, slice: &[T]) -> &T {
+        // N.B., use intrinsic indexing
+        // &(*slice)[self]
+        unsafe { &*self.get_unchecked(slice) }
+    }
+}
+
+unsafe impl<T> SliceIndex<[T]> for Range<usize> {
+    type Output = [T];
+
+    fn get(self, slice: &[T]) -> Option<&[T]> {
+        if self.start > self.end
+        /* || self.end > slice.len() */
+        {
+            Option::None
+        } else {
+            unsafe { Option::Some(&*self.get_unchecked(slice)) }
+        }
+    }
+
+    unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] {
+        unsafe {
+            let a: *const T = slice.as_ptr();
+            let b: *const T = a.add(self.start);
+            slice_from_raw_parts(b, self.end - self.start)
+        }
+    }
+
+    fn index(self, slice: &[T]) -> &[T] {
+        unsafe { &*self.get_unchecked(slice) }
+    }
+}
+
+impl<T, I> Index<I> for [T]
+where
+    I: SliceIndex<[T]>,
+{
+    type Output = I::Output;
+
+    fn index(&self, index: I) -> &I::Output {
+        index.index(self)
+    }
+}
+
+fn main() -> i32 {
+    let array = [1, 2, 3, 4, 5];
+    let slice = &array[1..3];
+    let slice_access = slice[1];
+
+    unsafe {
+        let a = "slice_access=%i\n";
+        let b = a as *const str;
+        let c = b as *const i8;
+
+        printf(c, slice_access);
+    }
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/issue-1249.rs b/gcc/testsuite/rust/execute/torture/issue-1249.rs
new file mode 100644
index 00000000000..072204ea877
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/issue-1249.rs
@@ -0,0 +1,39 @@
+// { dg-options "-w" }
+// { dg-output "1\n2\n" }
+
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+trait T {
+    fn foo(&self);
+}
+
+impl dyn T {
+    fn bar(&self) {
+        unsafe {
+            let a = "1\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+            printf(c);
+        }
+        self.foo()
+    }
+}
+
+struct S;
+impl T for S {
+    fn foo(&self) {
+        unsafe {
+            let a = "2\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+            printf(c);
+        }
+    }
+}
+
+pub fn main() -> i32 {
+    <dyn T>::bar(&S);
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/issue-1436.rs b/gcc/testsuite/rust/execute/torture/issue-1436.rs
new file mode 100644
index 00000000000..5c079a61f07
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/issue-1436.rs
@@ -0,0 +1,172 @@
+// { dg-options "-w" }
+// { dg-output "" }
+mod intrinsics {
+    extern "rust-intrinsic" {
+        #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
+        pub fn offset<T>(ptr: *const T, count: isize) -> *const T;
+    }
+}
+
+mod mem {
+    extern "rust-intrinsic" {
+        fn size_of<T>() -> usize;
+    }
+}
+
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+struct FatPtr<T> {
+    data: *const T,
+    len: usize,
+}
+
+pub union Repr<T> {
+    rust: *const [T],
+    rust_mut: *mut [T],
+    raw: FatPtr<T>,
+}
+
+pub enum Option<T> {
+    None,
+    Some(T),
+}
+
+#[lang = "Range"]
+pub struct Range<Idx> {
+    pub start: Idx,
+    pub end: Idx,
+}
+
+#[lang = "const_slice_ptr"]
+impl<T> *const [T] {
+    pub const fn len(self) -> usize {
+        unsafe { Repr { rust: self }.raw.len }
+    }
+
+    pub const fn as_ptr(self) -> *const T {
+        self as *const T
+    }
+}
+
+#[lang = "const_ptr"]
+impl<T> *const T {
+    pub const unsafe fn offset(self, count: isize) -> *const T {
+        unsafe { intrinsics::offset(self, count) }
+    }
+
+    pub const unsafe fn add(self, count: usize) -> Self {
+        unsafe { self.offset(count as isize) }
+    }
+
+    pub const fn as_ptr(self) -> *const T {
+        self as *const T
+    }
+}
+
+const fn slice_from_raw_parts<T>(data: *const T, len: usize) -> *const [T] {
+    unsafe {
+        Repr {
+            raw: FatPtr { data, len },
+        }
+        .rust
+    }
+}
+
+#[lang = "index"]
+trait Index<Idx> {
+    type Output;
+
+    fn index(&self, index: Idx) -> &Self::Output;
+}
+
+impl<T> [T] {
+    pub const fn is_empty(&self) -> bool {
+        self.len() == 0
+    }
+
+    pub const fn len(&self) -> usize {
+        unsafe { Repr { rust: self }.raw.len }
+    }
+}
+
+pub unsafe trait SliceIndex<T> {
+    type Output;
+
+    fn get(self, slice: &T) -> Option<&Self::Output>;
+
+    unsafe fn get_unchecked(self, slice: *const T) -> *const Self::Output;
+
+    fn index(self, slice: &T) -> &Self::Output;
+}
+
+unsafe impl<T> SliceIndex<[T]> for usize {
+    type Output = T;
+
+    fn get(self, slice: &[T]) -> Option<&T> {
+        unsafe { Option::Some(&*self.get_unchecked(slice)) }
+    }
+
+    unsafe fn get_unchecked(self, slice: *const [T]) -> *const T {
+        // SAFETY: the caller guarantees that `slice` is not dangling, so it
+        // cannot be longer than `isize::MAX`. They also guarantee that
+        // `self` is in bounds of `slice` so `self` cannot overflow an `isize`,
+        // so the call to `add` is safe.
+        unsafe { slice.as_ptr().add(self) }
+    }
+
+    fn index(self, slice: &[T]) -> &T {
+        unsafe { &*self.get_unchecked(slice) }
+    }
+}
+
+unsafe impl<T> SliceIndex<[T]> for Range<usize> {
+    type Output = [T];
+
+    fn get(self, slice: &[T]) -> Option<&[T]> {
+        if self.start > self.end || self.end > slice.len() {
+            Option::None
+        } else {
+            unsafe { Option::Some(&*self.get_unchecked(slice)) }
+        }
+    }
+
+    unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] {
+        unsafe {
+            let a: *const T = slice.as_ptr();
+            let b: *const T = a.add(self.start);
+            slice_from_raw_parts(b, self.end - self.start)
+        }
+    }
+
+    fn index(self, slice: &[T]) -> &[T] {
+        unsafe { &*self.get_unchecked(slice) }
+    }
+}
+
+impl<T, I> Index<I> for [T]
+where
+    I: SliceIndex<[T]>,
+{
+    type Output = I::Output;
+
+    fn index(&self, index: I) -> &I::Output {
+        unsafe {
+            let a = "slice-index\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        }
+
+        index.index(self)
+    }
+}
+
+fn main() -> i32 {
+    let a = [1, 2, 3, 4, 5];
+    let b = a[1];
+
+    b - 2
+}
diff --git a/gcc/testsuite/rust/execute/torture/issue-1496.rs b/gcc/testsuite/rust/execute/torture/issue-1496.rs
new file mode 100644
index 00000000000..9f08b2ae98a
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/issue-1496.rs
@@ -0,0 +1,75 @@
+/* { dg-output "foo_deref\nimm_deref\n123\n" } */
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+#[lang = "deref"]
+pub trait Deref {
+    type Target;
+
+    fn deref(&self) -> &Self::Target;
+}
+
+impl<T> Deref for &T {
+    type Target = T;
+
+    fn deref(&self) -> &T {
+        unsafe {
+            let a = "imm_deref\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        }
+
+        *self
+    }
+}
+
+impl<T> Deref for &mut T {
+    type Target = T;
+
+    fn deref(&self) -> &T {
+        unsafe {
+            let a = "mut_deref\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        }
+
+        *self
+    }
+}
+
+struct Foo<T>(T);
+impl<T> Deref for Foo<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        unsafe {
+            let a = "foo_deref\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        }
+
+        &self.0
+    }
+}
+
+fn main() -> i32 {
+    let foo = Foo(123);
+    let bar = &foo as &i32;
+
+    unsafe {
+        let a = "%i\n\0";
+        let b = a as *const str;
+        let c = b as *const i8;
+
+        printf(c, *bar);
+    }
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/issue-647.rs b/gcc/testsuite/rust/execute/torture/issue-647.rs
new file mode 100644
index 00000000000..3f427ccb785
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/issue-647.rs
@@ -0,0 +1,33 @@
+/* { dg-output "Hello World 123\n" }*/
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+struct Foo<T>(T);
+
+struct Bar<T> {
+    a: Foo<T>,
+    b: bool,
+    // { dg-warning "field is never read" "" { target *-*-* } .-1 }
+}
+
+fn test<T>(a: Bar<T>) -> Foo<T> {
+    a.a
+}
+
+fn main() -> i32 {
+    let a: Bar<i32> = Bar::<i32> {
+        a: Foo::<i32>(123),
+        b: true,
+    };
+    let result: Foo<i32> = test(a);
+
+    unsafe {
+        let a = "Hello World %i\n";
+        let b = a as *const str;
+        let c = b as *const i8;
+
+        printf(c, result.0);
+    }
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/issue-845.rs b/gcc/testsuite/rust/execute/torture/issue-845.rs
new file mode 100644
index 00000000000..4c689e3b6c8
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/issue-845.rs
@@ -0,0 +1,47 @@
+// { dg-output "Foo::bar\n" }
+// { dg-additional-options "-w" }
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+struct Foo {}
+
+trait Bar {
+    fn bar(&self) {
+        unsafe {
+            let _a = "Bar::bar\n\0";
+            let _b = _a as *const str;
+            let _c = _b as *const i8;
+            printf(_c);
+        }
+    }
+}
+
+impl Foo {
+    fn bar(&self) {
+        unsafe {
+            let _a = "Foo::bar\n\0";
+            let _b = _a as *const str;
+            let _c = _b as *const i8;
+            printf(_c);
+        }
+    }
+}
+
+impl Bar for Foo {
+    fn bar(&self) {
+        unsafe {
+            let _a = "<Bar as Foo>::bar\n\0";
+            let _b = _a as *const str;
+            let _c = _b as *const i8;
+            printf(_c);
+        }
+    }
+}
+
+pub fn main() -> i32 {
+    let mut f = Foo {};
+    f.bar();
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/issue-851.rs b/gcc/testsuite/rust/execute/torture/issue-851.rs
new file mode 100644
index 00000000000..3881c7a2ada
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/issue-851.rs
@@ -0,0 +1,35 @@
+/* { dg-output "Result: 123\n" } */
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+enum Foo<T> {
+    A,
+    B(T),
+}
+
+fn inspect(a: Foo<i32>) {
+    match a {
+        Foo::A => unsafe {
+            let a = "A\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        },
+        Foo::B(x) => unsafe {
+            let a = "Result: %i\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c, x);
+        },
+    }
+}
+
+fn main() -> i32 {
+    let a = Foo::B(123);
+    inspect(a);
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/issue-858.rs b/gcc/testsuite/rust/execute/torture/issue-858.rs
new file mode 100644
index 00000000000..5a43f3e1b1a
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/issue-858.rs
@@ -0,0 +1,32 @@
+/* { dg-output "Result: 123\n" } */
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+enum Foo<T> {
+    A,
+    B(T),
+}
+
+fn main() -> i32 {
+    let result = Foo::B(123);
+
+    match result {
+        Foo::A => unsafe {
+            let a = "A\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        },
+        Foo::B(x) => unsafe {
+            let a = "Result: %i\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c, x);
+        },
+    }
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/issue-976.rs b/gcc/testsuite/rust/execute/torture/issue-976.rs
new file mode 100644
index 00000000000..42cf596fb7d
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/issue-976.rs
@@ -0,0 +1,14 @@
+/* { dg-output "hi" } */
+fn main() -> i32 {
+    {
+        extern "C" {
+            fn puts(s: *const i8);
+        }
+
+        unsafe {
+            puts("hi\0" as *const str as *const i8);
+        }
+    }
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/issue-995.rs b/gcc/testsuite/rust/execute/torture/issue-995.rs
new file mode 100644
index 00000000000..42570e33f74
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/issue-995.rs
@@ -0,0 +1,9 @@
+struct Pattern(i32);
+
+fn pattern_as_arg(Pattern(value): Pattern) -> i32 {
+    value
+}
+
+fn main() -> i32 {
+    pattern_as_arg(Pattern(15)) - 15
+}
diff --git a/gcc/testsuite/rust/execute/torture/macros1.rs b/gcc/testsuite/rust/execute/torture/macros1.rs
new file mode 100644
index 00000000000..652d2d8fe5b
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/macros1.rs
@@ -0,0 +1,13 @@
+macro_rules! add {
+    ($a:expr,$b:expr) => {
+        $a + $b
+    };
+}
+
+fn test() -> i32 {
+    add!(1 + 2, 3)
+}
+
+fn main() -> i32 {
+    test() - 6
+}
diff --git a/gcc/testsuite/rust/execute/torture/macros10.rs b/gcc/testsuite/rust/execute/torture/macros10.rs
new file mode 100644
index 00000000000..155a440ee04
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/macros10.rs
@@ -0,0 +1,22 @@
+// { dg-output "18\n" }
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+fn print_int(value: i32) {
+    let s = "%d\n\0" as *const str as *const i8;
+    unsafe {
+        printf(s, value);
+    }
+}
+
+macro_rules! add_exprs {
+    ($($e:expr)*) => (0 $(+ $e)*)
+}
+
+fn main() -> i32 {
+    // 1 + 2 + 15 => 18
+    print_int(add_exprs!(1 2 15));
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/macros11.rs b/gcc/testsuite/rust/execute/torture/macros11.rs
new file mode 100644
index 00000000000..5bde97d3dd4
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/macros11.rs
@@ -0,0 +1,25 @@
+// { dg-output "2" }
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+fn print_int(value: i32) {
+    let s = "%d\n\0";
+    let s_p = s as *const str;
+    let c_p = s_p as *const i8;
+    unsafe {
+        printf(c_p, value);
+    }
+}
+
+macro_rules! add_exprs {
+    ($($e:expr)?) => (0 $(+ $e)?)
+}
+
+fn main() -> i32 {
+    // 2
+    let a = add_exprs!(2);
+    print_int(a);
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/macros12.rs b/gcc/testsuite/rust/execute/torture/macros12.rs
new file mode 100644
index 00000000000..d310dff9ba8
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/macros12.rs
@@ -0,0 +1,22 @@
+// { dg-output "0\n" }
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+fn print_int(value: i32) {
+    let s = "%d\n\0" as *const str as *const i8;
+    unsafe {
+        printf(s, value);
+    }
+}
+
+macro_rules! add_exprs {
+    ($($e:expr)?) => (0 $(+ $e)?)
+}
+
+fn main() -> i32 {
+    // 0
+    print_int(add_exprs!());
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/macros13.rs b/gcc/testsuite/rust/execute/torture/macros13.rs
new file mode 100644
index 00000000000..afb20264625
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/macros13.rs
@@ -0,0 +1,22 @@
+// { dg-output "18\n" }
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+fn print_int(value: i32) {
+    let s = "%d\n\0" as *const str as *const i8;
+    unsafe {
+        printf(s, value);
+    }
+}
+
+macro_rules! add_exprs {
+    ($($e:expr)+) => (0 $(+ $e)+)
+}
+
+fn main() -> i32 {
+    // 1 + 2 + 15 => 18
+    print_int(add_exprs!(1 2 15));
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/macros14.rs b/gcc/testsuite/rust/execute/torture/macros14.rs
new file mode 100644
index 00000000000..00656546d4c
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/macros14.rs
@@ -0,0 +1,22 @@
+// { dg-output "15\n" }
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+fn print_int(value: i32) {
+    let s = "%d\n\0" as *const str as *const i8;
+    unsafe {
+        printf(s, value);
+    }
+}
+
+macro_rules! add_exprs {
+    ($($e:expr)*) => (15 $(+ $e)*)
+}
+
+fn main() -> i32 {
+    // 15
+    print_int(add_exprs!());
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/macros16.rs b/gcc/testsuite/rust/execute/torture/macros16.rs
new file mode 100644
index 00000000000..47ab2411c0d
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/macros16.rs
@@ -0,0 +1,14 @@
+macro_rules! add {
+    ($e:literal) => {
+        0 + $e
+    };
+    ($e:literal $($es:literal)*) => {
+        $e + add!($($es)*)
+    };
+}
+
+fn main() -> i32 {
+    let a = add!(1 2 3 10); // 16
+
+    a - 16
+}
diff --git a/gcc/testsuite/rust/execute/torture/macros17.rs b/gcc/testsuite/rust/execute/torture/macros17.rs
new file mode 100644
index 00000000000..390352ec47f
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/macros17.rs
@@ -0,0 +1,17 @@
+macro_rules! two {
+    (2) => {
+        3
+    };
+}
+
+macro_rules! one {
+    (1) => {{
+        two!(2)
+    }};
+}
+
+fn main() -> i32 {
+    let a = one!(1);
+
+    a - 3
+}
diff --git a/gcc/testsuite/rust/execute/torture/macros18.rs b/gcc/testsuite/rust/execute/torture/macros18.rs
new file mode 100644
index 00000000000..61df17e9da5
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/macros18.rs
@@ -0,0 +1,14 @@
+macro_rules! add {
+    ($e:literal) => {
+        0 + $e
+    };
+    ($e:literal $($es:literal)*) => {
+        $e + add!($($es)*)
+    };
+}
+
+fn main() -> i32 {
+    let a = add!(3 4); // 7
+
+    a - 7
+}
diff --git a/gcc/testsuite/rust/execute/torture/macros19.rs b/gcc/testsuite/rust/execute/torture/macros19.rs
new file mode 100644
index 00000000000..4732545410e
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/macros19.rs
@@ -0,0 +1,14 @@
+macro_rules! add {
+    ($e:expr, $($es:expr),*) => {
+        $e + add!($($es),*)
+    };
+    ($e:expr) => {
+        $e
+    };
+}
+
+fn main() -> i32 {
+    let a = add!(15, 2, 9); // 26
+
+    a - 26
+}
diff --git a/gcc/testsuite/rust/execute/torture/macros2.rs b/gcc/testsuite/rust/execute/torture/macros2.rs
new file mode 100644
index 00000000000..ba5098710ea
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/macros2.rs
@@ -0,0 +1,40 @@
+// { dg-output "arg\narg\narg\n" }
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+fn f() {
+    unsafe {
+        let r_s = "arg\n\0";
+        let s_p = r_s as *const str;
+        let c_p = s_p as *const i8;
+
+        printf(c_p);
+    }
+}
+
+macro_rules! kw0 {
+    (keyword) => {
+        f();
+    };
+}
+
+macro_rules! kw1 {
+    (fn) => {
+        f();
+    };
+}
+
+macro_rules! kw2 {
+    (kw0 kw1 kw3) => {
+        f();
+    };
+}
+
+fn main() -> i32 {
+    kw0!(keyword);
+    kw1!(fn);
+    kw2!(kw0 kw1 kw3);
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/macros20.rs b/gcc/testsuite/rust/execute/torture/macros20.rs
new file mode 100644
index 00000000000..97317a0879e
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/macros20.rs
@@ -0,0 +1,14 @@
+macro_rules! add {
+    ($e:expr , $($es:expr) , *) => {
+        $e + add!($($es) , *)
+    };
+    ($e:expr) => {
+        $e
+    };
+}
+
+fn main() -> i32 {
+    let a = add!(15, 2, 9); // 26
+
+    a - 26
+}
diff --git a/gcc/testsuite/rust/execute/torture/macros21.rs b/gcc/testsuite/rust/execute/torture/macros21.rs
new file mode 100644
index 00000000000..2508be1a6fd
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/macros21.rs
@@ -0,0 +1,15 @@
+macro_rules! add_parens {
+    ($($rep:ident ( ) )*) => {
+        { 0 $(+ $rep ( ))* }
+    };
+}
+
+fn f() -> i32 {
+    1
+}
+
+fn main() -> i32 {
+    let a = add_parens!(f() f() f());
+
+    a - 3
+}
diff --git a/gcc/testsuite/rust/execute/torture/macros22.rs b/gcc/testsuite/rust/execute/torture/macros22.rs
new file mode 100644
index 00000000000..3f291ace98e
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/macros22.rs
@@ -0,0 +1,27 @@
+// { dg-output "1\n2\nNaN\n3\n" }
+
+macro_rules! print_num {
+    ($l:literal) => {{
+        unsafe {
+            printf("%d\n\0" as *const str as *const i8, $l);
+        }
+    }};
+}
+
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+// Check to make sure that expanding macros does not break the flow of calls
+fn main() -> i32 {
+    print_num!(1);
+    print_num!(2);
+
+    unsafe {
+        printf("NaN\n\0" as *const str as *const i8);
+    }
+
+    print_num!(3);
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/macros23.rs b/gcc/testsuite/rust/execute/torture/macros23.rs
new file mode 100644
index 00000000000..846352d0487
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/macros23.rs
@@ -0,0 +1,19 @@
+trait Valuable {
+    const VALUE: i32;
+}
+
+struct Something;
+
+macro_rules! implement {
+    () => {
+        const VALUE: i32 = 18;
+    };
+}
+
+impl Valuable for Something {
+    implement!();
+}
+
+fn main() -> i32 {
+    Something::VALUE - 18
+}
diff --git a/gcc/testsuite/rust/execute/torture/macros24.rs b/gcc/testsuite/rust/execute/torture/macros24.rs
new file mode 100644
index 00000000000..f838a83af66
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/macros24.rs
@@ -0,0 +1,9 @@
+macro_rules! repeat {
+    ( $( $i:literal ),* ; $( $j:literal ),* ) => (( $( ($i,$j) ),* ))
+}
+
+fn main() -> i32 {
+    let t = repeat!(1, 1; 2, 2);
+
+    t.0 .0 + t.0 .1 + t.1 .0 + t.1 .1 - 6
+}
diff --git a/gcc/testsuite/rust/execute/torture/macros25.rs b/gcc/testsuite/rust/execute/torture/macros25.rs
new file mode 100644
index 00000000000..c2658721bdf
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/macros25.rs
@@ -0,0 +1,13 @@
+macro_rules! t {
+    ($t:tt) => {
+        $t
+    };
+}
+
+fn frob() -> i32 {
+    t!(15) + t!((14))
+}
+
+fn main() -> i32 {
+    frob() - 29
+}
diff --git a/gcc/testsuite/rust/execute/torture/macros26.rs b/gcc/testsuite/rust/execute/torture/macros26.rs
new file mode 100644
index 00000000000..30f0beef0d9
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/macros26.rs
@@ -0,0 +1,12 @@
+macro_rules! count_tt {
+    ($t:tt) => { 1 };
+    ($t:tt $($ts:tt)*) => { 1 + count_tt!($($ts)*) };
+}
+
+fn main() -> i32 {
+    let count = count_tt!(1 2 let a = 15) + count_tt!(1 2 (let a = 15));
+    //                    ^ ^ ^^^ ^ ^ ^^              ^ ^ ^^^^^^^^^^^^
+    //                    6 token-trees               3 token-trees
+
+    count - 9
+}
diff --git a/gcc/testsuite/rust/execute/torture/macros27.rs b/gcc/testsuite/rust/execute/torture/macros27.rs
new file mode 100644
index 00000000000..d515bb278a0
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/macros27.rs
@@ -0,0 +1,24 @@
+// { dg-additional-options "-frust-cfg=A" }
+
+macro_rules! attr {
+    (#[$attr:meta] $s:stmt) => {
+        #[$attr]
+        $s;
+    };
+}
+
+fn main() -> i32 {
+    let mut a = 0;
+
+    attr! {
+    #[cfg(A)]
+        a = 3
+    };
+
+    attr! {
+    #[cfg(B)]
+        a = 40
+    };
+
+    a - 3
+}
diff --git a/gcc/testsuite/rust/execute/torture/macros28.rs b/gcc/testsuite/rust/execute/torture/macros28.rs
new file mode 100644
index 00000000000..b011f924149
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/macros28.rs
@@ -0,0 +1,13 @@
+macro_rules! t {
+    () => {
+        i32
+    };
+}
+
+fn id<T>(arg: T) -> T {
+    arg
+}
+
+fn main() -> i32 {
+    id::<t!()>(15) - 15
+}
diff --git a/gcc/testsuite/rust/execute/torture/macros29.rs b/gcc/testsuite/rust/execute/torture/macros29.rs
new file mode 100644
index 00000000000..306979b9b5b
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/macros29.rs
@@ -0,0 +1,24 @@
+// { dg-output "1\n" }
+#[rustc_builtin_macro]
+macro_rules! concat {
+    () => {{}};
+}
+
+extern "C" {
+    fn printf(fmt: *const i8, ...);
+}
+
+fn print(s: u32) {
+    unsafe {
+        printf("%u\n\0" as *const str as *const i8, s);
+    }
+}
+
+fn main() -> i32 {
+    let res = concat!("test2") == "test3";
+    if !res {
+        print(1);
+    }
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/macros3.rs b/gcc/testsuite/rust/execute/torture/macros3.rs
new file mode 100644
index 00000000000..00f6d253f50
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/macros3.rs
@@ -0,0 +1,61 @@
+// { dg-output "invok\ninvok\ninvok\ninvok\ninvok\n" }
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+fn f() {
+    unsafe {
+        let r_s = "invok\n\0";
+        let s_p = r_s as *const str;
+        let c_p = s_p as *const i8;
+
+        printf(c_p);
+    }
+}
+
+macro_rules! invocation0 {
+    (valid) => {
+        f();
+    };
+    () => {};
+}
+
+macro_rules! invocation1 {
+    (valid) => {};
+    () => {
+        f();
+    };
+}
+
+macro_rules! invocation2 {
+    (valid) => {
+        f();
+    };
+    (invalid) => {};
+}
+
+macro_rules! invocation3 {
+    (this is a valid invocation) => {
+        f();
+    };
+    (not this one) => {};
+}
+
+macro_rules! invocation4 {
+    (fn f() {}) => {
+        f();
+    };
+    (not a keyword) => {};
+}
+
+fn main() -> i32 {
+    invocation0!(valid);
+    invocation1!();
+    invocation2!(valid);
+    invocation3!(this is a valid invocation);
+    invocation4!(
+        fn f() {}
+    );
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/macros30.rs b/gcc/testsuite/rust/execute/torture/macros30.rs
new file mode 100644
index 00000000000..ab36f5e78af
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/macros30.rs
@@ -0,0 +1,25 @@
+// { dg-output "1\n" }
+#[rustc_builtin_macro]
+macro_rules! concat {
+    () => {{}};
+}
+
+extern "C" {
+    fn printf(fmt: *const i8, ...);
+}
+
+fn print(s: u32) {
+    unsafe {
+        printf("%u\n\0" as *const str as *const i8, s);
+    }
+}
+
+fn main() -> i32 {
+    let mut x = concat!("x");
+    x = concat!("y");
+    if x == "y" {
+        print(1);
+    }
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/macros31.rs b/gcc/testsuite/rust/execute/torture/macros31.rs
new file mode 100644
index 00000000000..483f897a92b
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/macros31.rs
@@ -0,0 +1,32 @@
+// { dg-additional-options "-w -frust-cfg=A" }
+// { dg-output "A\nB\n" }
+#[rustc_builtin_macro]
+macro_rules! cfg {
+    () => {{}};
+}
+
+extern "C" {
+    fn printf(fmt: *const i8, ...);
+}
+
+fn print(s: &str) {
+    unsafe {
+        printf(
+            "%s\n" as *const str as *const i8,
+            s as *const str as *const i8,
+        );
+    }
+}
+
+fn main() -> i32 {
+    let cfg = cfg!(A) || cfg!(B);
+    if cfg {
+        print("A");
+    }
+    let cfg = cfg!(A) && cfg!(B);
+    if !cfg {
+        print("B");
+    }
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/macros4.rs b/gcc/testsuite/rust/execute/torture/macros4.rs
new file mode 100644
index 00000000000..3303bfa58aa
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/macros4.rs
@@ -0,0 +1,15 @@
+macro_rules! add {
+    ($a:expr,$b:expr) => {
+        $a + $b
+    };
+    ($a:expr) => {
+        $a
+    };
+}
+
+fn main() -> i32 {
+    let mut x = add!(1);
+    x += add!(2, 3);
+
+    x - 6
+}
diff --git a/gcc/testsuite/rust/execute/torture/macros5.rs b/gcc/testsuite/rust/execute/torture/macros5.rs
new file mode 100644
index 00000000000..822665494a4
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/macros5.rs
@@ -0,0 +1,13 @@
+macro_rules! add {
+    ($a:expr,$b:expr) => {{
+        $a + $b
+    }};
+}
+
+fn test() -> i32 {
+    add!(1, 2)
+}
+
+fn main() -> i32 {
+    test() - 3
+}
diff --git a/gcc/testsuite/rust/execute/torture/macros6.rs b/gcc/testsuite/rust/execute/torture/macros6.rs
new file mode 100644
index 00000000000..652a765d5a8
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/macros6.rs
@@ -0,0 +1,12 @@
+macro_rules! Test {
+    ($a:ident, $b:ty) => {
+        struct $a($b);
+    };
+}
+
+Test!(Foo, i32);
+
+fn main() -> i32 {
+    let a = Foo(123);
+    a.0 - 123
+}
diff --git a/gcc/testsuite/rust/execute/torture/macros7.rs b/gcc/testsuite/rust/execute/torture/macros7.rs
new file mode 100644
index 00000000000..ed1f922f581
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/macros7.rs
@@ -0,0 +1,28 @@
+// { dg-output "any\nany\nany\n" }
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+fn f() {
+    let r_s = "any\n\0";
+    let s_p = r_s as *const str;
+    let c_p = s_p as *const i8;
+
+    unsafe {
+        printf(c_p);
+    }
+}
+
+macro_rules! any {
+    ($($a:expr)*) => {
+        f();
+    };
+}
+
+fn main() -> i32 {
+    any!();
+    any!(a + b);
+    any!(a + b    14 "gcc");
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/macros8.rs b/gcc/testsuite/rust/execute/torture/macros8.rs
new file mode 100644
index 00000000000..a12aca4910e
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/macros8.rs
@@ -0,0 +1,27 @@
+// { dg-output "zo1\nzo1\n" }
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+fn f() {
+    let r_s = "zo1\n\0";
+    let s_p = r_s as *const str;
+    let c_p = s_p as *const i8;
+
+    unsafe {
+        printf(c_p);
+    }
+}
+
+macro_rules! zero_or_one {
+    ($($a:expr)?) => {
+        f();
+    };
+}
+
+fn main() -> i32 {
+    zero_or_one!();
+    zero_or_one!(f());
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/macros9.rs b/gcc/testsuite/rust/execute/torture/macros9.rs
new file mode 100644
index 00000000000..0e3fd24e8a9
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/macros9.rs
@@ -0,0 +1,28 @@
+// { dg-output "oom\noom\noom\n" }
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+fn f() {
+    let r_s = "oom\n\0";
+    let s_p = r_s as *const str;
+    let c_p = s_p as *const i8;
+
+    unsafe {
+        printf(c_p);
+    }
+}
+
+macro_rules! one_or_more {
+    ($($a:expr)+) => {
+        f();
+    };
+}
+
+fn main() -> i32 {
+    one_or_more!(f());
+    one_or_more!(f() f());
+    one_or_more!(f() f() 15 + 12);
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/match1.rs b/gcc/testsuite/rust/execute/torture/match1.rs
new file mode 100644
index 00000000000..e5af512f15d
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/match1.rs
@@ -0,0 +1,58 @@
+// { dg-output "Foo::A\nFoo::B\nFoo::C x\nFoo::D 20 80\n" }
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+enum Foo {
+    A,
+    B,
+    C(char),
+    D { x: i32, y: i32 },
+}
+
+fn inspect(f: Foo) {
+    match f {
+        Foo::A => unsafe {
+            let a = "Foo::A\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        },
+        Foo::B => unsafe {
+            let a = "Foo::B\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        },
+        Foo::C(x) => unsafe {
+            let a = "Foo::C %c\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c, x);
+        },
+        Foo::D { x, y } => unsafe {
+            let a = "Foo::D %i %i\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c, x, y);
+        },
+    }
+}
+
+fn main() -> i32 {
+    let a = Foo::A;
+    let b = Foo::B;
+    let c = Foo::C('x');
+    let d = Foo::D { x: 20, y: 80 };
+
+    inspect(a);
+    inspect(b);
+    inspect(c);
+    inspect(d);
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/match2.rs b/gcc/testsuite/rust/execute/torture/match2.rs
new file mode 100644
index 00000000000..02cedf29b3c
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/match2.rs
@@ -0,0 +1,41 @@
+// { dg-output "123\n80\n" }
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+enum Foo {
+    C(i32),
+    D { x: i32, y: i32 },
+}
+
+fn inspect(f: Foo) -> i32 {
+    match f {
+        Foo::C(x) => x,
+        Foo::D { x, y } => y,
+    }
+}
+
+fn main() -> i32 {
+    let a = Foo::C(123);
+    let b = Foo::D { x: 20, y: 80 };
+
+    let result = inspect(a);
+    unsafe {
+        let a = "%i\n\0";
+        let b = a as *const str;
+        let c = b as *const i8;
+
+        printf(c, result);
+    }
+
+    let result = inspect(b);
+    unsafe {
+        let a = "%i\n\0";
+        let b = a as *const str;
+        let c = b as *const i8;
+
+        printf(c, result);
+    }
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/match3.rs b/gcc/testsuite/rust/execute/torture/match3.rs
new file mode 100644
index 00000000000..8cded3044df
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/match3.rs
@@ -0,0 +1,51 @@
+// { dg-output "Foo::A\nwildcard\nwildcard\nFoo::D 20 80\n" }
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+enum Foo {
+    A,
+    B,
+    C(char),
+    D { x: i32, y: i32 },
+}
+
+fn inspect(f: Foo) {
+    match f {
+        Foo::A => unsafe {
+            let a = "Foo::A\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        },
+        Foo::D { x, y } => unsafe {
+            let a = "Foo::D %i %i\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c, x, y);
+        },
+        _ => unsafe {
+            let a = "wildcard\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        },
+    }
+}
+
+fn main() -> i32 {
+    let a = Foo::A;
+    let b = Foo::B;
+    let c = Foo::C('x');
+    let d = Foo::D { x: 20, y: 80 };
+
+    inspect(a);
+    inspect(b);
+    inspect(c);
+    inspect(d);
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/match_bool1.rs b/gcc/testsuite/rust/execute/torture/match_bool1.rs
new file mode 100644
index 00000000000..101dbb58571
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/match_bool1.rs
@@ -0,0 +1,49 @@
+// { dg-output "182 is more than 100\n55 is less than 100\n" }
+
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+fn foo(x: bool) -> i32 {
+    match x {
+        true => {
+            return 182;
+        }
+        false => {
+            return 55;
+        }
+    }
+}
+
+fn bar(y: i32) {
+    match y < 100 {
+        true => {
+            let a = "%i is less than 100\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            unsafe {
+                printf(c, y);
+            }
+        }
+        _ => {
+            let a = "%i is more than 100\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            unsafe {
+                printf(c, y);
+            }
+        }
+    }
+}
+
+fn main() -> i32 {
+    let a = foo(true);
+    let b = foo(false);
+
+    bar(a);
+    bar(b);
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/match_byte1.rs b/gcc/testsuite/rust/execute/torture/match_byte1.rs
new file mode 100644
index 00000000000..3546cfb9d8b
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/match_byte1.rs
@@ -0,0 +1,56 @@
+// { dg-output "a\nseven\nquote\nelse" }
+
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+fn foo(x: u8) {
+    match x {
+        b'a' => {
+            let a = "a\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+            unsafe {
+                printf(c);
+            }
+        }
+
+        b'\x07' => {
+            let a = "seven\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+            unsafe {
+                printf(c);
+            }
+        }
+
+        b'\'' => {
+            let a = "quote\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+            unsafe {
+                printf(c);
+            }
+        }
+
+        _ => {
+            let a = "else\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+            unsafe {
+                printf(c);
+            }
+        }
+    }
+}
+
+fn main() -> i32 {
+    let x: u8 = 7;
+
+    foo(b'a');
+    foo(x);
+    foo(b'\'');
+    foo(b'\\');
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/match_char1.rs b/gcc/testsuite/rust/execute/torture/match_char1.rs
new file mode 100644
index 00000000000..fa65876a907
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/match_char1.rs
@@ -0,0 +1,56 @@
+// { dg-output "amazing\nwildcard\ncompiler\nproductivity\n" }
+
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+fn foo(x: char) {
+    match x {
+        'a' => {
+            let a = "amazing\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+            unsafe {
+                printf(c);
+            }
+        }
+
+        'c' => {
+            let a = "compiler\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+            unsafe {
+                printf(c);
+            }
+        }
+
+        'p' => {
+            let a = "productivity\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+            unsafe {
+                printf(c);
+            }
+        }
+
+        _ => {
+            let a = "wildcard\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+            unsafe {
+                printf(c);
+            }
+        }
+    }
+}
+
+fn main() -> i32 {
+    let p = 'p';
+
+    foo('a');
+    foo('b');
+    foo('c');
+    foo(p);
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/match_int1.rs b/gcc/testsuite/rust/execute/torture/match_int1.rs
new file mode 100644
index 00000000000..209429added
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/match_int1.rs
@@ -0,0 +1,109 @@
+// { dg-output "other!\nother!\nother!\nfifteen!\nfifteen!\nother!\nother!\nfifteen!\n" }
+
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+fn foo_i32(x: i32) {
+    match x {
+        15 => {
+            let a = "fifteen!\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+            unsafe {
+                printf(c);
+            }
+        }
+
+        _ => {
+            let a = "other!\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+            unsafe {
+                printf(c);
+            }
+        }
+    }
+}
+
+fn foo_isize(x: isize) {
+    match x {
+        15 => {
+            let a = "fifteen!\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+            unsafe {
+                printf(c);
+            }
+        }
+
+        _ => {
+            let a = "other!\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+            unsafe {
+                printf(c);
+            }
+        }
+    }
+}
+
+fn foo_u32(x: u32) {
+    match x {
+        15 => {
+            let a = "fifteen!\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+            unsafe {
+                printf(c);
+            }
+        }
+
+        _ => {
+            let a = "other!\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+            unsafe {
+                printf(c);
+            }
+        }
+    }
+}
+
+fn foo_usize(x: usize) {
+    match x {
+        15 => {
+            let a = "fifteen!\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+            unsafe {
+                printf(c);
+            }
+        }
+
+        _ => {
+            let a = "other!\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+            unsafe {
+                printf(c);
+            }
+        }
+    }
+}
+
+fn main() -> i32 {
+    let x = -2;
+    foo_i32(x);
+    foo_i32(334);
+    foo_isize(-4768);
+    foo_isize(15);
+
+    let y = 127;
+    foo_u32(15);
+    foo_u32(y);
+    foo_usize(2394);
+    foo_usize(15);
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/match_loop1.rs b/gcc/testsuite/rust/execute/torture/match_loop1.rs
new file mode 100644
index 00000000000..bb6aee946f6
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/match_loop1.rs
@@ -0,0 +1,56 @@
+// { dg-output "E::One\nE::Two\nbreak!\n" }
+
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+enum E {
+    One,
+    Two,
+    Other,
+}
+
+fn foo() {
+    let mut x = E::One;
+
+    loop {
+        match x {
+            E::One => {
+                let a = "E::One\n\0";
+                let b = a as *const str;
+                let c = b as *const i8;
+                unsafe {
+                    printf(c);
+                }
+
+                x = E::Two;
+            }
+            E::Two => {
+                let a = "E::Two\n\0";
+                let b = a as *const str;
+                let c = b as *const i8;
+                unsafe {
+                    printf(c);
+                }
+
+                x = E::Other;
+            }
+            _ => {
+                let a = "break!\n\0";
+                let b = a as *const str;
+                let c = b as *const i8;
+                unsafe {
+                    printf(c);
+                }
+
+                break;
+            }
+        }
+    }
+}
+
+fn main() -> i32 {
+    foo();
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/match_range1.rs b/gcc/testsuite/rust/execute/torture/match_range1.rs
new file mode 100644
index 00000000000..82e9e34a989
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/match_range1.rs
@@ -0,0 +1,37 @@
+// { dg-output "zero to END_RANGE\nzero to END_RANGE\nelse\n" }
+
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+const END_RANGE: i32 = 15;
+
+fn foo(x: i32) {
+    match x {
+        0..=END_RANGE => {
+            let a = "zero to END_RANGE\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+            unsafe {
+                printf(c);
+            }
+        }
+
+        _ => {
+            let a = "else\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+            unsafe {
+                printf(c);
+            }
+        }
+    }
+}
+
+fn main() -> i32 {
+    foo(11);
+    foo(15);
+    foo(21);
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/match_range2.rs b/gcc/testsuite/rust/execute/torture/match_range2.rs
new file mode 100644
index 00000000000..8153f9e1c7e
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/match_range2.rs
@@ -0,0 +1,45 @@
+// { dg-output "lowercase\nuppercase\nother\n" }
+
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+const BIG_A: char = 'A';
+const BIG_Z: char = 'Z';
+
+fn bar(x: char) {
+    match x {
+        'a'..='z' => {
+            let a = "lowercase\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+            unsafe {
+                printf(c);
+            }
+        }
+        BIG_A..=BIG_Z => {
+            let a = "uppercase\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+            unsafe {
+                printf(c);
+            }
+        }
+        _ => {
+            let a = "other\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+            unsafe {
+                printf(c);
+            }
+        }
+    }
+}
+
+fn main() -> i32 {
+    bar('b');
+    bar('X');
+    bar('!');
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/match_tuple1.rs b/gcc/testsuite/rust/execute/torture/match_tuple1.rs
new file mode 100644
index 00000000000..cb61cc0847c
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/match_tuple1.rs
@@ -0,0 +1,45 @@
+// { dg-output "x:15\ny:20\n" }
+
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+enum Foo {
+    A,
+    B,
+}
+
+fn inspect(f: Foo, g: u8) -> i32 {
+    match (f, g) {
+        (Foo::A, 1) => {
+            return 5;
+        }
+
+        (Foo::A, 2) => {
+            return 10;
+        }
+
+        (Foo::B, 2) => {
+            return 15;
+        }
+
+        _ => {
+            return 20;
+        }
+    }
+    return 25;
+}
+
+fn main() -> i32 {
+    let x = inspect(Foo::B, 2);
+    let y = inspect(Foo::B, 1);
+
+    unsafe {
+        printf("x:%d\n" as *const str as *const i8, x);
+    }
+    unsafe {
+        printf("y:%d\n" as *const str as *const i8, y);
+    }
+
+    y - x - 5
+}
diff --git a/gcc/testsuite/rust/execute/torture/method1.rs b/gcc/testsuite/rust/execute/torture/method1.rs
new file mode 100644
index 00000000000..6af6133939b
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/method1.rs
@@ -0,0 +1,27 @@
+/* { dg-output "124\n458" } */
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+struct Foo(i32);
+impl Foo {
+    fn bar(&self, i: i32) {
+        unsafe {
+            let a = "%i\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c, self.0 + i);
+        }
+    }
+}
+
+fn main() -> i32 {
+    let a = Foo(123);
+    a.bar(1);
+
+    let b = &Foo(456);
+    b.bar(2);
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/method2.rs b/gcc/testsuite/rust/execute/torture/method2.rs
new file mode 100644
index 00000000000..f532b4488c6
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/method2.rs
@@ -0,0 +1,76 @@
+// { dg-additional-options "-w" }
+// { dg-output "foo_deref\nimm_deref\n" }
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+#[lang = "deref"]
+pub trait Deref {
+    type Target;
+
+    fn deref(&self) -> &Self::Target;
+}
+
+impl<T> Deref for &T {
+    type Target = T;
+
+    fn deref(&self) -> &T {
+        unsafe {
+            let a = "imm_deref\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        }
+
+        *self
+    }
+}
+
+impl<T> Deref for &mut T {
+    type Target = T;
+
+    fn deref(&self) -> &T {
+        unsafe {
+            let a = "mut_deref\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        }
+
+        *self
+    }
+}
+
+struct Bar(i32);
+impl Bar {
+    fn foobar(self) -> i32 {
+        self.0
+    }
+}
+
+struct Foo<T>(T);
+impl<T> Deref for Foo<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        unsafe {
+            let a = "foo_deref\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        }
+
+        &self.0
+    }
+}
+
+pub fn main() -> i32 {
+    let bar = Bar(123);
+    let foo: Foo<&Bar> = Foo(&bar);
+    let foobar: i32 = foo.foobar();
+
+    foobar - 123
+}
diff --git a/gcc/testsuite/rust/execute/torture/method3.rs b/gcc/testsuite/rust/execute/torture/method3.rs
new file mode 100644
index 00000000000..0e9e8ff42a0
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/method3.rs
@@ -0,0 +1,78 @@
+// { dg-additional-options "-w" }
+// { dg-output "mut_deref\nfoobar: 123\n" }
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+#[lang = "deref"]
+pub trait Deref {
+    type Target;
+
+    fn deref(&self) -> &Self::Target;
+}
+
+#[lang = "deref_mut"]
+pub trait DerefMut: Deref {
+    fn deref_mut(&mut self) -> &mut Self::Target;
+}
+
+impl<T> Deref for &T {
+    type Target = T;
+
+    fn deref(&self) -> &T {
+        *self
+    }
+}
+
+impl<T> Deref for &mut T {
+    type Target = T;
+    fn deref(&self) -> &T {
+        *self
+    }
+}
+
+pub struct Bar(i32);
+impl Bar {
+    pub fn foobar(&mut self) -> i32 {
+        self.0
+    }
+}
+
+pub struct Foo<T>(T);
+impl<T> Deref for Foo<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+impl<T> DerefMut for Foo<T> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        unsafe {
+            let a = "mut_deref\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        }
+
+        &mut self.0
+    }
+}
+
+pub fn main() -> i32 {
+    let bar = Bar(123);
+    let mut foo: Foo<Bar> = Foo(bar);
+    let foobar = foo.foobar();
+
+    unsafe {
+        let a = "foobar: %i\n\0";
+        let b = a as *const str;
+        let c = b as *const i8;
+
+        printf(c, foobar);
+    }
+
+    foobar - 123
+}
diff --git a/gcc/testsuite/rust/execute/torture/method4.rs b/gcc/testsuite/rust/execute/torture/method4.rs
new file mode 100644
index 00000000000..5c6fdfe02c3
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/method4.rs
@@ -0,0 +1,78 @@
+// { dg-additional-options "-w" }
+// { dg-output "mut_deref\nfoobar: 123\n" }
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+#[lang = "deref"]
+pub trait Deref {
+    type Target;
+
+    fn deref(&self) -> &Self::Target;
+}
+
+#[lang = "deref_mut"]
+pub trait DerefMut: Deref {
+    fn deref_mut(&mut self) -> &mut Self::Target;
+}
+
+impl<T> Deref for &T {
+    type Target = T;
+
+    fn deref(&self) -> &T {
+        *self
+    }
+}
+
+impl<T> Deref for &mut T {
+    type Target = T;
+    fn deref(&self) -> &T {
+        *self
+    }
+}
+
+pub struct Bar(i32);
+impl Bar {
+    pub fn foobar(&mut self) -> i32 {
+        self.0
+    }
+}
+
+pub struct Foo<T>(T);
+impl<T> Deref for Foo<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+impl<T> DerefMut for Foo<T> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        unsafe {
+            let a = "mut_deref\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        }
+
+        &mut self.0
+    }
+}
+
+pub fn main() -> i32 {
+    let mut bar = Bar(123);
+    let mut foo: Foo<&mut Bar> = Foo(&mut bar);
+    let foobar = foo.foobar();
+
+    unsafe {
+        let a = "foobar: %i\n\0";
+        let b = a as *const str;
+        let c = b as *const i8;
+
+        printf(c, foobar);
+    }
+
+    foobar - 123
+}
diff --git a/gcc/testsuite/rust/execute/torture/mod1.rs b/gcc/testsuite/rust/execute/torture/mod1.rs
new file mode 100644
index 00000000000..700393850af
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/mod1.rs
@@ -0,0 +1,21 @@
+mod A {
+    pub mod B {
+        pub mod C {
+            pub struct Foo {
+                pub f: i32,
+            }
+            impl Foo {
+                pub fn new() -> Self {
+                    Foo { f: 23i32 }
+                }
+            }
+        }
+    }
+}
+
+fn main() -> i32 {
+    let a = A::B::C::Foo::new();
+    let b = A::B::C::Foo { f: -23i32 };
+
+    a.f + b.f
+}
diff --git a/gcc/testsuite/rust/execute/torture/modules/mod.rs b/gcc/testsuite/rust/execute/torture/modules/mod.rs
new file mode 100644
index 00000000000..9020aaf4bb8
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/modules/mod.rs
@@ -0,0 +1,3 @@
+fn return_12() -> i32 {
+    12
+}
diff --git a/gcc/testsuite/rust/execute/torture/operator_overload_1.rs b/gcc/testsuite/rust/execute/torture/operator_overload_1.rs
new file mode 100644
index 00000000000..5a28c5f4e93
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/operator_overload_1.rs
@@ -0,0 +1,36 @@
+/* { dg-output "3\n" } */
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+#[lang = "add"]
+pub trait Add<Rhs = Self> {
+    type Output;
+
+    fn add(self, rhs: Rhs) -> Self::Output;
+}
+
+impl Add for i32 {
+    type Output = i32;
+
+    fn add(self, other: i32) -> i32 {
+        let res = self + other;
+
+        unsafe {
+            let a = "%i\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c, res);
+        }
+
+        res
+    }
+}
+
+fn main() -> i32 {
+    let a;
+    a = 1 + 2;
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/operator_overload_10.rs b/gcc/testsuite/rust/execute/torture/operator_overload_10.rs
new file mode 100644
index 00000000000..f5d45db5338
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/operator_overload_10.rs
@@ -0,0 +1,75 @@
+/* { dg-output "foo_deref\n123\n" } */
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+#[lang = "deref"]
+pub trait Deref {
+    type Target;
+
+    fn deref(&self) -> &Self::Target;
+}
+
+impl<T> Deref for &T {
+    type Target = T;
+
+    fn deref(&self) -> &T {
+        unsafe {
+            let a = "imm_deref\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        }
+
+        *self
+    }
+}
+
+impl<T> Deref for &mut T {
+    type Target = T;
+
+    fn deref(&self) -> &T {
+        unsafe {
+            let a = "mut_deref\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        }
+
+        *self
+    }
+}
+
+struct Foo<T>(T);
+impl<T> Deref for Foo<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        unsafe {
+            let a = "foo_deref\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        }
+
+        &self.0
+    }
+}
+
+fn main() -> i32 {
+    let foo: Foo<i32> = Foo(123);
+    let bar: i32 = *foo;
+
+    unsafe {
+        let a = "%i\n\0";
+        let b = a as *const str;
+        let c = b as *const i8;
+
+        printf(c, bar);
+    }
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/operator_overload_11.rs b/gcc/testsuite/rust/execute/torture/operator_overload_11.rs
new file mode 100644
index 00000000000..1919941c486
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/operator_overload_11.rs
@@ -0,0 +1,37 @@
+// { dg-output "1\n" }
+// { dg-additional-options "-w" }
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+#[lang = "bitand"]
+pub trait BitAnd<Rhs = Self> {
+    type Output;
+
+    fn bitand(self, rhs: Rhs) -> Self::Output;
+}
+
+impl BitAnd for i32 {
+    type Output = i32;
+
+    fn bitand(self, other: i32) -> i32 {
+        let res = self & other;
+
+        unsafe {
+            let a = "%i\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c, res);
+        }
+
+        res
+    }
+}
+
+fn main() -> i32 {
+    let a;
+    a = 1 & 1;
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/operator_overload_12.rs b/gcc/testsuite/rust/execute/torture/operator_overload_12.rs
new file mode 100644
index 00000000000..7433330fa31
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/operator_overload_12.rs
@@ -0,0 +1,31 @@
+// { dg-output "1\n" }
+// { dg-additional-options "-w" }
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+#[lang = "bitand_assign"]
+pub trait BitAndAssign<Rhs = Self> {
+    fn bitand_assign(&mut self, rhs: Rhs);
+}
+
+impl BitAndAssign for i32 {
+    fn bitand_assign(&mut self, other: i32) {
+        *self &= other;
+
+        unsafe {
+            let a = "%i\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c, *self);
+        }
+    }
+}
+
+fn main() -> i32 {
+    let mut a = 1;
+    a &= 1;
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/operator_overload_2.rs b/gcc/testsuite/rust/execute/torture/operator_overload_2.rs
new file mode 100644
index 00000000000..a577718451d
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/operator_overload_2.rs
@@ -0,0 +1,38 @@
+/* { dg-output "3\n" } */
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+#[lang = "add"]
+pub trait Add<Rhs = Self> {
+    type Output;
+
+    fn add(self, rhs: Rhs) -> Self::Output;
+}
+
+struct Foo(i32);
+
+impl Add for Foo {
+    type Output = Foo;
+
+    fn add(self, other: Foo) -> Foo {
+        let res = Foo(self.0 + other.0);
+
+        unsafe {
+            let a = "%i\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c, res.0);
+        }
+
+        res
+    }
+}
+
+fn main() -> i32 {
+    let a;
+    a = Foo(1) + Foo(2);
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/operator_overload_3.rs b/gcc/testsuite/rust/execute/torture/operator_overload_3.rs
new file mode 100644
index 00000000000..57f58076c3e
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/operator_overload_3.rs
@@ -0,0 +1,55 @@
+/* { dg-output "3\n3\n" } */
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+#[lang = "add"]
+pub trait Add<Rhs = Self> {
+    type Output;
+
+    fn add(self, rhs: Rhs) -> Self::Output;
+}
+
+impl Add for i32 {
+    type Output = i32;
+
+    fn add(self, other: i32) -> i32 {
+        let res = self + other;
+
+        unsafe {
+            let a = "%i\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c, res);
+        }
+
+        res
+    }
+}
+
+struct Foo(i32);
+impl Add for Foo {
+    type Output = Foo;
+
+    fn add(self, other: Foo) -> Foo {
+        let res = Foo(self.0 + other.0);
+
+        unsafe {
+            let a = "%i\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c, res.0);
+        }
+
+        res
+    }
+}
+
+fn main() -> i32 {
+    let a;
+    a = Foo(1) + Foo(2);
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/operator_overload_4.rs b/gcc/testsuite/rust/execute/torture/operator_overload_4.rs
new file mode 100644
index 00000000000..ce9887b2ead
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/operator_overload_4.rs
@@ -0,0 +1,33 @@
+/* { dg-output "neg\n" } */
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+#[lang = "neg"]
+pub trait Neg {
+    type Output;
+
+    fn neg(self) -> Self::Output;
+}
+
+impl Neg for i32 {
+    type Output = i32;
+
+    fn neg(self) -> i32 {
+        unsafe {
+            let a = "neg\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        }
+        -self
+    }
+}
+
+fn main() -> i32 {
+    let a: i32 = 1;
+    let _b = -a;
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/operator_overload_5.rs b/gcc/testsuite/rust/execute/torture/operator_overload_5.rs
new file mode 100644
index 00000000000..a525f743680
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/operator_overload_5.rs
@@ -0,0 +1,33 @@
+/* { dg-output "not\n" } */
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+#[lang = "not"]
+pub trait Not {
+    type Output;
+
+    fn not(self) -> Self::Output;
+}
+
+impl Not for i32 {
+    type Output = i32;
+
+    fn not(self) -> i32 {
+        unsafe {
+            let a = "not\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        }
+        !self
+    }
+}
+
+fn main() -> i32 {
+    let a: i32 = 1;
+    let _b = !a;
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/operator_overload_6.rs b/gcc/testsuite/rust/execute/torture/operator_overload_6.rs
new file mode 100644
index 00000000000..fbd2a8fa9d3
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/operator_overload_6.rs
@@ -0,0 +1,37 @@
+/* { dg-output "add_assign\n3\n" } */
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+#[lang = "add_assign"]
+pub trait AddAssign<Rhs = Self> {
+    fn add_assign(&mut self, rhs: Rhs);
+}
+
+impl AddAssign for i32 {
+    fn add_assign(&mut self, other: i32) {
+        unsafe {
+            let a = "add_assign\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        }
+        *self += other
+    }
+}
+
+fn main() -> i32 {
+    let mut res = 1;
+    res += 2;
+
+    unsafe {
+        let a = "%i\n\0";
+        let b = a as *const str;
+        let c = b as *const i8;
+
+        printf(c, res);
+    }
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/operator_overload_7.rs b/gcc/testsuite/rust/execute/torture/operator_overload_7.rs
new file mode 100644
index 00000000000..886a7010efc
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/operator_overload_7.rs
@@ -0,0 +1,42 @@
+/* { dg-output "imm_deref\n123\n" } */
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+#[lang = "deref"]
+pub trait Deref {
+    type Target;
+
+    fn deref(&self) -> &Self::Target;
+}
+
+impl<T> Deref for &T {
+    type Target = T;
+
+    fn deref(&self) -> &T {
+        unsafe {
+            let a = "imm_deref\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        }
+
+        *self
+    }
+}
+
+fn main() -> i32 {
+    let foo: &i32 = &123;
+    let res: i32 = *foo;
+
+    unsafe {
+        let a = "%i\n\0";
+        let b = a as *const str;
+        let c = b as *const i8;
+
+        printf(c, res);
+    }
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/operator_overload_8.rs b/gcc/testsuite/rust/execute/torture/operator_overload_8.rs
new file mode 100644
index 00000000000..862e29a4bc6
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/operator_overload_8.rs
@@ -0,0 +1,58 @@
+/* { dg-output "imm_deref\n123\n" } */
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+#[lang = "deref"]
+pub trait Deref {
+    type Target;
+
+    fn deref(&self) -> &Self::Target;
+}
+
+impl<T> Deref for &T {
+    type Target = T;
+
+    fn deref(&self) -> &T {
+        unsafe {
+            let a = "imm_deref\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        }
+
+        *self
+    }
+}
+
+impl<T> Deref for &mut T {
+    type Target = T;
+
+    fn deref(&self) -> &T {
+        unsafe {
+            let a = "mut_deref\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        }
+
+        *self
+    }
+}
+
+fn main() -> i32 {
+    let foo: &i32 = &123;
+    let res: i32 = *foo;
+
+    unsafe {
+        let a = "%i\n\0";
+        let b = a as *const str;
+        let c = b as *const i8;
+
+        printf(c, res);
+    }
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/operator_overload_9.rs b/gcc/testsuite/rust/execute/torture/operator_overload_9.rs
new file mode 100644
index 00000000000..fd972e28ab3
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/operator_overload_9.rs
@@ -0,0 +1,58 @@
+/* { dg-output "mut_deref\n123\n" } */
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+#[lang = "deref"]
+pub trait Deref {
+    type Target;
+
+    fn deref(&self) -> &Self::Target;
+}
+
+impl<T> Deref for &T {
+    type Target = T;
+
+    fn deref(&self) -> &T {
+        unsafe {
+            let a = "imm_deref\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        }
+
+        *self
+    }
+}
+
+impl<T> Deref for &mut T {
+    type Target = T;
+
+    fn deref(&self) -> &T {
+        unsafe {
+            let a = "mut_deref\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        }
+
+        *self
+    }
+}
+
+fn main() -> i32 {
+    let foo = &mut 123;
+    let res: i32 = *foo;
+
+    unsafe {
+        let a = "%i\n\0";
+        let b = a as *const str;
+        let c = b as *const i8;
+
+        printf(c, res);
+    }
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/slice-magic.rs b/gcc/testsuite/rust/execute/torture/slice-magic.rs
new file mode 100644
index 00000000000..d1132989ddb
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/slice-magic.rs
@@ -0,0 +1,106 @@
+// { dg-additional-options "-w" }
+extern "rust-intrinsic" {
+    #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
+    pub fn offset<T>(dst: *const T, offset: isize) -> *const T;
+}
+
+struct FatPtr<T> {
+    data: *const T,
+    len: usize,
+}
+
+union Repr<T> {
+    rust: *const [T],
+    rust_mut: *mut [T],
+    raw: FatPtr<T>,
+}
+
+#[lang = "Range"]
+pub struct Range<Idx> {
+    pub start: Idx,
+    pub end: Idx,
+}
+
+#[lang = "const_slice_ptr"]
+impl<A> *const [A] {
+    pub const fn len(self) -> usize {
+        unsafe { Repr { rust: self }.raw.len }
+    }
+
+    pub const fn as_ptr(self) -> *const A {
+        self as *const A
+    }
+}
+
+#[lang = "const_ptr"]
+impl<B> *const B {
+    pub const unsafe fn offset(self, count: isize) -> *const B {
+        unsafe { offset(self, count) }
+    }
+
+    pub const unsafe fn add(self, count: usize) -> Self {
+        unsafe { self.offset(count as isize) }
+    }
+
+    pub const fn as_ptr(self) -> *const B {
+        self as *const B
+    }
+}
+
+const fn slice_from_raw_parts<C>(data: *const C, len: usize) -> *const [C] {
+    unsafe {
+        Repr {
+            raw: FatPtr { data, len },
+        }
+        .rust
+    }
+}
+
+#[lang = "index"]
+trait Index<Idx> {
+    type Output;
+
+    fn index(&self, index: Idx) -> &Self::Output;
+}
+
+pub unsafe trait SliceIndex<X> {
+    type Output;
+
+    unsafe fn get_unchecked(self, slice: *const X) -> *const Self::Output;
+
+    fn index(self, slice: &X) -> &Self::Output;
+}
+
+unsafe impl<Y> SliceIndex<[Y]> for Range<usize> {
+    type Output = [Y];
+
+    unsafe fn get_unchecked(self, slice: *const [Y]) -> *const [Y] {
+        unsafe {
+            let a: *const Y = slice.as_ptr();
+            let b: *const Y = a.add(self.start);
+            slice_from_raw_parts(b, self.end - self.start)
+        }
+    }
+
+    fn index(self, slice: &[Y]) -> &[Y] {
+        unsafe { &*self.get_unchecked(slice) }
+    }
+}
+
+impl<T, I> Index<I> for [T]
+where
+    I: SliceIndex<[T]>,
+{
+    type Output = I::Output;
+
+    fn index(&self, index: I) -> &I::Output {
+        index.index(self)
+    }
+}
+
+fn main() -> i32 {
+    let a = [1, 2, 3, 4, 5];
+    let b = &a[1..3];
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/slice-magic2.rs b/gcc/testsuite/rust/execute/torture/slice-magic2.rs
new file mode 100644
index 00000000000..64a566185fa
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/slice-magic2.rs
@@ -0,0 +1,106 @@
+// { dg-additional-options "-w" }
+extern "rust-intrinsic" {
+    #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
+    pub fn offset<T>(dst: *const T, offset: isize) -> *const T;
+}
+
+struct FatPtr<T> {
+    data: *const T,
+    len: usize,
+}
+
+union Repr<T> {
+    rust: *const [T],
+    rust_mut: *mut [T],
+    raw: FatPtr<T>,
+}
+
+#[lang = "Range"]
+pub struct Range<Idx> {
+    pub start: Idx,
+    pub end: Idx,
+}
+
+#[lang = "const_slice_ptr"]
+impl<T> *const [T] {
+    pub const fn len(self) -> usize {
+        unsafe { Repr { rust: self }.raw.len }
+    }
+
+    pub const fn as_ptr(self) -> *const T {
+        self as *const T
+    }
+}
+
+#[lang = "const_ptr"]
+impl<T> *const T {
+    pub const unsafe fn offset(self, count: isize) -> *const T {
+        unsafe { offset(self, count) }
+    }
+
+    pub const unsafe fn add(self, count: usize) -> Self {
+        unsafe { self.offset(count as isize) }
+    }
+
+    pub const fn as_ptr(self) -> *const T {
+        self as *const T
+    }
+}
+
+const fn slice_from_raw_parts<T>(data: *const T, len: usize) -> *const [T] {
+    unsafe {
+        Repr {
+            raw: FatPtr { data, len },
+        }
+        .rust
+    }
+}
+
+#[lang = "index"]
+trait Index<Idx> {
+    type Output;
+
+    fn index(&self, index: Idx) -> &Self::Output;
+}
+
+pub unsafe trait SliceIndex<T> {
+    type Output;
+
+    unsafe fn get_unchecked(self, slice: *const T) -> *const Self::Output;
+
+    fn index(self, slice: &T) -> &Self::Output;
+}
+
+unsafe impl<T> SliceIndex<[T]> for Range<usize> {
+    type Output = [T];
+
+    unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] {
+        unsafe {
+            let a: *const T = slice.as_ptr();
+            let b: *const T = a.add(self.start);
+            slice_from_raw_parts(b, self.end - self.start)
+        }
+    }
+
+    fn index(self, slice: &[T]) -> &[T] {
+        unsafe { &*self.get_unchecked(slice) }
+    }
+}
+
+impl<T, I> Index<I> for [T]
+where
+    I: SliceIndex<[T]>,
+{
+    type Output = I::Output;
+
+    fn index(&self, index: I) -> &I::Output {
+        index.index(self)
+    }
+}
+
+fn main() -> i32 {
+    let a = [1, 2, 3, 4, 5];
+    let b = &a[1..3];
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/slice1.rs b/gcc/testsuite/rust/execute/torture/slice1.rs
new file mode 100644
index 00000000000..a0488b3912c
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/slice1.rs
@@ -0,0 +1,27 @@
+// { dg-additional-options "-w" }
+struct FatPtr<T> {
+    data: *const T,
+    len: usize,
+}
+
+union Repr<T> {
+    rust: *const [T],
+    rust_mut: *mut [T],
+    raw: FatPtr<T>,
+}
+
+const fn slice_from_raw_parts<T>(data: *const T, len: usize) -> *const [T] {
+    unsafe {
+        let a = FatPtr { data, len };
+        let b = Repr { raw: a };
+        b.rust
+    }
+}
+
+fn main() -> i32 {
+    let a = 123;
+    let b: *const i32 = &a;
+    let c = slice_from_raw_parts(b, 1);
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/str-layout1.rs b/gcc/testsuite/rust/execute/torture/str-layout1.rs
new file mode 100644
index 00000000000..80bdc2a9c9f
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/str-layout1.rs
@@ -0,0 +1,57 @@
+// { dg-additional-options "-w" }
+// { dg-output "t1sz=5 t2sz=10" }
+mod mem {
+    extern "rust-intrinsic" {
+        #[rustc_const_stable(feature = "const_transmute", since = "1.46.0")]
+        fn transmute<T, U>(_: T) -> U;
+    }
+}
+
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+struct FatPtr<T> {
+    data: *const T,
+    len: usize,
+}
+
+pub union Repr<T> {
+    rust: *const [T],
+    rust_mut: *mut [T],
+    raw: FatPtr<T>,
+}
+
+impl<T> [T] {
+    pub const fn len(&self) -> usize {
+        unsafe { Repr { rust: self }.raw.len }
+    }
+}
+
+impl str {
+    pub const fn len(&self) -> usize {
+        self.as_bytes().len()
+    }
+
+    pub const fn as_bytes(&self) -> &[u8] {
+        unsafe { mem::transmute(self) }
+    }
+}
+
+fn main() -> i32 {
+    let t1: &str = "TEST1";
+    let t2: &str = &"TEST_12345";
+
+    let t1sz = t1.len();
+    let t2sz = t2.len();
+
+    unsafe {
+        let a = "t1sz=%i t2sz=%i\n";
+        let b = a as *const str;
+        let c = b as *const i8;
+
+        printf(c, t1sz as i32, t2sz as i32);
+    }
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/str-zero.rs b/gcc/testsuite/rust/execute/torture/str-zero.rs
new file mode 100644
index 00000000000..e7fba0d1372
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/str-zero.rs
@@ -0,0 +1,26 @@
+/* { dg-output "bar foo baz foobar\n" } */
+extern "C"
+{
+  fn printf(s: *const i8, ...);
+  fn memchr(s: *const i8, c: u8, n: usize) -> *const i8;
+}
+
+pub fn main () -> i32
+{
+  let f = "%s %s %s %s\n\0";
+  let s = "bar\0\
+           foo\
+           \x00\
+           baz\u{0000}\
+           foobar\0";
+  let cf = f as *const str as *const i8;
+  let cs = s as *const str as *const i8;
+  unsafe
+    {
+      let cs2 = memchr (cs, b'f', 5);
+      let cs3 = memchr (cs2, b'b', 5);
+      let cs4 = memchr (cs3, b'f', 5);
+      printf (cf, cs, cs2, cs3, cs4);
+    }
+  0
+}
diff --git a/gcc/testsuite/rust/execute/torture/trait1.rs b/gcc/testsuite/rust/execute/torture/trait1.rs
new file mode 100644
index 00000000000..dc3cc471c33
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/trait1.rs
@@ -0,0 +1,52 @@
+/* { dg-output "S::f\nT1::f\nT2::f\n" } */
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+struct S;
+
+impl S {
+    fn f() {
+        unsafe {
+            let a = "S::f\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        }
+    }
+}
+
+trait T1 {
+    fn f() {
+        unsafe {
+            let a = "T1::f\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        }
+    }
+}
+impl T1 for S {}
+
+trait T2 {
+    fn f() {
+        unsafe {
+            let a = "T2::f\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c);
+        }
+    }
+}
+impl T2 for S {}
+
+fn main() -> i32 {
+    S::f();
+    <S as T1>::f();
+    <S as T2>::f();
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/trait10.rs b/gcc/testsuite/rust/execute/torture/trait10.rs
new file mode 100644
index 00000000000..e581e324bbf
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/trait10.rs
@@ -0,0 +1,41 @@
+/* { dg-output "123\n" } */
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+struct Foo(i32);
+trait Bar {
+    fn baz(&self);
+}
+
+impl Bar for Foo {
+    fn baz(&self) {
+        unsafe {
+            let a = "%i\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c, self.0);
+        }
+    }
+}
+
+struct S;
+impl S {
+    fn dynamic_dispatch(self, t: &dyn Bar) {
+        // { dg-warning "unused name" "" { target *-*-* } .-1 }
+        t.baz();
+    }
+}
+
+pub fn main() -> i32 {
+    let a;
+    a = &Foo(123);
+
+    let b;
+    b = S;
+
+    b.dynamic_dispatch(a);
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/trait11.rs b/gcc/testsuite/rust/execute/torture/trait11.rs
new file mode 100644
index 00000000000..283c9ecd0ed
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/trait11.rs
@@ -0,0 +1,38 @@
+/* { dg-output "3\n" } */
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+trait FnLike<A, R> {
+    fn call(&self, arg: A) -> R;
+}
+
+struct S;
+impl<'a, T> FnLike<&'a T, &'a T> for S {
+    fn call(&self, arg: &'a T) -> &'a T {
+        // { dg-warning "unused name .self." "" { target *-*-* } .-1 }
+        arg
+    }
+}
+
+fn indirect<F>(f: F)
+where
+    F: for<'a> FnLike<&'a isize, &'a isize>,
+{
+    let x = 3;
+    let y = f.call(&x);
+
+    unsafe {
+        let a = "%i\n\0";
+        let b = a as *const str;
+        let c = b as *const i8;
+
+        printf(c, *y);
+    }
+}
+
+fn main() -> i32 {
+    indirect(S);
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/trait12.rs b/gcc/testsuite/rust/execute/torture/trait12.rs
new file mode 100644
index 00000000000..68b0a4014ad
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/trait12.rs
@@ -0,0 +1,38 @@
+/* { dg-output "3\n" } */
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+trait FnLike<A, R> {
+    fn call(&self, arg: A) -> R;
+}
+
+type FnObject<'b> = dyn for<'a> FnLike<&'a isize, &'a isize> + 'b;
+
+struct Identity;
+
+impl<'a, T> FnLike<&'a T, &'a T> for Identity {
+    fn call(&self, arg: &'a T) -> &'a T {
+        // { dg-warning "unused name .self." "" { target *-*-* } .-1 }
+        arg
+    }
+}
+
+fn call_repeatedly(f: &FnObject) {
+    let x = 3;
+    let y = f.call(&x);
+
+    unsafe {
+        let a = "%i\n\0";
+        let b = a as *const str;
+        let c = b as *const i8;
+
+        printf(c, *y);
+    }
+}
+
+fn main() -> i32 {
+    call_repeatedly(&Identity);
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/trait13.rs b/gcc/testsuite/rust/execute/torture/trait13.rs
new file mode 100644
index 00000000000..3071da27a6a
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/trait13.rs
@@ -0,0 +1,48 @@
+/* { dg-output "123\n456\n" } */
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+struct Foo(i32);
+trait Bar {
+    fn baz(&self);
+
+    fn qux(&self) {
+        // { dg-warning "unused name" "" { target *-*-* } .-1 }
+        unsafe {
+            let a = "%i\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c, 456);
+        }
+    }
+}
+
+impl Bar for Foo {
+    fn baz(&self) {
+        unsafe {
+            let a = "%i\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c, self.0);
+        }
+    }
+}
+
+fn dynamic_dispatch(t: &dyn Bar) {
+    t.baz();
+    t.qux();
+}
+
+fn main() -> i32 {
+    let a;
+    a = Foo(123);
+
+    let b: &dyn Bar;
+    b = &a;
+    dynamic_dispatch(b);
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/trait2.rs b/gcc/testsuite/rust/execute/torture/trait2.rs
new file mode 100644
index 00000000000..c96615fa891
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/trait2.rs
@@ -0,0 +1,37 @@
+/* { dg-output "Bar::A = 456\n<Foo as Bar>::A = 456\n" } */
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+trait Foo {
+    const A: i32 = 123;
+}
+
+struct Bar;
+impl Foo for Bar {
+    const A: i32 = 456;
+}
+
+fn main() -> i32 {
+    let a;
+    a = Bar::A;
+
+    unsafe {
+        let _a = "Bar::A = %i\n\0";
+        let _b = _a as *const str;
+        let _c = _b as *const i8;
+        printf(_c, a);
+    }
+
+    let b;
+    b = <Bar as Foo>::A;
+
+    unsafe {
+        let _a = "<Foo as Bar>::A = %i\n\0";
+        let _b = _a as *const str;
+        let _c = _b as *const i8;
+        printf(_c, b);
+    }
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/trait3.rs b/gcc/testsuite/rust/execute/torture/trait3.rs
new file mode 100644
index 00000000000..accfa9d0a36
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/trait3.rs
@@ -0,0 +1,43 @@
+/* { dg-output "123, 777" } */
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+trait A {
+    fn a() -> i32 {
+        123
+    }
+}
+
+trait B: A {
+    fn b() -> i32 {
+        <T as A>::a() + 456
+    }
+}
+
+struct T;
+// { dg-warning "struct is never constructed" "" { target *-*-* } .-1 }
+
+impl A for T {
+    fn a() -> i32 {
+        321
+    }
+}
+
+struct S;
+impl A for S {}
+impl B for S {}
+
+fn main() -> i32 {
+    let aa = S::a();
+    let bb = S::b();
+
+    unsafe {
+        let a = "%i, %i\n\0";
+        let b = a as *const str;
+        let c = b as *const i8;
+
+        printf(c, aa, bb);
+    }
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/trait4.rs b/gcc/testsuite/rust/execute/torture/trait4.rs
new file mode 100644
index 00000000000..8c0d257cd7e
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/trait4.rs
@@ -0,0 +1,34 @@
+/* { dg-output "123\n" }*/
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+struct Foo(i32);
+trait Bar {
+    fn baz(&self);
+}
+
+impl Bar for Foo {
+    fn baz(&self) {
+        unsafe {
+            let a = "%i\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c, self.0);
+        }
+    }
+}
+
+fn type_bound<T: Bar>(t: &T) {
+    t.baz();
+}
+
+fn main() -> i32 {
+    let a;
+
+    a = &Foo(123);
+    type_bound(a);
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/trait5.rs b/gcc/testsuite/rust/execute/torture/trait5.rs
new file mode 100644
index 00000000000..49f11a6085a
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/trait5.rs
@@ -0,0 +1,39 @@
+/* { dg-output "123\n123\n" } */
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+struct Foo(i32);
+trait Bar {
+    fn baz(&self);
+}
+
+impl Bar for Foo {
+    fn baz(&self) {
+        unsafe {
+            let a = "%i\n\0";
+            let b = a as *const str;
+            let c = b as *const i8;
+
+            printf(c, self.0);
+        }
+    }
+}
+
+fn static_dispatch<T: Bar>(t: &T) {
+    t.baz();
+}
+
+fn dynamic_dispatch(t: &dyn Bar) {
+    t.baz();
+}
+
+fn main() -> i32 {
+    let a = &Foo(123);
+    static_dispatch(a);
+
+    let b: &dyn Bar = a;
+    dynamic_dispatch(b);
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/trait6.rs b/gcc/testsuite/rust/execute/torture/trait6.rs
new file mode 100644
index 00000000000..c83d6666c87
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/trait6.rs
@@ -0,0 +1,39 @@
+/* { dg-output "123\n" } */
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+pub trait Foo {
+    type A;
+
+    fn bar(self) -> Self::A;
+}
+
+struct S(i32);
+impl Foo for S {
+    type A = i32;
+
+    fn bar(self) -> Self::A {
+        self.0
+    }
+}
+
+fn test_bar<T: Foo>(x: T) -> T::A {
+    x.bar()
+}
+
+fn main() -> i32 {
+    let a;
+    a = S(123);
+
+    let bar: i32 = test_bar::<S>(a);
+    unsafe {
+        let a = "%i\n\0";
+        let b = a as *const str;
+        let c = b as *const i8;
+
+        printf(c, bar);
+    }
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/trait7.rs b/gcc/testsuite/rust/execute/torture/trait7.rs
new file mode 100644
index 00000000000..064f88d5de9
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/trait7.rs
@@ -0,0 +1,39 @@
+/* { dg-output "123\n" } */
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+pub trait Foo {
+    type A;
+
+    fn bar(self) -> Self::A;
+}
+
+struct S(i32);
+impl Foo for S {
+    type A = i32;
+
+    fn bar(self) -> Self::A {
+        self.0
+    }
+}
+
+fn test_bar<T: Foo>(x: T) -> T::A {
+    x.bar()
+}
+
+fn main() -> i32 {
+    let a;
+    a = S(123);
+
+    let bar: i32 = test_bar(a);
+    unsafe {
+        let a = "%i\n\0";
+        let b = a as *const str;
+        let c = b as *const i8;
+
+        printf(c, bar);
+    }
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/trait8.rs b/gcc/testsuite/rust/execute/torture/trait8.rs
new file mode 100644
index 00000000000..14392ff0cca
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/trait8.rs
@@ -0,0 +1,39 @@
+/* { dg-output "123\n" } */
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+pub trait Foo {
+    type A;
+
+    fn bar(&self) -> Self::A;
+}
+
+struct S(i32);
+impl Foo for S {
+    type A = i32;
+
+    fn bar(&self) -> Self::A {
+        self.0
+    }
+}
+
+fn test_bar<T: Foo>(x: T) -> T::A {
+    x.bar()
+}
+
+fn main() -> i32 {
+    let a;
+    a = S(123);
+
+    let bar: i32 = test_bar(a);
+    unsafe {
+        let a = "%i\n\0";
+        let b = a as *const str;
+        let c = b as *const i8;
+
+        printf(c, bar);
+    }
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/trait9.rs b/gcc/testsuite/rust/execute/torture/trait9.rs
new file mode 100644
index 00000000000..c0e6d36f183
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/trait9.rs
@@ -0,0 +1,35 @@
+/* { dg-output "3\n" } */
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+trait FnLike<A, R> {
+    fn call(&self, arg: A) -> R;
+}
+
+struct S;
+impl<T> FnLike<&T, &T> for S {
+    fn call(&self, arg: &T) -> &T {
+        // { dg-warning "unused name .self." "" { target *-*-* } .-1 }
+        arg
+    }
+}
+
+fn indirect<F: FnLike<&isize, &isize>>(f: F) {
+    let x = 3;
+    let y = f.call(&x);
+
+    unsafe {
+        let a = "%i\n\0";
+        let b = a as *const str;
+        let c = b as *const i8;
+
+        printf(c, *y);
+    }
+}
+
+fn main() -> i32 {
+    indirect(S);
+
+    0
+}
diff --git a/gcc/testsuite/rust/execute/torture/transmute1.rs b/gcc/testsuite/rust/execute/torture/transmute1.rs
new file mode 100644
index 00000000000..b9ec38ca618
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/transmute1.rs
@@ -0,0 +1,23 @@
+// { dg-additional-options "-w" }
+
+extern "rust-intrinsic" {
+    fn transmute<T, U>(value: T) -> U;
+}
+
+struct WrapI {
+    inner: i32,
+}
+
+struct WrapF {
+    inner: f32,
+}
+
+fn main() -> i32 {
+    let f = 15.4f32;
+    let f_wrap = WrapF { inner: f };
+
+    let fst = unsafe { transmute::<f32, i32>(f) };
+    let snd = unsafe { transmute::<WrapF, WrapI>(f_wrap) };
+
+    fst - snd.inner
+}
diff --git a/gcc/testsuite/rust/execute/torture/wrapping_op1.rs b/gcc/testsuite/rust/execute/torture/wrapping_op1.rs
new file mode 100644
index 00000000000..64b37085ab7
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/wrapping_op1.rs
@@ -0,0 +1,14 @@
+extern "rust-intrinsic" {
+    pub fn wrapping_add<T>(l: T, r: T) -> T;
+}
+
+fn five() -> u8 {
+    5
+}
+
+fn main() -> u8 {
+    let l = 255;
+    let r = five();
+
+    unsafe { wrapping_add(l, r) - 4 }
+}
diff --git a/gcc/testsuite/rust/execute/torture/wrapping_op2.rs b/gcc/testsuite/rust/execute/torture/wrapping_op2.rs
new file mode 100644
index 00000000000..f9990157894
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/wrapping_op2.rs
@@ -0,0 +1,20 @@
+extern "rust-intrinsic" {
+    pub fn wrapping_add<T>(l: T, r: T) -> T;
+    pub fn wrapping_sub<T>(l: T, r: T) -> T;
+    pub fn wrapping_mul<T>(l: T, r: T) -> T;
+}
+
+fn five() -> u8 {
+    5
+}
+
+fn main() -> u8 {
+    let l = 255;
+    let r = five();
+
+    let ret0 = unsafe { wrapping_add(l, r) - 4 }; // 4
+    let ret1 = unsafe { wrapping_sub(r, l) - 6 }; // 6
+    let ret2 = unsafe { wrapping_mul(r, l) - 251 }; // 251
+
+    ret0 + ret1 + ret2
+}
diff --git a/gcc/testsuite/rust/execute/xfail/macro1.rs b/gcc/testsuite/rust/execute/xfail/macro1.rs
new file mode 100644
index 00000000000..eab5a0285cf
--- /dev/null
+++ b/gcc/testsuite/rust/execute/xfail/macro1.rs
@@ -0,0 +1,32 @@
+// { dg-output "macro\nmacro\nmacro\nmacro\n" }
+extern "C" {
+    fn printf(s: *const i8, ...);
+}
+
+fn f() {
+    let r_s = "macro\n\0";
+    let s_p = r_s as *const str;
+    let c_p = s_p as *const i8;
+
+    printf(c_p);
+}
+
+macro_rules! empty0 {
+    () => ( f() );
+}
+
+macro_rules! empty1 {
+    {} => { f() };
+}
+
+macro_rules! empty2 {
+    [] => [ f() ];
+}
+
+// using multiple parens/brackets/curlies variants allows us to make sure we
+// parse everything properly
+fn main() {
+    empty0!();
+    empty1!{};
+    empty2![];
+}
-- 
2.25.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [PATCH Rust front-end v2 07/37] gccrs: Add gcc-check-target check-rust
  2022-08-24 11:59 Rust frontend patches v2 herron.philip
                   ` (5 preceding siblings ...)
  2022-08-24 11:59 ` [PATCH Rust front-end v2 06/37] gccrs: Add execution " herron.philip
@ 2022-08-24 11:59 ` herron.philip
  2022-09-14 13:41   ` Richard Biener
  2022-08-24 11:59 ` [PATCH Rust front-end v2 08/37] gccrs: Add the Rust front-end AST data structures herron.philip
                   ` (30 subsequent siblings)
  37 siblings, 1 reply; 59+ messages in thread
From: herron.philip @ 2022-08-24 11:59 UTC (permalink / raw)
  To: gcc-patches; +Cc: gcc-rust, Philip Herron

From: Philip Herron <philip.herron@embecosm.com>

This allows us to invoke the rust testsuite.

ChangeLog:
	* Makefile.def: Add autogen target
	* Makefile.in: regenerate via autogen
---
 Makefile.def | 1 +
 Makefile.in  | 8 ++++++++
 2 files changed, 9 insertions(+)

diff --git a/Makefile.def b/Makefile.def
index 3291b126b26..821016af3a2 100644
--- a/Makefile.def
+++ b/Makefile.def
@@ -681,6 +681,7 @@ languages = { language=go;	gcc-check-target=check-go;
 languages = { language=d;	gcc-check-target=check-d;
 				lib-check-target=check-target-libphobos; };
 languages = { language=jit;	gcc-check-target=check-jit; };
+languages = { language=rust;	gcc-check-target=check-rust; };
 
 // Toplevel bootstrap
 bootstrap_stage = { id=1 ; };
diff --git a/Makefile.in b/Makefile.in
index 1919dfee829..9ed2c0dec52 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -60583,6 +60583,14 @@ check-gcc-jit:
 	(cd gcc && $(MAKE) $(GCC_FLAGS_TO_PASS) check-jit);
 check-jit: check-gcc-jit
 
+.PHONY: check-gcc-rust check-rust
+check-gcc-rust:
+	r=`${PWD_COMMAND}`; export r; \
+	s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
+	$(HOST_EXPORTS) \
+	(cd gcc && $(MAKE) $(GCC_FLAGS_TO_PASS) check-rust);
+check-rust: check-gcc-rust
+
 
 # The gcc part of install-no-fixedincludes, which relies on an intimate
 # knowledge of how a number of gcc internal targets (inter)operate.  Delegate.
-- 
2.25.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [PATCH Rust front-end v2 08/37] gccrs: Add the Rust front-end AST data structures
  2022-08-24 11:59 Rust frontend patches v2 herron.philip
                   ` (6 preceding siblings ...)
  2022-08-24 11:59 ` [PATCH Rust front-end v2 07/37] gccrs: Add gcc-check-target check-rust herron.philip
@ 2022-08-24 11:59 ` herron.philip
  2022-08-24 11:59 ` [PATCH Rust front-end v2 09/37] gccrs: Add Lexer for Rust front-end herron.philip
                   ` (29 subsequent siblings)
  37 siblings, 0 replies; 59+ messages in thread
From: herron.philip @ 2022-08-24 11:59 UTC (permalink / raw)
  To: gcc-patches; +Cc: gcc-rust, The Other, Philip Herron

From: The Other <simplytheother@gmail.com>

This is a full C++11 class hierarchy representing the Rust AST. We do not
allow dynamic_cast and so the main mechanism to work with the AST is by
using the visitor interface. Slowly we are adding TREE_CODE style node
types to the AST which will allow for more ways to work with the AST but
for now this is it.

See: https://doc.rust-lang.org/reference/items.html

Co-authored-by: Philip Herron <philip.herron@embecosm.com>
Co-authored-by: Arthur Cohen <arthur.cohen@embecosm.com
---
 gcc/rust/ast/rust-ast-dump.cc        | 1089 +++++
 gcc/rust/ast/rust-ast-dump.h         |  246 ++
 gcc/rust/ast/rust-ast-full-decls.h   |  273 ++
 gcc/rust/ast/rust-ast-full-test.cc   | 5814 ++++++++++++++++++++++++++
 gcc/rust/ast/rust-ast-full.h         |   31 +
 gcc/rust/ast/rust-ast-visitor.h      |  234 ++
 gcc/rust/ast/rust-ast.h              | 2007 +++++++++
 gcc/rust/ast/rust-cond-compilation.h |  249 ++
 gcc/rust/ast/rust-expr.h             | 4631 ++++++++++++++++++++
 gcc/rust/ast/rust-item.h             | 4382 +++++++++++++++++++
 gcc/rust/ast/rust-macro.h            |  958 +++++
 gcc/rust/ast/rust-path.h             | 1297 ++++++
 gcc/rust/ast/rust-pattern.h          | 1576 +++++++
 gcc/rust/ast/rust-stmt.h             |  358 ++
 gcc/rust/ast/rust-type.h             |  962 +++++
 gcc/rust/operator.h                  |   72 +
 16 files changed, 24179 insertions(+)
 create mode 100644 gcc/rust/ast/rust-ast-dump.cc
 create mode 100644 gcc/rust/ast/rust-ast-dump.h
 create mode 100644 gcc/rust/ast/rust-ast-full-decls.h
 create mode 100644 gcc/rust/ast/rust-ast-full-test.cc
 create mode 100644 gcc/rust/ast/rust-ast-full.h
 create mode 100644 gcc/rust/ast/rust-ast-visitor.h
 create mode 100644 gcc/rust/ast/rust-ast.h
 create mode 100644 gcc/rust/ast/rust-cond-compilation.h
 create mode 100644 gcc/rust/ast/rust-expr.h
 create mode 100644 gcc/rust/ast/rust-item.h
 create mode 100644 gcc/rust/ast/rust-macro.h
 create mode 100644 gcc/rust/ast/rust-path.h
 create mode 100644 gcc/rust/ast/rust-pattern.h
 create mode 100644 gcc/rust/ast/rust-stmt.h
 create mode 100644 gcc/rust/ast/rust-type.h
 create mode 100644 gcc/rust/operator.h

diff --git a/gcc/rust/ast/rust-ast-dump.cc b/gcc/rust/ast/rust-ast-dump.cc
new file mode 100644
index 00000000000..ad9ad0b7de7
--- /dev/null
+++ b/gcc/rust/ast/rust-ast-dump.cc
@@ -0,0 +1,1089 @@
+// 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/>.
+
+#include "rust-ast-dump.h"
+
+namespace Rust {
+namespace AST {
+
+Indent::Indent () : tabs (0) {}
+
+std::ostream &
+operator<< (std::ostream &stream, const Indent &indent)
+{
+  return stream << std::string (indent.tabs, '\t');
+}
+
+void
+Indent::increment ()
+{
+  tabs++;
+}
+
+void
+Indent::decrement ()
+{
+  rust_assert (tabs != 0);
+  tabs--;
+}
+
+Dump::Dump (std::ostream &stream) : stream (stream), indentation (Indent ()) {}
+
+void
+Dump::go (AST::Crate &crate)
+{
+  for (auto &item : crate.items)
+    {
+      stream << indentation;
+      item->accept_vis (*this);
+      stream << "\n";
+    }
+}
+
+void
+Dump::go (AST::Item &item)
+{
+  item.accept_vis (*this);
+}
+
+void
+Dump::format_function_param (FunctionParam &param)
+{
+  param.get_pattern ()->accept_vis (*this);
+  stream << ": ";
+  param.get_type ()->accept_vis (*this);
+}
+
+void
+Dump::emit_attrib (const Attribute &attrib)
+{
+  stream << "#";
+  stream << "[";
+
+  for (size_t i = 0; i < attrib.get_path ().get_segments ().size (); i++)
+    {
+      const auto &seg = attrib.get_path ().get_segments ().at (i);
+      bool has_next = (i + 1) < attrib.get_path ().get_segments ().size ();
+
+      stream << seg.get_segment_name ();
+      if (has_next)
+	stream << "::";
+    }
+
+  if (attrib.has_attr_input ())
+    {
+      stream << " = ";
+
+      bool is_literal = attrib.get_attr_input ().get_attr_input_type ()
+			== AST::AttrInput::AttrInputType::LITERAL;
+      if (is_literal)
+	{
+	  auto &literal
+	    = static_cast<AST::AttrInputLiteral &> (attrib.get_attr_input ());
+	  const auto &value = literal.get_literal ().as_string ();
+
+	  stream << "\"" << value << "\"";
+	}
+      else
+	{
+	  stream << "FIXME";
+	}
+    }
+
+  stream << "]";
+}
+
+void
+Dump::visit (Token &tok)
+{}
+
+void
+Dump::visit (DelimTokenTree &delim_tok_tree)
+{}
+
+void
+Dump::visit (AttrInputMetaItemContainer &input)
+{}
+
+void
+Dump::visit (IdentifierExpr &ident_expr)
+{
+  stream << ident_expr.get_ident ();
+}
+
+void
+Dump::visit (Lifetime &lifetime)
+{}
+
+void
+Dump::visit (LifetimeParam &lifetime_param)
+{}
+
+void
+Dump::visit (ConstGenericParam &lifetime_param)
+{}
+
+// rust-path.h
+void
+Dump::visit (PathInExpression &path)
+{}
+
+void
+Dump::visit (TypePathSegment &segment)
+{}
+
+void
+Dump::visit (TypePathSegmentGeneric &segment)
+{}
+
+void
+Dump::visit (TypePathSegmentFunction &segment)
+{}
+
+void
+Dump::visit (TypePath &path)
+{
+  stream << path.as_string ();
+}
+
+void
+Dump::visit (QualifiedPathInExpression &path)
+{}
+
+void
+Dump::visit (QualifiedPathInType &path)
+{}
+
+// rust-expr.h
+void
+Dump::visit (LiteralExpr &expr)
+{
+  stream << expr.as_string ();
+}
+
+void
+Dump::visit (AttrInputLiteral &attr_input)
+{}
+
+void
+Dump::visit (MetaItemLitExpr &meta_item)
+{}
+
+void
+Dump::visit (MetaItemPathLit &meta_item)
+{}
+
+void
+Dump::visit (BorrowExpr &expr)
+{}
+
+void
+Dump::visit (DereferenceExpr &expr)
+{}
+
+void
+Dump::visit (ErrorPropagationExpr &expr)
+{}
+
+void
+Dump::visit (NegationExpr &expr)
+{}
+
+void
+Dump::visit (ArithmeticOrLogicalExpr &expr)
+{
+  expr.get_left_expr ()->accept_vis (*this);
+  stream << " ";
+
+  switch (expr.get_expr_type ())
+    {
+    case ArithmeticOrLogicalOperator::ADD:
+      stream << "+";
+      break;
+
+    case ArithmeticOrLogicalOperator::SUBTRACT:
+      stream << "-";
+      break;
+
+    case ArithmeticOrLogicalOperator::MULTIPLY:
+      stream << "*";
+      break;
+
+    case ArithmeticOrLogicalOperator::DIVIDE:
+      stream << "/";
+      break;
+
+    case ArithmeticOrLogicalOperator::MODULUS:
+      stream << "%";
+      break;
+
+    case ArithmeticOrLogicalOperator::BITWISE_AND:
+      stream << "&";
+      break;
+
+    case ArithmeticOrLogicalOperator::BITWISE_OR:
+      stream << "|";
+      break;
+
+    case ArithmeticOrLogicalOperator::BITWISE_XOR:
+      stream << "^";
+      break;
+
+    case ArithmeticOrLogicalOperator::LEFT_SHIFT:
+      stream << "<<";
+      break;
+
+    case ArithmeticOrLogicalOperator::RIGHT_SHIFT:
+      stream << ">>";
+      break;
+    }
+
+  stream << " ";
+  expr.get_right_expr ()->accept_vis (*this);
+}
+
+void
+Dump::visit (ComparisonExpr &expr)
+{}
+
+void
+Dump::visit (LazyBooleanExpr &expr)
+{}
+
+void
+Dump::visit (TypeCastExpr &expr)
+{}
+
+void
+Dump::visit (AssignmentExpr &expr)
+{}
+
+void
+Dump::visit (CompoundAssignmentExpr &expr)
+{}
+
+void
+Dump::visit (GroupedExpr &expr)
+{}
+
+void
+Dump::visit (ArrayElemsValues &elems)
+{}
+
+void
+Dump::visit (ArrayElemsCopied &elems)
+{}
+
+void
+Dump::visit (ArrayExpr &expr)
+{}
+
+void
+Dump::visit (ArrayIndexExpr &expr)
+{}
+
+void
+Dump::visit (TupleExpr &expr)
+{}
+
+void
+Dump::visit (TupleIndexExpr &expr)
+{}
+
+void
+Dump::visit (StructExprStruct &expr)
+{}
+
+void
+Dump::visit (StructExprFieldIdentifier &field)
+{}
+
+void
+Dump::visit (StructExprFieldIdentifierValue &field)
+{}
+
+void
+Dump::visit (StructExprFieldIndexValue &field)
+{}
+
+void
+Dump::visit (StructExprStructFields &expr)
+{}
+
+void
+Dump::visit (StructExprStructBase &expr)
+{}
+
+void
+Dump::visit (CallExpr &expr)
+{}
+
+void
+Dump::visit (MethodCallExpr &expr)
+{}
+
+void
+Dump::visit (FieldAccessExpr &expr)
+{}
+
+void
+Dump::visit (ClosureExprInner &expr)
+{}
+
+void
+Dump::visit (BlockExpr &expr)
+{
+  stream << "{\n";
+  indentation.increment ();
+
+  for (auto &stmt : expr.get_statements ())
+    {
+      stream << indentation;
+      stmt->accept_vis (*this);
+      stream << ";\n";
+    }
+
+  if (expr.has_tail_expr ())
+    {
+      stream << indentation;
+      expr.get_tail_expr ()->accept_vis (*this);
+    }
+
+  indentation.decrement ();
+  stream << "\n" << indentation << "}\n";
+}
+
+void
+Dump::visit (ClosureExprInnerTyped &expr)
+{}
+
+void
+Dump::visit (ContinueExpr &expr)
+{}
+
+void
+Dump::visit (BreakExpr &expr)
+{}
+
+void
+Dump::visit (RangeFromToExpr &expr)
+{}
+
+void
+Dump::visit (RangeFromExpr &expr)
+{}
+
+void
+Dump::visit (RangeToExpr &expr)
+{}
+
+void
+Dump::visit (RangeFullExpr &expr)
+{}
+
+void
+Dump::visit (RangeFromToInclExpr &expr)
+{}
+
+void
+Dump::visit (RangeToInclExpr &expr)
+{}
+
+void
+Dump::visit (ReturnExpr &expr)
+{}
+
+void
+Dump::visit (UnsafeBlockExpr &expr)
+{}
+
+void
+Dump::visit (LoopExpr &expr)
+{}
+
+void
+Dump::visit (WhileLoopExpr &expr)
+{}
+
+void
+Dump::visit (WhileLetLoopExpr &expr)
+{}
+
+void
+Dump::visit (ForLoopExpr &expr)
+{}
+
+void
+Dump::visit (IfExpr &expr)
+{}
+
+void
+Dump::visit (IfExprConseqElse &expr)
+{}
+
+void
+Dump::visit (IfExprConseqIf &expr)
+{}
+
+void
+Dump::visit (IfExprConseqIfLet &expr)
+{}
+
+void
+Dump::visit (IfLetExpr &expr)
+{}
+
+void
+Dump::visit (IfLetExprConseqElse &expr)
+{}
+
+void
+Dump::visit (IfLetExprConseqIf &expr)
+{}
+
+void
+Dump::visit (IfLetExprConseqIfLet &expr)
+{}
+
+void
+Dump::visit (MatchExpr &expr)
+{}
+
+void
+Dump::visit (AwaitExpr &expr)
+{}
+
+void
+Dump::visit (AsyncBlockExpr &expr)
+{}
+
+// rust-item.h
+void
+Dump::visit (TypeParam &param)
+{
+  stream << param.get_type_representation ();
+  if (param.has_type ())
+    {
+      stream << " = ";
+      param.get_type ()->accept_vis (*this);
+    }
+}
+
+void
+Dump::visit (LifetimeWhereClauseItem &item)
+{}
+
+void
+Dump::visit (TypeBoundWhereClauseItem &item)
+{}
+
+void
+Dump::visit (Method &method)
+{
+  stream << indentation << "fn " << method.get_method_name () << '(';
+
+  auto &self = method.get_self_param ();
+  stream << self.as_string ();
+
+  auto &params = method.get_function_params ();
+  for (auto &param : params)
+    {
+      stream << ", ";
+      format_function_param (param);
+    }
+
+  stream << ") ";
+
+  if (method.has_return_type ())
+    {
+      stream << "-> ";
+      method.get_return_type ()->accept_vis (*this);
+      stream << " ";
+    }
+
+  auto &block = method.get_definition ();
+  if (!block)
+    stream << ';';
+  else
+    block->accept_vis (*this);
+
+  stream << '\n';
+}
+
+void
+Dump::visit (Module &module)
+{}
+
+void
+Dump::visit (ExternCrate &crate)
+{}
+
+void
+Dump::visit (UseTreeGlob &use_tree)
+{}
+
+void
+Dump::visit (UseTreeList &use_tree)
+{}
+
+void
+Dump::visit (UseTreeRebind &use_tree)
+{}
+
+void
+Dump::visit (UseDeclaration &use_decl)
+{}
+
+void
+Dump::visit (Function &function)
+{
+  stream << "fn " << function.get_function_name ();
+
+  if (function.has_generics ())
+    {
+      stream << "<";
+      for (size_t i = 0; i < function.get_generic_params ().size (); i++)
+	{
+	  auto &param = function.get_generic_params ().at (i);
+	  param->accept_vis (*this);
+
+	  bool has_next = (i + 1) < function.get_generic_params ().size ();
+	  if (has_next)
+	    stream << ", ";
+	}
+      stream << ">";
+    }
+
+  stream << '(';
+  auto &params = function.get_function_params ();
+  if (params.size () >= 1)
+    {
+      format_function_param (params[0]);
+      for (size_t i = 1; i < params.size (); i++)
+	{
+	  stream << ", ";
+	  format_function_param (params[i]);
+	}
+    }
+
+  stream << ") ";
+
+  if (function.has_return_type ())
+    {
+      stream << "-> ";
+      function.get_return_type ()->accept_vis (*this);
+      stream << " ";
+    }
+
+  auto &block = function.get_definition ();
+  if (!block)
+    stream << ';';
+  else
+    block->accept_vis (*this);
+
+  stream << '\n';
+}
+
+void
+Dump::visit (TypeAlias &type_alias)
+{}
+
+void
+Dump::visit (StructStruct &struct_item)
+{}
+
+void
+Dump::visit (TupleStruct &tuple_struct)
+{}
+
+void
+Dump::visit (EnumItem &item)
+{}
+
+void
+Dump::visit (EnumItemTuple &item)
+{}
+
+void
+Dump::visit (EnumItemStruct &item)
+{}
+
+void
+Dump::visit (EnumItemDiscriminant &item)
+{}
+
+void
+Dump::visit (Enum &enum_item)
+{}
+
+void
+Dump::visit (Union &union_item)
+{}
+
+void
+Dump::visit (ConstantItem &const_item)
+{}
+
+void
+Dump::visit (StaticItem &static_item)
+{}
+
+void
+Dump::format_function_common (std::unique_ptr<Type> &return_type,
+			      std::unique_ptr<BlockExpr> &block)
+{
+  if (return_type)
+    {
+      stream << "-> ";
+      return_type->accept_vis (*this);
+    }
+
+  if (block)
+    {
+      if (return_type)
+	stream << ' ';
+      block->accept_vis (*this);
+    }
+  else
+    stream << ";\n";
+}
+
+void
+Dump::visit (TraitItemFunc &item)
+{
+  auto func = item.get_trait_function_decl ();
+  stream << indentation << "fn " << func.get_identifier () << '(';
+
+  auto &params = func.get_function_params ();
+  for (auto &param : params)
+    {
+      stream << ", ";
+      format_function_param (param);
+    }
+
+  stream << ") ";
+
+  format_function_common (func.get_return_type (), item.get_definition ());
+}
+
+void
+Dump::visit (TraitItemMethod &item)
+{
+  auto method = item.get_trait_method_decl ();
+  stream << indentation << "fn " << method.get_identifier () << '(';
+
+  auto &self = method.get_self_param ();
+  stream << self.as_string ();
+
+  auto &params = method.get_function_params ();
+  for (auto &param : params)
+    {
+      stream << ", ";
+      format_function_param (param);
+    }
+
+  stream << ") ";
+
+  format_function_common (method.get_return_type (), item.get_definition ());
+}
+
+void
+Dump::visit (TraitItemConst &item)
+{
+  stream << indentation << "const " << item.get_identifier () << ": ";
+  item.get_type ()->accept_vis (*this);
+  stream << ";\n";
+}
+
+void
+Dump::visit (TraitItemType &item)
+{
+  stream << indentation << "type " << item.get_identifier () << ";\n";
+}
+
+void
+Dump::visit (Trait &trait)
+{
+  for (const auto &attr : trait.get_outer_attrs ())
+    {
+      emit_attrib (attr);
+      stream << "\n" << indentation;
+    }
+
+  stream << "trait " << trait.get_identifier ();
+
+  // Traits actually have an implicit Self thrown at the start so we must expect
+  // the number of generic params to be > 1
+  if (trait.get_generic_params ().size () > 1)
+    {
+      stream << "<";
+      for (size_t i = 1; i < trait.get_generic_params ().size (); i++)
+	{
+	  auto &param = trait.get_generic_params ().at (i);
+	  param->accept_vis (*this);
+
+	  bool has_next = (i + 1) < trait.get_generic_params ().size ();
+	  if (has_next)
+	    stream << ", ";
+	}
+      stream << ">";
+    }
+
+  stream << " {\n";
+
+  indentation.increment ();
+
+  for (auto &item : trait.get_trait_items ())
+    item->accept_vis (*this);
+
+  indentation.decrement ();
+  stream << "\n}\n";
+}
+
+void
+Dump::visit (InherentImpl &impl)
+{
+  stream << "impl ";
+
+  // FIXME: Handle generics
+
+  impl.get_type ()->accept_vis (*this);
+
+  // FIXME: Handle where-clause
+  // FIXME: Handle inner attributes
+
+  stream << " {\n";
+  indentation.increment ();
+
+  for (auto &item : impl.get_impl_items ())
+    item->accept_vis (*this);
+
+  indentation.decrement ();
+  stream << "\n}\n";
+}
+
+void
+Dump::visit (TraitImpl &impl)
+{
+  stream << "impl ";
+  impl.get_trait_path ().accept_vis (*this);
+  stream << " for ";
+  impl.get_type ()->accept_vis (*this);
+
+  stream << " {\n";
+  indentation.increment ();
+
+  for (auto &item : impl.get_impl_items ())
+    item->accept_vis (*this);
+
+  indentation.decrement ();
+  stream << "\n}\n";
+}
+
+void
+Dump::visit (ExternalStaticItem &item)
+{}
+
+void
+Dump::visit (ExternalFunctionItem &function)
+{
+  stream << "fn " << function.get_identifier () << '(';
+
+  for (size_t i = 0; i < function.get_function_params ().size (); i++)
+    {
+      auto &param = function.get_function_params ().at (i);
+      bool has_next = (i + 1) < function.get_function_params ().size ();
+
+      stream << param.get_name () << ": ";
+      param.get_type ()->accept_vis (*this);
+
+      if (has_next)
+	stream << ", ";
+    }
+
+  stream << ')';
+  if (function.has_return_type ())
+    {
+      stream << "-> ";
+      function.get_return_type ()->accept_vis (*this);
+    }
+}
+
+void
+Dump::visit (ExternBlock &block)
+{
+  stream << "extern ";
+
+  if (block.has_abi ())
+    {
+      stream << "\"";
+      stream << block.get_abi ();
+      stream << "\" ";
+    }
+
+  stream << "{\n";
+  indentation.increment ();
+
+  for (auto &item : block.get_extern_items ())
+    {
+      stream << indentation;
+      item->accept_vis (*this);
+      stream << ";\n";
+    }
+
+  indentation.decrement ();
+  stream << "\n" << indentation << "}\n";
+}
+
+// rust-macro.h
+void
+Dump::visit (MacroMatchFragment &match)
+{}
+
+void
+Dump::visit (MacroMatchRepetition &match)
+{}
+
+void
+Dump::visit (MacroMatcher &matcher)
+{}
+
+void
+Dump::visit (MacroRulesDefinition &rules_def)
+{}
+
+void
+Dump::visit (MacroInvocation &macro_invoc)
+{}
+
+void
+Dump::visit (MetaItemPath &meta_item)
+{}
+
+void
+Dump::visit (MetaItemSeq &meta_item)
+{}
+
+void
+Dump::visit (MetaWord &meta_item)
+{}
+
+void
+Dump::visit (MetaNameValueStr &meta_item)
+{}
+
+void
+Dump::visit (MetaListPaths &meta_item)
+{}
+
+void
+Dump::visit (MetaListNameValueStr &meta_item)
+{}
+
+// rust-pattern.h
+void
+Dump::visit (LiteralPattern &pattern)
+{}
+
+void
+Dump::visit (IdentifierPattern &pattern)
+{
+  stream << pattern.get_ident ();
+}
+
+void
+Dump::visit (WildcardPattern &pattern)
+{}
+
+// void Dump::visit(RangePatternBound& bound){}
+
+void
+Dump::visit (RangePatternBoundLiteral &bound)
+{}
+
+void
+Dump::visit (RangePatternBoundPath &bound)
+{}
+
+void
+Dump::visit (RangePatternBoundQualPath &bound)
+{}
+
+void
+Dump::visit (RangePattern &pattern)
+{}
+
+void
+Dump::visit (ReferencePattern &pattern)
+{}
+
+// void Dump::visit(StructPatternField& field){}
+
+void
+Dump::visit (StructPatternFieldTuplePat &field)
+{}
+
+void
+Dump::visit (StructPatternFieldIdentPat &field)
+{}
+
+void
+Dump::visit (StructPatternFieldIdent &field)
+{}
+
+void
+Dump::visit (StructPattern &pattern)
+{}
+
+// void Dump::visit(TupleStructItems& tuple_items){}
+
+void
+Dump::visit (TupleStructItemsNoRange &tuple_items)
+{}
+
+void
+Dump::visit (TupleStructItemsRange &tuple_items)
+{}
+
+void
+Dump::visit (TupleStructPattern &pattern)
+{}
+
+// void Dump::visit(TuplePatternItems& tuple_items){}
+
+void
+Dump::visit (TuplePatternItemsMultiple &tuple_items)
+{}
+
+void
+Dump::visit (TuplePatternItemsRanged &tuple_items)
+{}
+
+void
+Dump::visit (TuplePattern &pattern)
+{}
+
+void
+Dump::visit (GroupedPattern &pattern)
+{}
+
+void
+Dump::visit (SlicePattern &pattern)
+{}
+
+// rust-stmt.h
+void
+Dump::visit (EmptyStmt &stmt)
+{}
+
+void
+Dump::visit (LetStmt &stmt)
+{
+  stream << "let ";
+  auto &pattern = stmt.get_pattern ();
+  if (pattern)
+    pattern->accept_vis (*this);
+
+  if (stmt.has_type ())
+    {
+      stream << ": ";
+      stmt.get_type ()->accept_vis (*this);
+    }
+
+  if (stmt.has_init_expr ())
+    {
+      stream << " = ";
+      stmt.get_init_expr ()->accept_vis (*this);
+    }
+}
+
+void
+Dump::visit (ExprStmtWithoutBlock &stmt)
+{}
+
+void
+Dump::visit (ExprStmtWithBlock &stmt)
+{}
+
+// rust-type.h
+void
+Dump::visit (TraitBound &bound)
+{}
+
+void
+Dump::visit (ImplTraitType &type)
+{}
+
+void
+Dump::visit (TraitObjectType &type)
+{}
+
+void
+Dump::visit (ParenthesisedType &type)
+{}
+
+void
+Dump::visit (ImplTraitTypeOneBound &type)
+{}
+
+void
+Dump::visit (TraitObjectTypeOneBound &type)
+{}
+
+void
+Dump::visit (TupleType &type)
+{}
+
+void
+Dump::visit (NeverType &type)
+{}
+
+void
+Dump::visit (RawPointerType &type)
+{}
+
+void
+Dump::visit (ReferenceType &type)
+{
+  type.get_type_referenced ()->accept_vis (*this);
+}
+
+void
+Dump::visit (ArrayType &type)
+{
+  type.get_elem_type ()->accept_vis (*this);
+}
+
+void
+Dump::visit (SliceType &type)
+{
+  type.get_elem_type ()->accept_vis (*this);
+}
+
+void
+Dump::visit (InferredType &type)
+{
+  stream << "_";
+}
+
+void
+Dump::visit (BareFunctionType &type)
+{}
+
+} // namespace AST
+} // namespace Rust
diff --git a/gcc/rust/ast/rust-ast-dump.h b/gcc/rust/ast/rust-ast-dump.h
new file mode 100644
index 00000000000..c3854e8287d
--- /dev/null
+++ b/gcc/rust/ast/rust-ast-dump.h
@@ -0,0 +1,246 @@
+// 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/>.
+
+#include "rust-ast-visitor.h"
+#include "rust-ast.h"
+#include "rust-ast-full.h"
+
+#ifndef RUST_AST_DUMP_H
+#define RUST_AST_DUMP_H
+
+namespace Rust {
+namespace AST {
+
+// TODO: We might want to reuse this class somewhere else
+class Indent
+{
+public:
+  Indent ();
+
+  friend std::ostream &operator<< (std::ostream &stream, const Indent &indent);
+
+  void increment ();
+  void decrement ();
+
+private:
+  size_t tabs;
+};
+
+class Dump : public ASTVisitor
+{
+public:
+  Dump (std::ostream &stream);
+
+  /**
+   * Run the visitor on an entire crate and its items
+   */
+  void go (AST::Crate &crate);
+  void go (AST::Item &item);
+
+private:
+  std::ostream &stream;
+  Indent indentation;
+
+  // Format together common items of functions: Parameters, return type, block
+  void format_function_common (std::unique_ptr<Type> &return_type,
+			       std::unique_ptr<BlockExpr> &block);
+
+  /**
+   * Format a function's definition parameter
+   */
+  void format_function_param (FunctionParam &param);
+  void emit_attrib (const Attribute &attrib);
+
+  // rust-ast.h
+  void visit (Token &tok);
+  void visit (DelimTokenTree &delim_tok_tree);
+  void visit (AttrInputMetaItemContainer &input);
+  void visit (IdentifierExpr &ident_expr);
+  void visit (Lifetime &lifetime);
+  void visit (LifetimeParam &lifetime_param);
+  void visit (ConstGenericParam &const_param);
+
+  // rust-path.h
+  void visit (PathInExpression &path);
+  void visit (TypePathSegment &segment);
+  void visit (TypePathSegmentGeneric &segment);
+  void visit (TypePathSegmentFunction &segment);
+  void visit (TypePath &path);
+  void visit (QualifiedPathInExpression &path);
+  void visit (QualifiedPathInType &path);
+
+  // rust-expr.h
+  void visit (LiteralExpr &expr);
+  void visit (AttrInputLiteral &attr_input);
+  void visit (MetaItemLitExpr &meta_item);
+  void visit (MetaItemPathLit &meta_item);
+  void visit (BorrowExpr &expr);
+  void visit (DereferenceExpr &expr);
+  void visit (ErrorPropagationExpr &expr);
+  void visit (NegationExpr &expr);
+  void visit (ArithmeticOrLogicalExpr &expr);
+  void visit (ComparisonExpr &expr);
+  void visit (LazyBooleanExpr &expr);
+  void visit (TypeCastExpr &expr);
+  void visit (AssignmentExpr &expr);
+  void visit (CompoundAssignmentExpr &expr);
+  void visit (GroupedExpr &expr);
+  void visit (ArrayElemsValues &elems);
+  void visit (ArrayElemsCopied &elems);
+  void visit (ArrayExpr &expr);
+  void visit (ArrayIndexExpr &expr);
+  void visit (TupleExpr &expr);
+  void visit (TupleIndexExpr &expr);
+  void visit (StructExprStruct &expr);
+  void visit (StructExprFieldIdentifier &field);
+  void visit (StructExprFieldIdentifierValue &field);
+  void visit (StructExprFieldIndexValue &field);
+  void visit (StructExprStructFields &expr);
+  void visit (StructExprStructBase &expr);
+  void visit (CallExpr &expr);
+  void visit (MethodCallExpr &expr);
+  void visit (FieldAccessExpr &expr);
+  void visit (ClosureExprInner &expr);
+  void visit (BlockExpr &expr);
+  void visit (ClosureExprInnerTyped &expr);
+  void visit (ContinueExpr &expr);
+  void visit (BreakExpr &expr);
+  void visit (RangeFromToExpr &expr);
+  void visit (RangeFromExpr &expr);
+  void visit (RangeToExpr &expr);
+  void visit (RangeFullExpr &expr);
+  void visit (RangeFromToInclExpr &expr);
+  void visit (RangeToInclExpr &expr);
+  void visit (ReturnExpr &expr);
+  void visit (UnsafeBlockExpr &expr);
+  void visit (LoopExpr &expr);
+  void visit (WhileLoopExpr &expr);
+  void visit (WhileLetLoopExpr &expr);
+  void visit (ForLoopExpr &expr);
+  void visit (IfExpr &expr);
+  void visit (IfExprConseqElse &expr);
+  void visit (IfExprConseqIf &expr);
+  void visit (IfExprConseqIfLet &expr);
+  void visit (IfLetExpr &expr);
+  void visit (IfLetExprConseqElse &expr);
+  void visit (IfLetExprConseqIf &expr);
+  void visit (IfLetExprConseqIfLet &expr);
+  void visit (MatchExpr &expr);
+  void visit (AwaitExpr &expr);
+  void visit (AsyncBlockExpr &expr);
+
+  // rust-item.h
+  void visit (TypeParam &param);
+  void visit (LifetimeWhereClauseItem &item);
+  void visit (TypeBoundWhereClauseItem &item);
+  void visit (Method &method);
+  void visit (Module &module);
+  void visit (ExternCrate &crate);
+  void visit (UseTreeGlob &use_tree);
+  void visit (UseTreeList &use_tree);
+  void visit (UseTreeRebind &use_tree);
+  void visit (UseDeclaration &use_decl);
+  void visit (Function &function);
+  void visit (TypeAlias &type_alias);
+  void visit (StructStruct &struct_item);
+  void visit (TupleStruct &tuple_struct);
+  void visit (EnumItem &item);
+  void visit (EnumItemTuple &item);
+  void visit (EnumItemStruct &item);
+  void visit (EnumItemDiscriminant &item);
+  void visit (Enum &enum_item);
+  void visit (Union &union_item);
+  void visit (ConstantItem &const_item);
+  void visit (StaticItem &static_item);
+  void visit (TraitItemFunc &item);
+  void visit (TraitItemMethod &item);
+  void visit (TraitItemConst &item);
+  void visit (TraitItemType &item);
+  void visit (Trait &trait);
+  void visit (InherentImpl &impl);
+  void visit (TraitImpl &impl);
+  void visit (ExternalStaticItem &item);
+  void visit (ExternalFunctionItem &item);
+  void visit (ExternBlock &block);
+
+  // rust-macro.h
+  void visit (MacroMatchFragment &match);
+  void visit (MacroMatchRepetition &match);
+  void visit (MacroMatcher &matcher);
+  void visit (MacroRulesDefinition &rules_def);
+  void visit (MacroInvocation &macro_invoc);
+  void visit (MetaItemPath &meta_item);
+  void visit (MetaItemSeq &meta_item);
+  void visit (MetaWord &meta_item);
+  void visit (MetaNameValueStr &meta_item);
+  void visit (MetaListPaths &meta_item);
+  void visit (MetaListNameValueStr &meta_item);
+
+  // rust-pattern.h
+  void visit (LiteralPattern &pattern);
+  void visit (IdentifierPattern &pattern);
+  void visit (WildcardPattern &pattern);
+  // void visit(RangePatternBound& bound);
+  void visit (RangePatternBoundLiteral &bound);
+  void visit (RangePatternBoundPath &bound);
+  void visit (RangePatternBoundQualPath &bound);
+  void visit (RangePattern &pattern);
+  void visit (ReferencePattern &pattern);
+  // void visit(StructPatternField& field);
+  void visit (StructPatternFieldTuplePat &field);
+  void visit (StructPatternFieldIdentPat &field);
+  void visit (StructPatternFieldIdent &field);
+  void visit (StructPattern &pattern);
+  // void visit(TupleStructItems& tuple_items);
+  void visit (TupleStructItemsNoRange &tuple_items);
+  void visit (TupleStructItemsRange &tuple_items);
+  void visit (TupleStructPattern &pattern);
+  // void visit(TuplePatternItems& tuple_items);
+  void visit (TuplePatternItemsMultiple &tuple_items);
+  void visit (TuplePatternItemsRanged &tuple_items);
+  void visit (TuplePattern &pattern);
+  void visit (GroupedPattern &pattern);
+  void visit (SlicePattern &pattern);
+
+  // rust-stmt.h
+  void visit (EmptyStmt &stmt);
+  void visit (LetStmt &stmt);
+  void visit (ExprStmtWithoutBlock &stmt);
+  void visit (ExprStmtWithBlock &stmt);
+
+  // rust-type.h
+  void visit (TraitBound &bound);
+  void visit (ImplTraitType &type);
+  void visit (TraitObjectType &type);
+  void visit (ParenthesisedType &type);
+  void visit (ImplTraitTypeOneBound &type);
+  void visit (TraitObjectTypeOneBound &type);
+  void visit (TupleType &type);
+  void visit (NeverType &type);
+  void visit (RawPointerType &type);
+  void visit (ReferenceType &type);
+  void visit (ArrayType &type);
+  void visit (SliceType &type);
+  void visit (InferredType &type);
+  void visit (BareFunctionType &type);
+};
+
+} // namespace AST
+} // namespace Rust
+
+#endif // !RUST_AST_DUMP_H
diff --git a/gcc/rust/ast/rust-ast-full-decls.h b/gcc/rust/ast/rust-ast-full-decls.h
new file mode 100644
index 00000000000..47f332193cc
--- /dev/null
+++ b/gcc/rust/ast/rust-ast-full-decls.h
@@ -0,0 +1,273 @@
+// 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_AST_FULL_DECLS_H
+#define RUST_AST_FULL_DECLS_H
+
+// Forward declarations for all AST classes. Useful for not having to include
+// all definitions.
+
+namespace Rust {
+namespace AST {
+// rust-ast.h
+class AttrInput;
+class TokenTree;
+class MacroMatch;
+class Token;
+struct Literal;
+class DelimTokenTree;
+class PathSegment;
+class SimplePathSegment;
+class SimplePath;
+struct Attribute;
+class MetaItemInner;
+class AttrInputMetaItemContainer;
+class MetaItem;
+class Stmt;
+class Item;
+class Expr;
+class ExprWithoutBlock;
+class IdentifierExpr;
+class Pattern;
+class Type;
+class TypeNoBounds;
+class TypeParamBound;
+class Lifetime;
+class GenericParam;
+class LifetimeParam;
+class ConstGenericParam;
+class MacroItem;
+class TraitItem;
+class InherentImplItem;
+class TraitImplItem;
+struct Crate;
+class PathExpr;
+
+// rust-path.h
+class PathIdentSegment;
+struct GenericArgsBinding;
+struct GenericArgs;
+class PathExprSegment;
+class PathPattern;
+class PathInExpression;
+class TypePathSegment;
+class TypePathSegmentGeneric;
+struct TypePathFunction;
+class TypePathSegmentFunction;
+class TypePath;
+struct QualifiedPathType;
+class QualifiedPathInExpression;
+class QualifiedPathInType;
+
+// rust-expr.h
+class ExprWithBlock;
+class LiteralExpr;
+class AttrInputLiteral;
+class MetaItemLitExpr;
+class MetaItemPathLit;
+class OperatorExpr;
+class BorrowExpr;
+class DereferenceExpr;
+class ErrorPropagationExpr;
+class NegationExpr;
+class ArithmeticOrLogicalExpr;
+class ComparisonExpr;
+class LazyBooleanExpr;
+class TypeCastExpr;
+class AssignmentExpr;
+class CompoundAssignmentExpr;
+class GroupedExpr;
+class ArrayElems;
+class ArrayElemsValues;
+class ArrayElemsCopied;
+class ArrayExpr;
+class ArrayIndexExpr;
+class TupleExpr;
+class TupleIndexExpr;
+class StructExpr;
+class StructExprStruct;
+struct StructBase;
+class StructExprField;
+class StructExprFieldIdentifier;
+class StructExprFieldWithVal;
+class StructExprFieldIdentifierValue;
+class StructExprFieldIndexValue;
+class StructExprStructFields;
+class StructExprStructBase;
+class CallExpr;
+class MethodCallExpr;
+class FieldAccessExpr;
+struct ClosureParam;
+class ClosureExpr;
+class ClosureExprInner;
+class BlockExpr;
+class ClosureExprInnerTyped;
+class ContinueExpr;
+class BreakExpr;
+class RangeExpr;
+class RangeFromToExpr;
+class RangeFromExpr;
+class RangeToExpr;
+class RangeFullExpr;
+class RangeFromToInclExpr;
+class RangeToInclExpr;
+class ReturnExpr;
+class UnsafeBlockExpr;
+class LoopLabel;
+class BaseLoopExpr;
+class LoopExpr;
+class WhileLoopExpr;
+class WhileLetLoopExpr;
+class ForLoopExpr;
+class IfExpr;
+class IfExprConseqElse;
+class IfExprConseqIf;
+class IfLetExpr;
+class IfExprConseqIfLet;
+class IfLetExprConseqElse;
+class IfLetExprConseqIf;
+class IfLetExprConseqIfLet;
+struct MatchArm;
+// class MatchCase;
+// class MatchCaseBlockExpr;
+// class MatchCaseExpr;
+struct MatchCase;
+class MatchExpr;
+class AwaitExpr;
+class AsyncBlockExpr;
+
+// rust-stmt.h
+class EmptyStmt;
+class LetStmt;
+class ExprStmt;
+class ExprStmtWithoutBlock;
+class ExprStmtWithBlock;
+
+// rust-item.h
+class TypeParam;
+class WhereClauseItem;
+class LifetimeWhereClauseItem;
+class TypeBoundWhereClauseItem;
+struct WhereClause;
+struct SelfParam;
+struct FunctionQualifiers;
+struct FunctionParam;
+struct Visibility;
+class Method;
+class VisItem;
+class Module;
+class ExternCrate;
+class UseTree;
+class UseTreeGlob;
+class UseTreeList;
+class UseTreeRebind;
+class UseDeclaration;
+class Function;
+class TypeAlias;
+class Struct;
+struct StructField;
+class StructStruct;
+struct TupleField;
+class TupleStruct;
+class EnumItem;
+class EnumItemTuple;
+class EnumItemStruct;
+class EnumItemDiscriminant;
+class Enum;
+class Union;
+class ConstantItem;
+class StaticItem;
+struct TraitFunctionDecl;
+class TraitItemFunc;
+struct TraitMethodDecl;
+class TraitItemMethod;
+class TraitItemConst;
+class TraitItemType;
+class Trait;
+class Impl;
+class InherentImpl;
+class TraitImpl;
+class ExternalItem;
+class ExternalStaticItem;
+struct NamedFunctionParam;
+class ExternalFunctionItem;
+class ExternBlock;
+
+// rust-macro.h
+class MacroMatchFragment;
+class MacroMatchRepetition;
+class MacroMatcher;
+struct MacroTranscriber;
+struct MacroRule;
+class MacroRulesDefinition;
+class MacroInvocation;
+class MetaItemPath;
+class MetaItemSeq;
+class MetaWord;
+class MetaNameValueStr;
+class MetaListPaths;
+class MetaListNameValueStr;
+
+// rust-pattern.h
+class LiteralPattern;
+class IdentifierPattern;
+class WildcardPattern;
+class RangePatternBound;
+class RangePatternBoundLiteral;
+class RangePatternBoundPath;
+class RangePatternBoundQualPath;
+class RangePattern;
+class ReferencePattern;
+struct StructPatternEtc;
+class StructPatternField;
+class StructPatternFieldTuplePat;
+class StructPatternFieldIdentPat;
+class StructPatternFieldIdent;
+struct StructPatternElements;
+class StructPattern;
+class TupleStructItems;
+class TupleStructItemsNoRange;
+class TupleStructItemsRange;
+class TupleStructPattern;
+class TuplePatternItems;
+class TuplePatternItemsMultiple;
+class TuplePatternItemsRanged;
+class TuplePattern;
+class GroupedPattern;
+class SlicePattern;
+
+// rust-type.h
+class TraitBound;
+class ImplTraitType;
+class TraitObjectType;
+class ParenthesisedType;
+class ImplTraitTypeOneBound;
+class TraitObjectTypeOneBound;
+class TupleType;
+class NeverType;
+class RawPointerType;
+class ReferenceType;
+class ArrayType;
+class SliceType;
+class InferredType;
+struct MaybeNamedParam;
+class BareFunctionType;
+} // namespace AST
+} // namespace Rust
+
+#endif
diff --git a/gcc/rust/ast/rust-ast-full-test.cc b/gcc/rust/ast/rust-ast-full-test.cc
new file mode 100644
index 00000000000..cac816d2545
--- /dev/null
+++ b/gcc/rust/ast/rust-ast-full-test.cc
@@ -0,0 +1,5814 @@
+/* General AST-related method implementations for Rust frontend.
+   Copyright (C) 2009-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/>.  */
+
+// FIXME: This does not work on Windows
+#include <string>
+#include <unistd.h>
+#include <memory>
+
+#include "rust-ast-full.h"
+#include "rust-diagnostics.h"
+#include "rust-ast-visitor.h"
+#include "rust-macro.h"
+#include "rust-session-manager.h"
+#include "rust-lex.h"
+#include "rust-parse.h"
+#include "operator.h"
+
+/* Compilation unit used for various AST-related functions that would make
+ * the headers too long if they were defined inline and don't receive any
+ * benefits from being defined inline because they are virtual. Also used
+ * for various other stuff. */
+
+namespace Rust {
+namespace AST {
+
+enum indent_mode
+{
+  enter,
+  out,
+  stay
+};
+
+std::string
+indent_spaces (enum indent_mode mode)
+{
+  static int indent = 0;
+  std::string str = "";
+  if (out == mode)
+    indent--;
+  for (int i = 0; i < indent; i++)
+    str += " ";
+  if (enter == mode)
+    indent++;
+
+  return str;
+}
+
+// Gets a string in a certain delim type.
+std::string
+get_string_in_delims (std::string str_input, DelimType delim_type)
+{
+  switch (delim_type)
+    {
+    case PARENS:
+      return "(" + str_input + ")";
+    case SQUARE:
+      return "[" + str_input + "]";
+    case CURLY:
+      return "{" + str_input + "}";
+    default:
+      return "ERROR-MARK-STRING (delims)";
+    }
+  gcc_unreachable ();
+}
+
+enum AttrMode
+{
+  OUTER,
+  INNER
+};
+
+std::string
+get_mode_dump_desc (AttrMode mode)
+{
+  switch (mode)
+    {
+    case OUTER:
+      return "outer attributes";
+    case INNER:
+      return "inner attributes";
+    default:
+      gcc_unreachable ();
+      return "";
+    }
+}
+
+// Adds lines below adding attributes
+std::string
+append_attributes (std::vector<Attribute> attrs, AttrMode mode)
+{
+  indent_spaces (enter);
+
+  std::string str
+    = "\n" + indent_spaces (stay) + get_mode_dump_desc (mode) + ": ";
+  // str += "\n" + indent_spaces (stay) + "inner attributes: ";
+  if (attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with outer or "inner attribute"
+       * syntax - just prints the body */
+      for (const auto &attr : attrs)
+	str += "\n" + indent_spaces (stay) + attr.as_string ();
+    }
+
+  indent_spaces (out);
+
+  return str;
+}
+
+// Removes the beginning and end quotes of a quoted string.
+std::string
+unquote_string (std::string input)
+{
+  rust_assert (input.front () == '"');
+  rust_assert (input.back () == '"');
+  return input.substr (1, input.length () - 2);
+}
+
+std::string
+Crate::as_string () const
+{
+  rust_debug ("beginning crate recursive as-string");
+
+  std::string str ("Crate: ");
+
+  // inner attributes
+  str += append_attributes (inner_attrs, INNER);
+
+  // items
+  str += "\n items: ";
+  if (items.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &item : items)
+	{
+	  // DEBUG: null pointer check
+	  if (item == nullptr)
+	    {
+	      rust_debug ("something really terrible has gone wrong - "
+			  "null pointer item in crate.");
+	      return "NULL_POINTER_MARK";
+	    }
+
+	  str += "\n  " + item->as_string ();
+	}
+    }
+
+  return str + "\n";
+}
+
+std::string
+Attribute::as_string () const
+{
+  std::string path_str = path.as_string ();
+  if (attr_input == nullptr)
+    return path_str;
+  else
+    return path_str + attr_input->as_string ();
+}
+
+// Copy constructor must deep copy attr_input as unique pointer
+Attribute::Attribute (Attribute const &other)
+  : path (other.path), locus (other.locus)
+{
+  // guard to protect from null pointer dereference
+  if (other.attr_input != nullptr)
+    attr_input = other.attr_input->clone_attr_input ();
+}
+
+// overload assignment operator to use custom clone method
+Attribute &
+Attribute::operator= (Attribute const &other)
+{
+  path = other.path;
+  locus = other.locus;
+  // guard to protect from null pointer dereference
+  if (other.attr_input != nullptr)
+    attr_input = other.attr_input->clone_attr_input ();
+  else
+    attr_input = nullptr;
+
+  return *this;
+}
+
+std::string
+DelimTokenTree::as_string () const
+{
+  std::string start_delim;
+  std::string end_delim;
+  switch (delim_type)
+    {
+    case PARENS:
+      start_delim = "(";
+      end_delim = ")";
+      break;
+    case SQUARE:
+      start_delim = "[";
+      end_delim = "]";
+      break;
+    case CURLY:
+      start_delim = "{";
+      end_delim = "}";
+      break;
+    default:
+      rust_debug ("Invalid delimiter type, "
+		  "Should be PARENS, SQUARE, or CURLY.");
+      return "Invalid delimiter type";
+    }
+  std::string str = start_delim;
+  if (!token_trees.empty ())
+    {
+      for (const auto &tree : token_trees)
+	{
+	  // DEBUG: null pointer check
+	  if (tree == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"token tree in delim token tree.");
+	      return "NULL_POINTER_MARK";
+	    }
+
+	  str += tree->as_string ();
+	}
+    }
+  str += end_delim;
+
+  return str;
+}
+
+std::string
+Token::as_string () const
+{
+  if (tok_ref->has_str ())
+    {
+      std::string str = tok_ref->get_str ();
+
+      std::string quote = is_string_lit () ? "\"" : "";
+      return quote + str + quote;
+    }
+  else
+    {
+      return tok_ref->get_token_description ();
+    }
+}
+
+std::string
+SimplePathSegment::as_string () const
+{
+  return segment_name;
+}
+
+std::string
+SimplePath::as_string () const
+{
+  std::string path;
+  if (has_opening_scope_resolution)
+    path = "::";
+
+  // crappy hack because doing proper for loop would be more code
+  bool first_time = true;
+  for (const auto &segment : segments)
+    {
+      if (first_time)
+	{
+	  path += segment.as_string ();
+	  first_time = false;
+	}
+      else
+	{
+	  path += "::" + segment.as_string ();
+	}
+
+      // DEBUG: remove later. Checks for path error.
+      if (segment.is_error ())
+	{
+	  rust_debug ("segment in path is error - this should've been filtered "
+		      "out. first segment "
+		      "was '%s'",
+		      segments.at (0).as_string ().c_str ());
+	}
+    }
+
+  return path;
+}
+
+std::string
+Visibility::as_string () const
+{
+  switch (vis_type)
+    {
+    case PRIV:
+      return std::string ("");
+    case PUB:
+      return std::string ("pub");
+    case PUB_CRATE:
+      return std::string ("pub(crate)");
+    case PUB_SELF:
+      return std::string ("pub(self)");
+    case PUB_SUPER:
+      return std::string ("pub(super)");
+    case PUB_IN_PATH:
+      return std::string ("pub(in ") + in_path.as_string () + std::string (")");
+    default:
+      gcc_unreachable ();
+    }
+}
+
+// Creates a string that reflects the visibility stored.
+std::string
+VisItem::as_string () const
+{
+  // FIXME: can't do formatting on string to make identation occur.
+  std::string str;
+
+  if (!outer_attrs.empty ())
+    {
+      for (const auto &attr : outer_attrs)
+	str += attr.as_string () + "\n";
+    }
+
+  if (has_visibility ())
+    str += visibility.as_string () + " ";
+
+  return str;
+}
+
+std::string
+Module::as_string () const
+{
+  std::string str = VisItem::as_string () + "mod " + module_name;
+
+  // Return early if we're dealing with an unloaded module as their body resides
+  // in a different file
+  if (kind == ModuleKind::UNLOADED)
+    return str + "\n no body (reference to external file)\n";
+
+  // inner attributes
+  str += append_attributes (inner_attrs, INNER);
+
+  // items
+  str += "\n items: ";
+
+  // This can still happen if the module is loaded but empty, i.e. `mod foo {}`
+  if (items.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &item : items)
+	{
+	  // DEBUG: null pointer check
+	  if (item == nullptr)
+	    {
+	      rust_debug ("something really terrible has gone wrong - "
+			  "null pointer item in crate.");
+	      return "NULL_POINTER_MARK";
+	    }
+
+	  str += "\n  " + item->as_string ();
+	}
+    }
+
+  return str + "\n";
+}
+
+std::string
+StaticItem::as_string () const
+{
+  std::string str = VisItem::as_string ();
+
+  str += indent_spaces (stay) + "static";
+
+  if (has_mut)
+    str += " mut";
+
+  str += " " + name;
+
+  // DEBUG: null pointer check
+  if (type == nullptr)
+    {
+      rust_debug ("something really terrible has gone wrong - null "
+		  "pointer type in static item.");
+      return "NULL_POINTER_MARK";
+    }
+  str += "\n" + indent_spaces (stay) + "Type: " + type->as_string ();
+
+  // DEBUG: null pointer check
+  if (expr == nullptr)
+    {
+      rust_debug ("something really terrible has gone wrong - null "
+		  "pointer expr in static item.");
+      return "NULL_POINTER_MARK";
+    }
+  str += "\n" + indent_spaces (stay) + "Expression: " + expr->as_string ();
+
+  return str + "\n";
+}
+
+std::string
+ExternCrate::as_string () const
+{
+  std::string str = VisItem::as_string ();
+
+  str += "extern crate " + referenced_crate;
+
+  if (has_as_clause ())
+    str += " as " + as_clause_name;
+
+  return str;
+}
+
+std::string
+TupleStruct::as_string () const
+{
+  std::string str = VisItem::as_string ();
+
+  str += "struct " + struct_name;
+
+  // generic params
+  str += "\n Generic params: ";
+  if (generic_params.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : generic_params)
+	{
+	  // DEBUG: null pointer check
+	  if (param == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"generic param in enum.");
+	      return "NULL_POINTER_MARK";
+	    }
+
+	  str += "\n  " + param->as_string ();
+	}
+    }
+
+  // tuple fields
+  str += "\n Tuple fields: ";
+  if (fields.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &field : fields)
+	str += "\n  " + field.as_string ();
+    }
+
+  str += "\n Where clause: ";
+  if (has_where_clause ())
+    str += where_clause.as_string ();
+  else
+    str += "none";
+
+  return str;
+}
+
+std::string
+ConstantItem::as_string () const
+{
+  std::string str = VisItem::as_string ();
+
+  str += "const " + identifier;
+
+  // DEBUG: null pointer check
+  if (type == nullptr)
+    {
+      rust_debug ("something really terrible has gone wrong - null "
+		  "pointer type in const item.");
+      return "NULL_POINTER_MARK";
+    }
+  str += "\n  Type: " + type->as_string ();
+
+  // DEBUG: null pointer check
+  if (const_expr == nullptr)
+    {
+      rust_debug ("something really terrible has gone wrong - null "
+		  "pointer expr in const item.");
+      return "NULL_POINTER_MARK";
+    }
+  str += "\n  Expression: " + const_expr->as_string ();
+
+  return str + "\n";
+}
+
+std::string
+InherentImpl::as_string () const
+{
+  std::string str = VisItem::as_string ();
+
+  str += "impl ";
+
+  // generic params
+  str += "\n Generic params: ";
+  if (generic_params.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : generic_params)
+	{
+	  // DEBUG: null pointer check
+	  if (param == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"generic param in inherent impl.");
+	      return "NULL_POINTER_MARK";
+	    }
+
+	  str += "\n  " + param->as_string ();
+	}
+    }
+
+  str += "\n Type: " + trait_type->as_string ();
+
+  str += "\n Where clause: ";
+  if (has_where_clause ())
+    str += where_clause.as_string ();
+  else
+    str += "none";
+
+  // inner attributes
+  str += append_attributes (inner_attrs, INNER);
+
+  // inherent impl items
+  str += "\n Inherent impl items: ";
+  if (!has_impl_items ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &item : impl_items)
+	str += "\n  " + item->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+Method::as_string () const
+{
+  std::string str ("Method: \n ");
+
+  str += vis.as_string () + " " + qualifiers.as_string ();
+
+  str += " fn " + method_name;
+
+  // generic params
+  str += "\n Generic params: ";
+  if (generic_params.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : generic_params)
+	{
+	  // DEBUG: null pointer check
+	  if (param == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"generic param in method.");
+	      return "NULL_POINTER_MARK";
+	    }
+
+	  str += "\n  " + param->as_string ();
+	}
+    }
+
+  str += "\n Self param: " + self_param.as_string ();
+
+  str += "\n Function params: ";
+  if (function_params.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : function_params)
+	str += "\n  " + param.as_string ();
+    }
+
+  str += "\n Return type: ";
+  if (has_return_type ())
+    str += return_type->as_string ();
+  else
+    str += "none (void)";
+
+  str += "\n Where clause: ";
+  if (has_where_clause ())
+    str += where_clause.as_string ();
+  else
+    str += "none";
+
+  str += "\n Block expr (body): \n  ";
+  str += function_body->as_string ();
+
+  return str;
+}
+
+std::string
+StructStruct::as_string () const
+{
+  std::string str = VisItem::as_string ();
+
+  str += "struct " + struct_name;
+
+  // generic params
+  str += "\n Generic params: ";
+  if (generic_params.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : generic_params)
+	{
+	  // DEBUG: null pointer check
+	  if (param == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"generic param in enum.");
+	      return "NULL_POINTER_MARK";
+	    }
+
+	  str += "\n  " + param->as_string ();
+	}
+    }
+
+  str += "\n Where clause: ";
+  if (has_where_clause ())
+    str += where_clause.as_string ();
+  else
+    str += "none";
+
+  // struct fields
+  str += "\n Struct fields: ";
+  if (is_unit)
+    {
+      str += "none (unit)";
+    }
+  else if (fields.empty ())
+    {
+      str += "none (non-unit)";
+    }
+  else
+    {
+      for (const auto &field : fields)
+	str += "\n  " + field.as_string ();
+    }
+
+  return str;
+}
+
+std::string
+UseDeclaration::as_string () const
+{
+  std::string str = VisItem::as_string ();
+
+  // DEBUG: null pointer check
+  if (use_tree == nullptr)
+    {
+      rust_debug (
+	"something really terrible has gone wrong - null pointer use tree in "
+	"use declaration.");
+      return "NULL_POINTER_MARK";
+    }
+
+  str += "use " + use_tree->as_string ();
+
+  return str;
+}
+
+std::string
+UseTreeGlob::as_string () const
+{
+  switch (glob_type)
+    {
+    case NO_PATH:
+      return "*";
+    case GLOBAL:
+      return "::*";
+      case PATH_PREFIXED: {
+	std::string path_str = path.as_string ();
+	return path_str + "::*";
+      }
+    default:
+      // some kind of error
+      return "ERROR-PATH";
+    }
+  gcc_unreachable ();
+}
+
+std::string
+UseTreeList::as_string () const
+{
+  std::string path_str;
+  switch (path_type)
+    {
+    case NO_PATH:
+      path_str = "{";
+      break;
+    case GLOBAL:
+      path_str = "::{";
+      break;
+      case PATH_PREFIXED: {
+	path_str = path.as_string () + "::{";
+	break;
+      }
+    default:
+      // some kind of error
+      return "ERROR-PATH-LIST";
+    }
+
+  if (has_trees ())
+    {
+      auto i = trees.begin ();
+      auto e = trees.end ();
+
+      // DEBUG: null pointer check
+      if (*i == nullptr)
+	{
+	  rust_debug ("something really terrible has gone wrong - null pointer "
+		      "tree in use tree list.");
+	  return "NULL_POINTER_MARK";
+	}
+
+      for (; i != e; i++)
+	{
+	  path_str += (*i)->as_string ();
+	  if (e != i + 1)
+	    path_str += ", ";
+	}
+    }
+  else
+    {
+      path_str += "none";
+    }
+
+  return path_str + "}";
+}
+
+std::string
+UseTreeRebind::as_string () const
+{
+  std::string path_str = path.as_string ();
+
+  switch (bind_type)
+    {
+    case NONE:
+      // nothing to add, just path
+      break;
+    case IDENTIFIER:
+      path_str += " as " + identifier;
+      break;
+    case WILDCARD:
+      path_str += " as _";
+      break;
+    default:
+      // error
+      return "ERROR-PATH-REBIND";
+    }
+
+  return path_str;
+}
+
+std::string
+Enum::as_string () const
+{
+  std::string str = VisItem::as_string ();
+  str += enum_name;
+
+  // generic params
+  str += "\n Generic params: ";
+  if (generic_params.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : generic_params)
+	{
+	  // DEBUG: null pointer check
+	  if (param == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"generic param in enum.");
+	      return "NULL_POINTER_MARK";
+	    }
+
+	  str += "\n  " + param->as_string ();
+	}
+    }
+
+  str += "\n Where clause: ";
+  if (has_where_clause ())
+    str += where_clause.as_string ();
+  else
+    str += "none";
+
+  // items
+  str += "\n Items: ";
+  if (items.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &item : items)
+	{
+	  // DEBUG: null pointer check
+	  if (item == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"enum item in enum.");
+	      return "NULL_POINTER_MARK";
+	    }
+
+	  str += "\n  " + item->as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+Trait::as_string () const
+{
+  std::string str = VisItem::as_string ();
+
+  if (has_unsafe)
+    str += "unsafe ";
+
+  str += "trait " + name;
+
+  // generic params
+  str += "\n Generic params: ";
+  if (generic_params.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : generic_params)
+	{
+	  // DEBUG: null pointer check
+	  if (param == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"generic param in trait.");
+	      return "NULL_POINTER_MARK";
+	    }
+
+	  str += "\n  " + param->as_string ();
+	}
+    }
+
+  str += "\n Type param bounds: ";
+  if (!has_type_param_bounds ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &bound : type_param_bounds)
+	{
+	  // DEBUG: null pointer check
+	  if (bound == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"type param bound in trait.");
+	      return "NULL_POINTER_MARK";
+	    }
+
+	  str += "\n  " + bound->as_string ();
+	}
+    }
+
+  str += "\n Where clause: ";
+  if (!has_where_clause ())
+    str += "none";
+  else
+    str += where_clause.as_string ();
+
+  str += "\n Trait items: ";
+  if (!has_trait_items ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &item : trait_items)
+	{
+	  // DEBUG: null pointer check
+	  if (item == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"trait item in trait.");
+	      return "NULL_POINTER_MARK";
+	    }
+
+	  str += "\n  " + item->as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+Union::as_string () const
+{
+  std::string str = VisItem::as_string ();
+
+  str += "union " + union_name;
+
+  // generic params
+  str += "\n Generic params: ";
+  if (generic_params.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : generic_params)
+	{
+	  // DEBUG: null pointer check
+	  if (param == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"generic param in union.");
+	      return "NULL_POINTER_MARK";
+	    }
+
+	  str += "\n  " + param->as_string ();
+	}
+    }
+
+  str += "\n Where clause: ";
+  if (has_where_clause ())
+    str += where_clause.as_string ();
+  else
+    str += "none";
+
+  // struct fields
+  str += "\n Struct fields (variants): ";
+  if (variants.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &field : variants)
+	str += "\n  " + field.as_string ();
+    }
+
+  return str;
+}
+
+std::string
+Function::as_string () const
+{
+  std::string str = VisItem::as_string () + "\n";
+  std::string qstr = qualifiers.as_string ();
+  if ("" != qstr)
+    str += qstr + " ";
+
+  if (has_return_type ())
+    {
+      // DEBUG: null pointer check
+      if (return_type == nullptr)
+	{
+	  rust_debug (
+	    "something really terrible has gone wrong - null pointer return "
+	    "type in function.");
+	  return "NULL_POINTER_MARK";
+	}
+
+      str += return_type->as_string () + " ";
+    }
+  else
+    {
+      str += "void ";
+    }
+
+  str += function_name;
+
+  if (has_generics ())
+    {
+      str += "<";
+
+      auto i = generic_params.begin ();
+      auto e = generic_params.end ();
+
+      // DEBUG: null pointer check
+      if (i == e)
+	{
+	  rust_debug ("something really terrible has gone wrong - null pointer "
+		      "generic param in function item.");
+	  return "NULL_POINTER_MARK";
+	}
+
+      for (; i != e; i++)
+	{
+	  str += (*i)->as_string ();
+	  if (e != i + 1)
+	    str += ", ";
+	}
+      str += ">";
+    }
+
+  if (has_function_params ())
+    {
+      auto i = function_params.begin ();
+      auto e = function_params.end ();
+      str += "(";
+      for (; i != e; i++)
+	{
+	  str += (*i).as_string ();
+	  if (e != i + 1)
+	    str += ", ";
+	}
+      str += ")";
+    }
+  else
+    {
+      str += "()";
+    }
+
+  if (has_where_clause ())
+    str += " where " + where_clause.as_string ();
+
+  str += "\n";
+
+  // DEBUG: null pointer check
+  if (function_body == nullptr)
+    {
+      rust_debug (
+	"something really terrible has gone wrong - null pointer function "
+	"body in function.");
+      return "NULL_POINTER_MARK";
+    }
+  str += function_body->as_string () + "\n";
+
+  return str;
+}
+
+std::string
+WhereClause::as_string () const
+{
+  // just print where clause items, don't mention "where" or "where clause"
+  std::string str;
+
+  if (where_clause_items.empty ())
+    {
+      str = "none";
+    }
+  else
+    {
+      for (const auto &item : where_clause_items)
+	str += "\n  " + item->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+BlockExpr::as_string () const
+{
+  std::string istr = indent_spaces (enter);
+  std::string str = istr + "BlockExpr:\n" + istr;
+
+  // get outer attributes
+  str += append_attributes (outer_attrs, OUTER);
+
+  // inner attributes
+  str += append_attributes (inner_attrs, INNER);
+
+  // statements
+  str += "\n" + indent_spaces (stay) + "statements: ";
+  if (statements.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &stmt : statements)
+	{
+	  // DEBUG: null pointer check
+	  if (stmt == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"stmt in block expr.");
+	      return "NULL_POINTER_MARK";
+	    }
+
+	  str += "\n" + indent_spaces (stay) + stmt->as_string ();
+	}
+    }
+
+  // final expression
+  str += "\n" + indent_spaces (stay) + "final expression: ";
+  if (expr == nullptr)
+    str += "none";
+  else
+    str += "\n" + expr->as_string ();
+
+  str += "\n" + indent_spaces (out);
+  return str;
+}
+
+std::string
+TraitImpl::as_string () const
+{
+  std::string str = VisItem::as_string ();
+
+  if (has_unsafe)
+    str += "unsafe ";
+
+  str += "impl ";
+
+  // generic params
+  str += "\n Generic params: ";
+  if (!has_generics ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : generic_params)
+	str += "\n  " + param->as_string ();
+    }
+
+  str += "\n Has exclam: ";
+  if (has_exclam)
+    str += "true";
+  else
+    str += "false";
+
+  str += "\n TypePath (to trait): " + trait_path.as_string ();
+
+  str += "\n Type (struct to impl on): " + trait_type->as_string ();
+
+  str += "\n Where clause: ";
+  if (!has_where_clause ())
+    str += "none";
+  else
+    str += where_clause.as_string ();
+
+  // inner attributes
+  str += append_attributes (inner_attrs, INNER);
+
+  str += "\n trait impl items: ";
+  if (!has_impl_items ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &item : impl_items)
+	str += "\n  " + item->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+TypeAlias::as_string () const
+{
+  std::string str = VisItem::as_string ();
+
+  str += " " + new_type_name;
+
+  // generic params
+  str += "\n Generic params: ";
+  if (!has_generics ())
+    {
+      str += "none";
+    }
+  else
+    {
+      auto i = generic_params.begin ();
+      auto e = generic_params.end ();
+
+      for (; i != e; i++)
+	{
+	  str += (*i)->as_string ();
+	  if (e != i + 1)
+	    str += ", ";
+	}
+    }
+
+  str += "\n Where clause: ";
+  if (!has_where_clause ())
+    str += "none";
+  else
+    str += where_clause.as_string ();
+
+  str += "\n Type: " + existing_type->as_string ();
+
+  return str;
+}
+
+std::string
+ExternBlock::as_string () const
+{
+  std::string str = VisItem::as_string ();
+
+  str += "extern ";
+  if (has_abi ())
+    str += "\"" + abi + "\" ";
+
+  str += append_attributes (inner_attrs, INNER);
+
+  str += "\n external items: ";
+  if (!has_extern_items ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &item : extern_items)
+	str += "\n  " + item->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+MacroRule::as_string () const
+{
+  std::string str ("Macro rule: ");
+
+  str += "\n Matcher: \n  ";
+  str += matcher.as_string ();
+
+  str += "\n Transcriber: \n  ";
+  str += transcriber.as_string ();
+
+  return str;
+}
+
+std::string
+MacroRulesDefinition::as_string () const
+{
+  std::string str;
+
+  // get outer attrs
+  str += append_attributes (outer_attrs, OUTER);
+
+  str += "macro_rules!";
+
+  str += rule_name;
+
+  str += "\n Macro rules: ";
+  if (rules.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &rule : rules)
+	str += "\n  " + rule.as_string ();
+    }
+
+  str += "\n Delim type: ";
+  switch (delim_type)
+    {
+    case PARENS:
+      str += "parentheses";
+      break;
+    case SQUARE:
+      str += "square";
+      break;
+    case CURLY:
+      str += "curly";
+      break;
+    default:
+      return "ERROR_MARK_STRING - delim type in macro invocation";
+    }
+
+  return str;
+}
+
+std::string
+MacroInvocation::as_string () const
+{
+  std::string str = "MacroInvocation: ";
+
+  str += append_attributes (outer_attrs, OUTER);
+
+  str += "\n " + invoc_data.as_string ();
+
+  str += "\n has semicolon: ";
+  str += has_semicolon () ? "true" : "false";
+
+  return str;
+}
+
+std::string
+MacroInvocData::as_string () const
+{
+  return path.as_string () + "!" + token_tree.as_string ();
+}
+
+std::string
+PathInExpression::as_string () const
+{
+  std::string str;
+
+  if (has_opening_scope_resolution)
+    str = "::";
+
+  return str + PathPattern::as_string ();
+}
+
+std::string
+ExprStmtWithBlock::as_string () const
+{
+  std::string str = indent_spaces (enter) + "ExprStmtWithBlock: \n";
+
+  if (expr == nullptr)
+    {
+      str += "none (this should not happen and is an error)";
+    }
+  else
+    {
+      indent_spaces (enter);
+      str += expr->as_string ();
+      indent_spaces (out);
+    }
+
+  indent_spaces (out);
+  return str;
+}
+
+std::string
+ClosureParam::as_string () const
+{
+  std::string str (pattern->as_string ());
+
+  if (has_type_given ())
+    str += " : " + type->as_string ();
+
+  return str;
+}
+
+std::string
+ClosureExpr::as_string () const
+{
+  std::string str = "ClosureExpr:";
+
+  str += append_attributes (outer_attrs, OUTER);
+
+  str += "\n Has move: ";
+  if (has_move)
+    str += "true";
+  else
+    str += "false";
+
+  str += "\n Params: ";
+  if (params.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : params)
+	str += "\n  " + param.as_string ();
+    }
+
+  return str;
+}
+
+std::string
+ClosureExprInnerTyped::as_string () const
+{
+  std::string str = ClosureExpr::as_string ();
+
+  str += "\n Return type: " + return_type->as_string ();
+
+  str += "\n Body: " + expr->as_string ();
+
+  return str;
+}
+
+std::string
+PathPattern::as_string () const
+{
+  std::string str;
+
+  for (const auto &segment : segments)
+    str += segment.as_string () + "::";
+
+  // basically a hack - remove last two characters of string (remove final ::)
+  str.erase (str.length () - 2);
+
+  return str;
+}
+
+std::string
+QualifiedPathType::as_string () const
+{
+  std::string str ("<");
+  str += type_to_invoke_on->as_string ();
+
+  if (has_as_clause ())
+    str += " as " + trait_path.as_string ();
+
+  return str + ">";
+}
+
+std::string
+QualifiedPathInExpression::as_string () const
+{
+  return path_type.as_string () + "::" + PathPattern::as_string ();
+}
+
+std::string
+BorrowExpr::as_string () const
+{
+  /* TODO: find way to incorporate outer attrs - may have to represent in
+   * different style (i.e. something more like BorrowExpr: \n outer attrs) */
+
+  std::string str ("&");
+
+  if (double_borrow)
+    str += "&";
+
+  if (is_mut)
+    str += "mut ";
+
+  str += main_or_left_expr->as_string ();
+
+  return str;
+}
+
+std::string
+ReturnExpr::as_string () const
+{
+  /* TODO: find way to incorporate outer attrs - may have to represent in
+   * different style (i.e. something more like BorrowExpr: \n outer attrs) */
+
+  std::string str ("return ");
+
+  if (has_returned_expr ())
+    str += return_expr->as_string ();
+
+  return str;
+}
+
+std::string
+GroupedExpr::as_string () const
+{
+  std::string str ("Grouped expr:");
+
+  // outer attrs
+  str += append_attributes (outer_attrs, OUTER);
+
+  // inner attributes
+  str += append_attributes (inner_attrs, INNER);
+
+  str += "\n Expr in parens: " + expr_in_parens->as_string ();
+
+  return str;
+}
+
+std::string
+RangeToExpr::as_string () const
+{
+  return ".." + to->as_string ();
+}
+
+std::string
+ContinueExpr::as_string () const
+{
+  // TODO: rewrite format to allow outer attributes
+  std::string str ("continue ");
+
+  if (has_label ())
+    str += label.as_string ();
+
+  return str;
+}
+
+std::string
+NegationExpr::as_string () const
+{
+  // TODO: rewrite formula to allow outer attributes
+  std::string str;
+
+  switch (expr_type)
+    {
+    case NegationOperator::NEGATE:
+      str = "-";
+      break;
+    case NegationOperator::NOT:
+      str = "!";
+      break;
+    default:
+      return "ERROR_MARK_STRING - negation expr";
+    }
+
+  str += main_or_left_expr->as_string ();
+
+  return str;
+}
+
+std::string
+RangeFromExpr::as_string () const
+{
+  return from->as_string () + "..";
+}
+
+std::string
+RangeFullExpr::as_string () const
+{
+  return "..";
+}
+
+std::string
+ArrayIndexExpr::as_string () const
+{
+  // TODO: rewrite formula to allow outer attributes
+  return array_expr->as_string () + "[" + index_expr->as_string () + "]";
+}
+
+std::string
+AssignmentExpr::as_string () const
+{
+  std::string str ("AssignmentExpr: ");
+
+  if (main_or_left_expr == nullptr || right_expr == nullptr)
+    {
+      str += "error (either or both expressions are null)";
+    }
+  else
+    {
+      // left expr
+      str += "\n left: " + main_or_left_expr->as_string ();
+
+      // right expr
+      str += "\n right: " + right_expr->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+AsyncBlockExpr::as_string () const
+{
+  std::string str = "AsyncBlockExpr: ";
+
+  // get outer attributes
+  // str += "\n " + Expr::as_string ();
+  str += append_attributes (outer_attrs, OUTER);
+
+  str += "\n Has move: ";
+  str += has_move ? "true" : "false";
+
+  return str + "\n" + block_expr->as_string ();
+}
+
+std::string
+ComparisonExpr::as_string () const
+{
+  // TODO: rewrite to better reflect non-literal expressions
+  std::string str (main_or_left_expr->as_string ());
+
+  switch (expr_type)
+    {
+    case ComparisonOperator::EQUAL:
+      str += " == ";
+      break;
+    case ComparisonOperator::NOT_EQUAL:
+      str += " != ";
+      break;
+    case ComparisonOperator::GREATER_THAN:
+      str += " > ";
+      break;
+    case ComparisonOperator::LESS_THAN:
+      str += " < ";
+      break;
+    case ComparisonOperator::GREATER_OR_EQUAL:
+      str += " >= ";
+      break;
+    case ComparisonOperator::LESS_OR_EQUAL:
+      str += " <= ";
+      break;
+    default:
+      return "ERROR_MARK_STRING - comparison expr";
+    }
+
+  str += right_expr->as_string ();
+
+  return str;
+}
+
+std::string
+MethodCallExpr::as_string () const
+{
+  std::string str = "MethodCallExpr: ";
+
+  str += append_attributes (outer_attrs, OUTER);
+
+  str += "\n Object (receiver) expr: \n";
+  str += receiver->as_string ();
+
+  str += "\n Method path segment: \n";
+  str += method_name.as_string ();
+
+  str += "\n Call params:";
+  if (params.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : params)
+	{
+	  if (param == nullptr)
+	    return "ERROR_MARK_STRING - method call expr param is null";
+
+	  str += "\n  " + param->as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+TupleIndexExpr::as_string () const
+{
+  // TODO: rewrite dump to better reflect non-literal exprs
+  return tuple_expr->as_string () + "." + std::to_string (tuple_index);
+}
+
+std::string
+DereferenceExpr::as_string () const
+{
+  // TODO: rewrite dump to better reflect non-literal exprs
+  return "*" + main_or_left_expr->as_string ();
+}
+
+std::string
+FieldAccessExpr::as_string () const
+{
+  // TODO: rewrite dump to better reflect non-literal exprs
+  return receiver->as_string () + "." + field;
+}
+
+std::string
+LazyBooleanExpr::as_string () const
+{
+  // TODO: rewrite dump to better reflect non-literal exprs
+  std::string str (main_or_left_expr->as_string ());
+
+  switch (expr_type)
+    {
+    case LazyBooleanOperator::LOGICAL_OR:
+      str += " || ";
+      break;
+    case LazyBooleanOperator::LOGICAL_AND:
+      str += " && ";
+      break;
+    default:
+      return "ERROR_MARK_STRING - lazy boolean expr out of bounds";
+    }
+
+  str += right_expr->as_string ();
+
+  return str;
+}
+
+std::string
+RangeFromToExpr::as_string () const
+{
+  // TODO: rewrite dump to better reflect non-literal exprs
+  return from->as_string () + ".." + to->as_string ();
+}
+
+std::string
+RangeToInclExpr::as_string () const
+{
+  // TODO: rewrite dump to better reflect non-literal exprs
+  return "..=" + to->as_string ();
+}
+
+std::string
+UnsafeBlockExpr::as_string () const
+{
+  std::string str = "UnsafeBlockExpr:" + indent_spaces (enter);
+
+  // get outer attributes
+  str += append_attributes (outer_attrs, OUTER);
+
+  str += indent_spaces (stay) + expr->as_string () + "\n" + indent_spaces (out);
+
+  return str;
+}
+
+std::string
+ClosureExprInner::as_string () const
+{
+  std::string str = ClosureExpr::as_string ();
+
+  str += "\n Expression: " + closure_inner->as_string ();
+
+  return str;
+}
+
+std::string
+IfExpr::as_string () const
+{
+  std::string str = "IfExpr: ";
+
+  str += append_attributes (outer_attrs, OUTER);
+
+  str += "\n Condition expr: " + condition->as_string ();
+
+  str += "\n If block expr: " + if_block->as_string ();
+
+  return str;
+}
+
+std::string
+IfExprConseqElse::as_string () const
+{
+  std::string str = IfExpr::as_string ();
+
+  str += "\n Else block expr: " + else_block->as_string ();
+
+  return str;
+}
+
+std::string
+IfExprConseqIf::as_string () const
+{
+  std::string str = IfExpr::as_string ();
+
+  str += "\n Else if expr: \n  " + conseq_if_expr->as_string ();
+
+  return str;
+}
+
+std::string
+IfExprConseqIfLet::as_string () const
+{
+  std::string str = IfExpr::as_string ();
+
+  str += "\n Else if let expr: \n  " + if_let_expr->as_string ();
+
+  return str;
+}
+
+std::string
+IfLetExpr::as_string () const
+{
+  std::string str = "IfLetExpr: ";
+
+  str += append_attributes (outer_attrs, OUTER);
+
+  str += "\n Condition match arm patterns: ";
+  if (match_arm_patterns.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &pattern : match_arm_patterns)
+	str += "\n  " + pattern->as_string ();
+    }
+
+  str += "\n Scrutinee expr: " + value->as_string ();
+
+  str += "\n If let block expr: " + if_block->as_string ();
+
+  return str;
+}
+
+std::string
+IfLetExprConseqElse::as_string () const
+{
+  std::string str = IfLetExpr::as_string ();
+
+  str += "\n Else block expr: " + else_block->as_string ();
+
+  return str;
+}
+
+std::string
+IfLetExprConseqIf::as_string () const
+{
+  std::string str = IfLetExpr::as_string ();
+
+  str += "\n Else if expr: \n  " + if_expr->as_string ();
+
+  return str;
+}
+
+std::string
+IfLetExprConseqIfLet::as_string () const
+{
+  std::string str = IfLetExpr::as_string ();
+
+  str += "\n Else if let expr: \n  " + if_let_expr->as_string ();
+
+  return str;
+}
+
+std::string
+RangeFromToInclExpr::as_string () const
+{
+  // TODO: rewrite to allow dumps with non-literal exprs
+  return from->as_string () + "..=" + to->as_string ();
+}
+
+std::string
+ErrorPropagationExpr::as_string () const
+{
+  // TODO: rewrite to allow dumps with non-literal exprs
+  return main_or_left_expr->as_string () + "?";
+}
+
+std::string
+CompoundAssignmentExpr::as_string () const
+{
+  std::string operator_str;
+  operator_str.reserve (1);
+
+  // get operator string
+  switch (expr_type)
+    {
+    case CompoundAssignmentOperator::ADD:
+      operator_str = "+";
+      break;
+    case CompoundAssignmentOperator::SUBTRACT:
+      operator_str = "-";
+      break;
+    case CompoundAssignmentOperator::MULTIPLY:
+      operator_str = "*";
+      break;
+    case CompoundAssignmentOperator::DIVIDE:
+      operator_str = "/";
+      break;
+    case CompoundAssignmentOperator::MODULUS:
+      operator_str = "%";
+      break;
+    case CompoundAssignmentOperator::BITWISE_AND:
+      operator_str = "&";
+      break;
+    case CompoundAssignmentOperator::BITWISE_OR:
+      operator_str = "|";
+      break;
+    case CompoundAssignmentOperator::BITWISE_XOR:
+      operator_str = "^";
+      break;
+    case CompoundAssignmentOperator::LEFT_SHIFT:
+      operator_str = "<<";
+      break;
+    case CompoundAssignmentOperator::RIGHT_SHIFT:
+      operator_str = ">>";
+      break;
+    default:
+      operator_str = "invalid operator. wtf";
+      break;
+    }
+
+  operator_str += "=";
+
+  std::string str ("CompoundAssignmentExpr: ");
+  if (main_or_left_expr == nullptr || right_expr == nullptr)
+    {
+      str += "error. this is probably a parsing failure.";
+    }
+  else
+    {
+      str += "\n left: " + main_or_left_expr->as_string ();
+      str += "\n right: " + right_expr->as_string ();
+      str += "\n operator: " + operator_str;
+    }
+
+  return str;
+}
+
+std::string
+ArithmeticOrLogicalExpr::as_string () const
+{
+  std::string operator_str;
+  operator_str.reserve (1);
+
+  // get operator string
+  switch (expr_type)
+    {
+    case ArithmeticOrLogicalOperator::ADD:
+      operator_str = "+";
+      break;
+    case ArithmeticOrLogicalOperator::SUBTRACT:
+      operator_str = "-";
+      break;
+    case ArithmeticOrLogicalOperator::MULTIPLY:
+      operator_str = "*";
+      break;
+    case ArithmeticOrLogicalOperator::DIVIDE:
+      operator_str = "/";
+      break;
+    case ArithmeticOrLogicalOperator::MODULUS:
+      operator_str = "%";
+      break;
+    case ArithmeticOrLogicalOperator::BITWISE_AND:
+      operator_str = "&";
+      break;
+    case ArithmeticOrLogicalOperator::BITWISE_OR:
+      operator_str = "|";
+      break;
+    case ArithmeticOrLogicalOperator::BITWISE_XOR:
+      operator_str = "^";
+      break;
+    case ArithmeticOrLogicalOperator::LEFT_SHIFT:
+      operator_str = "<<";
+      break;
+    case ArithmeticOrLogicalOperator::RIGHT_SHIFT:
+      operator_str = ">>";
+      break;
+    default:
+      operator_str = "invalid operator. wtf";
+      break;
+    }
+
+  std::string str ("ArithmeticOrLogicalExpr: ");
+  if (main_or_left_expr == nullptr || right_expr == nullptr)
+    {
+      str += "error. this is probably a parsing failure.";
+    }
+  else
+    {
+      str += main_or_left_expr->as_string () + " ";
+      str += operator_str + " ";
+      str += right_expr->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+CallExpr::as_string () const
+{
+  std::string str = "CallExpr: ";
+
+  str += append_attributes (outer_attrs, OUTER);
+
+  str += "\n Function expr: ";
+  str += function->as_string ();
+
+  str += "\n Call params:";
+  if (!has_params ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : params)
+	{
+	  if (param == nullptr)
+	    return "ERROR_MARK_STRING - call expr param is null";
+
+	  str += "\n  " + param->as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+WhileLoopExpr::as_string () const
+{
+  std::string str = "WhileLoopExpr: ";
+
+  str += append_attributes (outer_attrs, OUTER);
+
+  str += "\n Label: ";
+  if (!has_loop_label ())
+    str += "none";
+  else
+    str += loop_label.as_string ();
+
+  str += "\n Conditional expr: " + condition->as_string ();
+
+  str += "\n Loop block: " + loop_block->as_string ();
+
+  return str;
+}
+
+std::string
+WhileLetLoopExpr::as_string () const
+{
+  std::string str = "WhileLetLoopExpr: ";
+
+  str += append_attributes (outer_attrs, OUTER);
+
+  str += "\n Label: ";
+  if (!has_loop_label ())
+    str += "none";
+  else
+    str += loop_label.as_string ();
+
+  str += "\n Match arm patterns: ";
+  if (match_arm_patterns.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &pattern : match_arm_patterns)
+	str += "\n  " + pattern->as_string ();
+    }
+
+  str += "\n Scrutinee expr: " + scrutinee->as_string ();
+
+  str += "\n Loop block: " + loop_block->as_string ();
+
+  return str;
+}
+
+std::string
+LoopExpr::as_string () const
+{
+  std::string str = "LoopExpr: (infinite loop)";
+
+  str += append_attributes (outer_attrs, OUTER);
+
+  str += "\n Label: ";
+  if (!has_loop_label ())
+    str += "none";
+  else
+    str += loop_label.as_string ();
+
+  str += "\n Loop block: " + loop_block->as_string ();
+
+  return str;
+}
+
+std::string
+ArrayExpr::as_string () const
+{
+  std::string str = "ArrayExpr:";
+
+  str += append_attributes (outer_attrs, OUTER);
+
+  // inner attributes
+  str += append_attributes (inner_attrs, INNER);
+
+  str += "\n Array elems: ";
+  str += internal_elements->as_string ();
+
+  return str;
+}
+
+std::string
+AwaitExpr::as_string () const
+{
+  // TODO: rewrite dump to allow non-literal exprs
+  return awaited_expr->as_string () + ".await";
+}
+
+std::string
+BreakExpr::as_string () const
+{
+  // TODO: rewrite dump to allow outer attrs, non-literal exprs
+  std::string str ("break ");
+
+  if (has_label ())
+    str += label.as_string () + " ";
+
+  if (has_break_expr ())
+    str += break_expr->as_string ();
+
+  return str;
+}
+
+std::string
+LoopLabel::as_string () const
+{
+  return label.as_string () + ": (label) ";
+}
+
+std::string
+MatchArm::as_string () const
+{
+  // outer attributes
+  std::string str = append_attributes (outer_attrs, OUTER);
+
+  str += "\nPatterns: ";
+  if (match_arm_patterns.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &pattern : match_arm_patterns)
+	str += "\n " + pattern->as_string ();
+    }
+
+  str += "\nGuard expr: ";
+  if (!has_match_arm_guard ())
+    str += "none";
+  else
+    str += guard_expr->as_string ();
+
+  return str;
+}
+
+std::string
+MatchCase::as_string () const
+{
+  std::string str ("MatchCase: (match arm) ");
+
+  str += "\n Match arm matcher: \n" + arm.as_string ();
+  str += "\n Expr: " + expr->as_string ();
+
+  return str;
+}
+
+std::string
+MatchExpr::as_string () const
+{
+  std::string str ("MatchExpr:");
+
+  str += append_attributes (outer_attrs, OUTER);
+
+  str += "\n Scrutinee expr: " + branch_value->as_string ();
+
+  // inner attributes
+  str += append_attributes (inner_attrs, INNER);
+
+  // match arms
+  str += "\n Match arms: ";
+  if (match_arms.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &arm : match_arms)
+	str += "\n  " + arm.as_string ();
+    }
+
+  return str;
+}
+
+std::string
+TupleExpr::as_string () const
+{
+  std::string str ("TupleExpr:");
+
+  str += append_attributes (outer_attrs, OUTER);
+
+  // inner attributes
+  str += append_attributes (inner_attrs, INNER);
+
+  str += "\n Tuple elements: ";
+  if (tuple_elems.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &elem : tuple_elems)
+	str += "\n  " + elem->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+ExprStmtWithoutBlock::as_string () const
+{
+  std::string str ("ExprStmtWithoutBlock:\n");
+  indent_spaces (enter);
+  str += indent_spaces (stay);
+
+  if (expr == nullptr)
+    str += "none (this shouldn't happen and is probably an error)";
+  else
+    str += expr->as_string ();
+  indent_spaces (out);
+
+  return str;
+}
+
+std::string
+FunctionParam::as_string () const
+{
+  // TODO: rewrite dump to allow non-literal types
+  return param_name->as_string () + " : " + type->as_string ();
+}
+
+std::string
+FunctionQualifiers::as_string () const
+{
+  std::string str;
+
+  switch (const_status)
+    {
+    case NONE:
+      // do nothing
+      break;
+    case CONST_FN:
+      str += "const ";
+      break;
+    case ASYNC_FN:
+      str += "async ";
+      break;
+    default:
+      return "ERROR_MARK_STRING: async-const status failure";
+    }
+
+  if (has_unsafe)
+    str += "unsafe ";
+
+  if (has_extern)
+    {
+      str += "extern";
+      if (extern_abi != "")
+	str += " \"" + extern_abi + "\"";
+    }
+
+  return str;
+}
+
+std::string
+TraitBound::as_string () const
+{
+  std::string str ("TraitBound:");
+
+  str += "\n Has opening question mark: ";
+  if (opening_question_mark)
+    str += "true";
+  else
+    str += "false";
+
+  str += "\n For lifetimes: ";
+  if (!has_for_lifetimes ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &lifetime : for_lifetimes)
+	str += "\n  " + lifetime.as_string ();
+    }
+
+  str += "\n Type path: " + type_path.as_string ();
+
+  return str;
+}
+
+std::string
+MacroMatcher::as_string () const
+{
+  std::string str ("Macro matcher: ");
+
+  str += "\n Delim type: ";
+
+  switch (delim_type)
+    {
+    case PARENS:
+      str += "parentheses";
+      break;
+    case SQUARE:
+      str += "square";
+      break;
+    case CURLY:
+      str += "curly";
+      break;
+    default:
+      return "ERROR_MARK_STRING - macro matcher delim";
+    }
+
+  str += "\n Matches: ";
+
+  if (matches.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &match : matches)
+	str += "\n  " + match->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+LifetimeParam::as_string () const
+{
+  std::string str ("LifetimeParam: ");
+
+  str += "\n Outer attribute: ";
+  if (!has_outer_attribute ())
+    str += "none";
+  else
+    str += outer_attr.as_string ();
+
+  str += "\n Lifetime: " + lifetime.as_string ();
+
+  str += "\n Lifetime bounds: ";
+  if (!has_lifetime_bounds ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &bound : lifetime_bounds)
+	str += "\n  " + bound.as_string ();
+    }
+
+  return str;
+}
+
+std::string
+ConstGenericParam::as_string () const
+{
+  std::string str ("ConstGenericParam: ");
+  str += "const " + name + ": " + type->as_string ();
+
+  if (has_default_value ())
+    str += " = " + get_default_value ().as_string ();
+
+  return str;
+}
+
+std::string
+MacroMatchFragment::as_string () const
+{
+  return "$" + ident + ": " + frag_spec.as_string ();
+}
+
+std::string
+QualifiedPathInType::as_string () const
+{
+  /* TODO: this may need adjusting if segments (e.g. with functions) can't be
+   * literalised */
+  std::string str = path_type.as_string ();
+
+  for (const auto &segment : segments)
+    str += "::" + segment->as_string ();
+
+  return str;
+}
+
+std::string
+MacroMatchRepetition::as_string () const
+{
+  std::string str ("Macro match repetition: ");
+
+  str += "\n Matches: ";
+  if (matches.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &match : matches)
+	str += "\n  " + match->as_string ();
+    }
+
+  str += "\n Sep: ";
+  if (!has_sep ())
+    str += "none";
+  else
+    str += sep->as_string ();
+
+  str += "\n Op: ";
+  switch (op)
+    {
+    case ANY:
+      str += "*";
+      break;
+    case ONE_OR_MORE:
+      str += "+";
+      break;
+    case ZERO_OR_ONE:
+      str += "?";
+      break;
+    case NONE:
+      str += "no op? shouldn't be allowed";
+      break;
+    default:
+      return "ERROR_MARK_STRING - unknown op in macro match repetition";
+    }
+
+  return str;
+}
+
+std::string
+Lifetime::as_string () const
+{
+  if (is_error ())
+    return "error lifetime";
+
+  switch (lifetime_type)
+    {
+    case NAMED:
+      return "'" + lifetime_name;
+    case STATIC:
+      return "'static";
+    case WILDCARD:
+      return "'_";
+    default:
+      return "ERROR-MARK-STRING: lifetime type failure";
+    }
+}
+
+std::string
+TypePath::as_string () const
+{
+  /* TODO: this may need to be rewritten if a segment (e.g. function) can't be
+   * literalised */
+  std::string str;
+
+  if (has_opening_scope_resolution)
+    str = "::";
+
+  for (const auto &segment : segments)
+    str += segment->as_string () + "::";
+
+  // kinda hack - remove last 2 '::' characters
+  str.erase (str.length () - 2);
+
+  return str;
+}
+
+std::string
+TypeParam::as_string () const
+{
+  std::string str ("TypeParam: ");
+
+  str += "\n Outer attribute: ";
+  if (!has_outer_attribute ())
+    str += "none";
+  else
+    str += outer_attr.as_string ();
+
+  str += "\n Identifier: " + type_representation;
+
+  str += "\n Type param bounds: ";
+  if (!has_type_param_bounds ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &bound : type_param_bounds)
+	str += "\n  " + bound->as_string ();
+    }
+
+  str += "\n Type: ";
+  if (!has_type ())
+    str += "none";
+  else
+    str += type->as_string ();
+
+  return str;
+}
+
+SimplePath
+PathPattern::convert_to_simple_path (bool with_opening_scope_resolution) const
+{
+  if (!has_segments ())
+    return SimplePath::create_empty ();
+
+  // create vector of reserved size (to minimise reallocations)
+  std::vector<SimplePathSegment> simple_segments;
+  simple_segments.reserve (segments.size ());
+
+  for (const auto &segment : segments)
+    {
+      // return empty path if doesn't meet simple path segment requirements
+      if (segment.is_error () || segment.has_generic_args ()
+	  || segment.as_string () == "Self")
+	return SimplePath::create_empty ();
+
+      // create segment and add to vector
+      std::string segment_str = segment.as_string ();
+      simple_segments.push_back (
+	SimplePathSegment (std::move (segment_str), segment.get_locus ()));
+    }
+
+  // kind of a HACK to get locus depending on opening scope resolution
+  Location locus = Linemap::unknown_location ();
+  if (with_opening_scope_resolution)
+    locus = simple_segments[0].get_locus () - 2; // minus 2 chars for ::
+  else
+    locus = simple_segments[0].get_locus ();
+  // FIXME: this hack probably doesn't actually work
+
+  return SimplePath (std::move (simple_segments), with_opening_scope_resolution,
+		     locus);
+}
+
+SimplePath
+TypePath::as_simple_path () const
+{
+  if (segments.empty ())
+    return SimplePath::create_empty ();
+
+  // create vector of reserved size (to minimise reallocations)
+  std::vector<SimplePathSegment> simple_segments;
+  simple_segments.reserve (segments.size ());
+
+  for (const auto &segment : segments)
+    {
+      // return empty path if doesn't meet simple path segment requirements
+      if (segment == nullptr || segment->is_error ()
+	  || !segment->is_ident_only () || segment->as_string () == "Self")
+	return SimplePath::create_empty ();
+
+      // create segment and add to vector
+      std::string segment_str = segment->as_string ();
+      simple_segments.push_back (
+	SimplePathSegment (std::move (segment_str), segment->get_locus ()));
+    }
+
+  return SimplePath (std::move (simple_segments), has_opening_scope_resolution,
+		     locus);
+}
+
+std::string
+PathExprSegment::as_string () const
+{
+  // TODO: rewrite dump to work with non-literalisable types
+  std::string ident_str = segment_name.as_string ();
+  if (has_generic_args ())
+    ident_str += "::<" + generic_args.as_string () + ">";
+
+  return ident_str;
+}
+
+std::string
+GenericArgs::as_string () const
+{
+  std::string args;
+
+  // lifetime args
+  if (!lifetime_args.empty ())
+    {
+      auto i = lifetime_args.begin ();
+      auto e = lifetime_args.end ();
+
+      for (; i != e; i++)
+	{
+	  args += (*i).as_string ();
+	  if (e != i + 1)
+	    args += ", ";
+	}
+    }
+
+  // type args
+  if (!generic_args.empty ())
+    {
+      auto i = generic_args.begin ();
+      auto e = generic_args.end ();
+
+      for (; i != e; i++)
+	{
+	  args += (*i).as_string ();
+	  if (e != i + 1)
+	    args += ", ";
+	}
+    }
+
+  // binding args
+  if (!binding_args.empty ())
+    {
+      auto i = binding_args.begin ();
+      auto e = binding_args.end ();
+
+      for (; i != e; i++)
+	{
+	  args += (*i).as_string ();
+	  if (e != i + 1)
+	    args += ", ";
+	}
+    }
+
+  return args;
+}
+
+std::string
+GenericArgsBinding::as_string () const
+{
+  // TODO: rewrite to work with non-literalisable types
+  return identifier + " = " + type->as_string ();
+}
+
+std::string
+ForLoopExpr::as_string () const
+{
+  std::string str = "ForLoopExpr: ";
+
+  str += append_attributes (outer_attrs, OUTER);
+
+  str += "\n Label: ";
+  if (!has_loop_label ())
+    str += "none";
+  else
+    str += loop_label.as_string ();
+
+  str += "\n Pattern: " + pattern->as_string ();
+
+  str += "\n Iterator expr: " + iterator_expr->as_string ();
+
+  str += "\n Loop block: " + loop_block->as_string ();
+
+  return str;
+}
+
+std::string
+RangePattern::as_string () const
+{
+  // TODO: maybe rewrite to work with non-linearisable bounds
+  if (has_ellipsis_syntax)
+    return lower->as_string () + "..." + upper->as_string ();
+  else
+    return lower->as_string () + "..=" + upper->as_string ();
+}
+
+std::string
+RangePatternBoundLiteral::as_string () const
+{
+  std::string str;
+
+  if (has_minus)
+    str += "-";
+
+  str += literal.as_string ();
+
+  return str;
+}
+
+std::string
+SlicePattern::as_string () const
+{
+  std::string str ("SlicePattern: ");
+
+  for (const auto &pattern : items)
+    str += "\n " + pattern->as_string ();
+
+  return str;
+}
+
+std::string
+TuplePatternItemsMultiple::as_string () const
+{
+  std::string str;
+
+  for (const auto &pattern : patterns)
+    str += "\n " + pattern->as_string ();
+
+  return str;
+}
+
+std::string
+TuplePatternItemsRanged::as_string () const
+{
+  std::string str;
+
+  str += "\n Lower patterns: ";
+  if (lower_patterns.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &lower : lower_patterns)
+	str += "\n  " + lower->as_string ();
+    }
+
+  str += "\n Upper patterns: ";
+  if (upper_patterns.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &upper : upper_patterns)
+	str += "\n  " + upper->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+TuplePattern::as_string () const
+{
+  return "TuplePattern: " + items->as_string ();
+}
+
+std::string
+StructPatternField::as_string () const
+{
+  // outer attributes
+  std::string str = append_attributes (outer_attrs, OUTER);
+
+  return str;
+}
+
+std::string
+StructPatternFieldIdent::as_string () const
+{
+  std::string str = StructPatternField::as_string ();
+
+  str += "\n";
+
+  if (has_ref)
+    str += "ref ";
+
+  if (has_mut)
+    str += "mut ";
+
+  str += ident;
+
+  return str;
+}
+
+std::string
+StructPatternFieldTuplePat::as_string () const
+{
+  // TODO: maybe rewrite to work with non-linearisable patterns
+  std::string str = StructPatternField::as_string ();
+
+  str += "\n";
+
+  str += std::to_string (index) + " : " + tuple_pattern->as_string ();
+
+  return str;
+}
+
+std::string
+StructPatternFieldIdentPat::as_string () const
+{
+  // TODO: maybe rewrite to work with non-linearisable patterns
+  std::string str = StructPatternField::as_string ();
+
+  str += "\n";
+
+  str += ident + " : " + ident_pattern->as_string ();
+
+  return str;
+}
+
+std::string
+StructPatternElements::as_string () const
+{
+  std::string str ("\n  Fields: ");
+
+  if (!has_struct_pattern_fields ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &field : fields)
+	str += "\n   " + field->as_string ();
+    }
+
+  str += "\n  Etc: ";
+  if (has_struct_pattern_etc)
+    str += "true";
+  else
+    str += "false";
+
+  return str;
+}
+
+std::string
+StructPattern::as_string () const
+{
+  std::string str ("StructPattern: \n Path: ");
+
+  str += path.as_string ();
+
+  str += "\n Struct pattern elems: ";
+  if (!has_struct_pattern_elems ())
+    str += "none";
+  else
+    str += elems.as_string ();
+
+  return str;
+}
+
+std::string
+LiteralPattern::as_string () const
+{
+  return lit.as_string ();
+}
+
+std::string
+ReferencePattern::as_string () const
+{
+  // TODO: maybe rewrite to work with non-linearisable patterns
+  std::string str ("&");
+
+  if (has_two_amps)
+    str += "&";
+
+  if (is_mut)
+    str += "mut ";
+
+  str += pattern->as_string ();
+
+  return str;
+}
+
+std::string
+IdentifierPattern::as_string () const
+{
+  // TODO: maybe rewrite to work with non-linearisable patterns
+  std::string str;
+
+  if (is_ref)
+    str += "ref ";
+
+  if (is_mut)
+    str += "mut ";
+
+  str += variable_ident;
+
+  if (has_pattern_to_bind ())
+    str += " @ " + to_bind->as_string ();
+
+  return str;
+}
+
+std::string
+TupleStructItemsNoRange::as_string () const
+{
+  std::string str;
+
+  for (const auto &pattern : patterns)
+    str += "\n  " + pattern->as_string ();
+
+  return str;
+}
+
+std::string
+TupleStructItemsRange::as_string () const
+{
+  std::string str ("\n  Lower patterns: ");
+
+  if (lower_patterns.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &lower : lower_patterns)
+	str += "\n   " + lower->as_string ();
+    }
+
+  str += "\n  Upper patterns: ";
+  if (upper_patterns.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &upper : upper_patterns)
+	str += "\n   " + upper->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+TupleStructPattern::as_string () const
+{
+  std::string str ("TupleStructPattern: \n Path: ");
+
+  str += path.as_string ();
+
+  str += "\n Tuple struct items: " + items->as_string ();
+
+  return str;
+}
+
+std::string
+LetStmt::as_string () const
+{
+  // TODO: rewrite to work with non-linearisable types and exprs
+  std::string str = append_attributes (outer_attrs, OUTER);
+
+  str += "\n" + indent_spaces (stay) + "let " + variables_pattern->as_string ();
+
+  if (has_type ())
+    str += " : " + type->as_string ();
+
+  if (has_init_expr ())
+    str += " = " + init_expr->as_string ();
+
+  return str;
+}
+
+// hopefully definition here will prevent circular dependency issue
+TraitBound *
+TypePath::to_trait_bound (bool in_parens) const
+{
+  return new TraitBound (TypePath (*this), get_locus (), in_parens);
+}
+
+std::string
+InferredType::as_string () const
+{
+  return "_ (inferred)";
+}
+
+std::string
+TypeCastExpr::as_string () const
+{
+  // TODO: rewrite to work with non-linearisable exprs and types
+  return main_or_left_expr->as_string () + " as "
+	 + type_to_convert_to->as_string ();
+}
+
+std::string
+ImplTraitType::as_string () const
+{
+  std::string str ("ImplTraitType: \n TypeParamBounds: ");
+
+  if (type_param_bounds.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &bound : type_param_bounds)
+	str += "\n  " + bound->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+ReferenceType::as_string () const
+{
+  // TODO: rewrite to work with non-linearisable types
+  std::string str ("&");
+
+  if (has_lifetime ())
+    str += lifetime.as_string () + " ";
+
+  if (has_mut)
+    str += "mut ";
+
+  str += type->as_string ();
+
+  return str;
+}
+
+std::string
+RawPointerType::as_string () const
+{
+  // TODO: rewrite to work with non-linearisable types
+  std::string str ("*");
+
+  switch (pointer_type)
+    {
+    case MUT:
+      str += "mut ";
+      break;
+    case CONST:
+      str += "const ";
+      break;
+    default:
+      return "ERROR_MARK_STRING - unknown pointer type in raw pointer type";
+    }
+
+  str += type->as_string ();
+
+  return str;
+}
+
+std::string
+TraitObjectType::as_string () const
+{
+  std::string str ("TraitObjectType: \n Has dyn dispatch: ");
+
+  if (has_dyn)
+    str += "true";
+  else
+    str += "false";
+
+  str += "\n TypeParamBounds: ";
+  if (type_param_bounds.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &bound : type_param_bounds)
+	str += "\n  " + bound->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+BareFunctionType::as_string () const
+{
+  std::string str ("BareFunctionType: \n For lifetimes: ");
+
+  if (!has_for_lifetimes ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &for_lifetime : for_lifetimes)
+	str += "\n  " + for_lifetime.as_string ();
+    }
+
+  str += "\n Qualifiers: " + function_qualifiers.as_string ();
+
+  str += "\n Params: ";
+  if (params.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : params)
+	str += "\n  " + param.as_string ();
+    }
+
+  str += "\n Is variadic: ";
+  if (is_variadic)
+    str += "true";
+  else
+    str += "false";
+
+  str += "\n Return type: ";
+  if (!has_return_type ())
+    str += "none (void)";
+  else
+    str += return_type->as_string ();
+
+  return str;
+}
+
+std::string
+ImplTraitTypeOneBound::as_string () const
+{
+  std::string str ("ImplTraitTypeOneBound: \n TraitBound: ");
+
+  return str + trait_bound.as_string ();
+}
+
+std::string
+TypePathSegmentGeneric::as_string () const
+{
+  // TODO: rewrite to work with non-linearisable types
+  return TypePathSegment::as_string () + "<" + generic_args.as_string () + ">";
+}
+
+std::string
+TraitObjectTypeOneBound::as_string () const
+{
+  std::string str ("TraitObjectTypeOneBound: \n Has dyn dispatch: ");
+
+  if (has_dyn)
+    str += "true";
+  else
+    str += "false";
+
+  str += "\n TraitBound: " + trait_bound.as_string ();
+
+  return str;
+}
+
+std::string
+TypePathFunction::as_string () const
+{
+  // TODO: rewrite to work with non-linearisable types
+  std::string str ("(");
+
+  if (has_inputs ())
+    {
+      auto i = inputs.begin ();
+      auto e = inputs.end ();
+
+      for (; i != e; i++)
+	{
+	  str += (*i)->as_string ();
+	  if (e != i + 1)
+	    str += ", ";
+	}
+    }
+
+  str += ")";
+
+  if (has_return_type ())
+    str += " -> " + return_type->as_string ();
+
+  return str;
+}
+
+std::string
+TypePathSegmentFunction::as_string () const
+{
+  // TODO: rewrite to work with non-linearisable types
+  return TypePathSegment::as_string () + function_path.as_string ();
+}
+
+std::string
+ArrayType::as_string () const
+{
+  // TODO: rewrite to work with non-linearisable types and exprs
+  return "[" + elem_type->as_string () + "; " + size->as_string () + "]";
+}
+
+std::string
+SliceType::as_string () const
+{
+  // TODO: rewrite to work with non-linearisable types
+  return "[" + elem_type->as_string () + "]";
+}
+
+std::string
+TupleType::as_string () const
+{
+  // TODO: rewrite to work with non-linearisable types
+  std::string str ("(");
+
+  if (!is_unit_type ())
+    {
+      auto i = elems.begin ();
+      auto e = elems.end ();
+
+      for (; i != e; i++)
+	{
+	  str += (*i)->as_string ();
+	  if (e != i + 1)
+	    str += ", ";
+	}
+    }
+
+  str += ")";
+
+  return str;
+}
+
+std::string
+StructExpr::as_string () const
+{
+  std::string str = append_attributes (outer_attrs, OUTER);
+  indent_spaces (enter);
+  str += "\n" + indent_spaces (stay) + "StructExpr:";
+  indent_spaces (enter);
+  str += "\n" + indent_spaces (stay) + "PathInExpr:\n";
+  str += indent_spaces (stay) + struct_name.as_string ();
+  indent_spaces (out);
+  indent_spaces (out);
+  return str;
+}
+
+std::string
+StructExprStruct::as_string () const
+{
+  // TODO: doesn't this require data from StructExpr?
+  std::string str ("StructExprStruct (or subclass): ");
+
+  str += "\n Path: " + get_struct_name ().as_string ();
+
+  // inner attributes
+  str += append_attributes (inner_attrs, INNER);
+
+  return str;
+}
+
+std::string
+StructBase::as_string () const
+{
+  if (base_struct != nullptr)
+    return base_struct->as_string ();
+  else
+    return "ERROR_MARK_STRING - invalid struct base had as string applied";
+}
+
+std::string
+StructExprFieldWithVal::as_string () const
+{
+  // used to get value string
+  return value->as_string ();
+}
+
+std::string
+StructExprFieldIdentifierValue::as_string () const
+{
+  // TODO: rewrite to work with non-linearisable exprs
+  return field_name + " : " + StructExprFieldWithVal::as_string ();
+}
+
+std::string
+StructExprFieldIndexValue::as_string () const
+{
+  // TODO: rewrite to work with non-linearisable exprs
+  return std::to_string (index) + " : " + StructExprFieldWithVal::as_string ();
+}
+
+std::string
+StructExprStructFields::as_string () const
+{
+  std::string str = StructExprStruct::as_string ();
+
+  str += "\n Fields: ";
+  if (fields.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &field : fields)
+	str += "\n  " + field->as_string ();
+    }
+
+  str += "\n Struct base: ";
+  if (!has_struct_base ())
+    str += "none";
+  else
+    str += struct_base.as_string ();
+
+  return str;
+}
+
+std::string
+EnumItem::as_string () const
+{
+  std::string str = VisItem::as_string ();
+  str += variant_name;
+
+  return str;
+}
+
+std::string
+EnumItemTuple::as_string () const
+{
+  std::string str = EnumItem::as_string ();
+
+  // add tuple opening parens
+  str += "(";
+
+  // tuple fields
+  if (has_tuple_fields ())
+    {
+      auto i = tuple_fields.begin ();
+      auto e = tuple_fields.end ();
+
+      for (; i != e; i++)
+	{
+	  str += (*i).as_string ();
+	  if (e != i + 1)
+	    str += ", ";
+	}
+    }
+
+  // add tuple closing parens
+  str += ")";
+
+  return str;
+}
+
+std::string
+TupleField::as_string () const
+{
+  // TODO: rewrite to work with non-linearisable exprs
+
+  // outer attributes
+  std::string str = append_attributes (outer_attrs, OUTER);
+
+  if (has_visibility ())
+    str += "\n" + visibility.as_string ();
+
+  str += " " + field_type->as_string ();
+
+  return str;
+}
+
+std::string
+EnumItemStruct::as_string () const
+{
+  std::string str = EnumItem::as_string ();
+
+  // add struct opening parens
+  str += "{";
+
+  // tuple fields
+  if (has_struct_fields ())
+    {
+      auto i = struct_fields.begin ();
+      auto e = struct_fields.end ();
+
+      for (; i != e; i++)
+	{
+	  str += (*i).as_string ();
+	  if (e != i + 1)
+	    str += ", ";
+	}
+    }
+
+  // add struct closing parens
+  str += "}";
+
+  return str;
+}
+
+std::string
+StructField::as_string () const
+{
+  // TODO: rewrite to work with non-linearisable exprs
+  // outer attributes
+  std::string str = append_attributes (outer_attrs, OUTER);
+
+  if (has_visibility ())
+    str += "\n" + visibility.as_string ();
+
+  str += " " + field_name + " : " + field_type->as_string ();
+
+  return str;
+}
+
+std::string
+EnumItemDiscriminant::as_string () const
+{
+  // TODO: rewrite to work with non-linearisable exprs
+  std::string str = EnumItem::as_string ();
+
+  // add equal and expression
+  str += " = " + expression->as_string ();
+
+  return str;
+}
+
+std::string
+ExternalStaticItem::as_string () const
+{
+  // outer attributes
+  std::string str = append_attributes (outer_attrs, OUTER);
+
+  // start visibility on new line and with a space
+  str += "\n" + visibility.as_string () + " ";
+
+  str += "static ";
+
+  if (has_mut)
+    str += "mut ";
+
+  // add name
+  str += item_name;
+
+  // add type on new line
+  str += "\n Type: " + item_type->as_string ();
+
+  return str;
+}
+
+std::string
+ExternalFunctionItem::as_string () const
+{
+  // outer attributes
+  std::string str = append_attributes (outer_attrs, OUTER);
+
+  // start visibility on new line and with a space
+  str += "\n" + visibility.as_string () + " ";
+
+  str += "fn ";
+
+  // add name
+  str += item_name;
+
+  // generic params
+  str += "\n Generic params: ";
+  if (generic_params.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : generic_params)
+	{
+	  // DEBUG: null pointer check
+	  if (param == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"generic param in external function item.");
+	      return "NULL_POINTER_MARK";
+	    }
+
+	  str += "\n  " + param->as_string ();
+	}
+    }
+
+  // function params
+  str += "\n Function params: ";
+  if (function_params.empty () && !has_variadics)
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : function_params)
+	str += "\n  " + param.as_string ();
+
+      if (has_variadics)
+	{
+	  str += "\n  variadic outer attrs: ";
+	  if (has_variadic_outer_attrs ())
+	    {
+	      for (const auto &attr : variadic_outer_attrs)
+		str += "\n   " + attr.as_string ();
+	    }
+	  else
+	    {
+	      str += "none";
+	    }
+	  str += "\n  ... (variadic)";
+	}
+    }
+
+  // add type on new line
+  str += "\n (return) Type: "
+	 + (has_return_type () ? return_type->as_string () : "()");
+
+  // where clause
+  str += "\n Where clause: ";
+  if (has_where_clause ())
+    str += where_clause.as_string ();
+  else
+    str += "none";
+
+  return str;
+}
+
+std::string
+NamedFunctionParam::as_string () const
+{
+  std::string str = append_attributes (outer_attrs, OUTER);
+
+  str += "\n" + name;
+
+  str += "\n Type: " + param_type->as_string ();
+
+  return str;
+}
+
+std::string
+TraitItemFunc::as_string () const
+{
+  std::string str = append_attributes (outer_attrs, OUTER);
+
+  str += "\n" + decl.as_string ();
+
+  str += "\n Definition (block expr): ";
+  if (has_definition ())
+    str += block_expr->as_string ();
+  else
+    str += "none";
+
+  return str;
+}
+
+std::string
+TraitFunctionDecl::as_string () const
+{
+  std::string str = qualifiers.as_string () + "fn " + function_name;
+
+  // generic params
+  str += "\n Generic params: ";
+  if (generic_params.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : generic_params)
+	{
+	  // DEBUG: null pointer check
+	  if (param == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"generic param in trait function decl.");
+	      return "NULL_POINTER_MARK";
+	    }
+
+	  str += "\n  " + param->as_string ();
+	}
+    }
+
+  str += "\n Function params: ";
+  if (has_params ())
+    {
+      for (const auto &param : function_params)
+	str += "\n  " + param.as_string ();
+    }
+  else
+    {
+      str += "none";
+    }
+
+  str += "\n Return type: ";
+  if (has_return_type ())
+    str += return_type->as_string ();
+  else
+    str += "none (void)";
+
+  str += "\n Where clause: ";
+  if (has_where_clause ())
+    str += where_clause.as_string ();
+  else
+    str += "none";
+
+  return str;
+}
+
+std::string
+TraitItemMethod::as_string () const
+{
+  std::string str = append_attributes (outer_attrs, OUTER);
+
+  str += "\n" + decl.as_string ();
+
+  str += "\n Definition (block expr): ";
+  if (has_definition ())
+    str += block_expr->as_string ();
+  else
+    str += "none";
+
+  return str;
+}
+
+std::string
+TraitMethodDecl::as_string () const
+{
+  std::string str = qualifiers.as_string () + "fn " + function_name;
+
+  // generic params
+  str += "\n Generic params: ";
+  if (generic_params.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : generic_params)
+	{
+	  // DEBUG: null pointer check
+	  if (param == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"generic param in trait function decl.");
+	      return "NULL_POINTER_MARK";
+	    }
+
+	  str += "\n  " + param->as_string ();
+	}
+    }
+
+  str += "\n Self param: " + self_param.as_string ();
+
+  str += "\n Function params: ";
+  if (has_params ())
+    {
+      for (const auto &param : function_params)
+	str += "\n  " + param.as_string ();
+    }
+  else
+    {
+      str += "none";
+    }
+
+  str += "\n Return type: ";
+  if (has_return_type ())
+    str += return_type->as_string ();
+  else
+    str += "none (void)";
+
+  str += "\n Where clause: ";
+  if (has_where_clause ())
+    str += where_clause.as_string ();
+  else
+    str += "none";
+
+  return str;
+}
+
+std::string
+TraitItemConst::as_string () const
+{
+  // TODO: rewrite to work with non-linearisable exprs
+  std::string str = append_attributes (outer_attrs, OUTER);
+
+  str += "\nconst " + name + " : " + type->as_string ();
+
+  if (has_expression ())
+    str += " = " + expr->as_string ();
+
+  return str;
+}
+
+std::string
+TraitItemType::as_string () const
+{
+  std::string str = append_attributes (outer_attrs, OUTER);
+
+  str += "\ntype " + name;
+
+  str += "\n Type param bounds: ";
+  if (!has_type_param_bounds ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &bound : type_param_bounds)
+	{
+	  // DEBUG: null pointer check
+	  if (bound == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"type param bound in trait item type.");
+	      return "NULL_POINTER_MARK";
+	    }
+
+	  str += "\n  " + bound->as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+SelfParam::as_string () const
+{
+  // TODO: rewrite to allow non-linearisable types
+  if (is_error ())
+    {
+      return "error";
+    }
+  else
+    {
+      if (has_type ())
+	{
+	  // type (i.e. not ref, no lifetime)
+	  std::string str;
+
+	  if (is_mut)
+	    str += "mut ";
+
+	  str += "self : ";
+
+	  str += type->as_string ();
+
+	  return str;
+	}
+      else if (has_lifetime ())
+	{
+	  // ref and lifetime
+	  std::string str = "&" + lifetime.as_string () + " ";
+
+	  if (is_mut)
+	    str += "mut ";
+
+	  str += "self";
+
+	  return str;
+	}
+      else if (has_ref)
+	{
+	  // ref with no lifetime
+	  std::string str = "&";
+
+	  if (is_mut)
+	    str += " mut ";
+
+	  str += "self";
+
+	  return str;
+	}
+      else
+	{
+	  // no ref, no type
+	  std::string str;
+
+	  if (is_mut)
+	    str += "mut ";
+
+	  str += "self";
+
+	  return str;
+	}
+    }
+}
+
+std::string
+ArrayElemsCopied::as_string () const
+{
+  // TODO: rewrite to allow non-linearisable exprs
+  return elem_to_copy->as_string () + "; " + num_copies->as_string ();
+}
+
+std::string
+LifetimeWhereClauseItem::as_string () const
+{
+  std::string str ("Lifetime: ");
+
+  str += lifetime.as_string ();
+
+  str += "\nLifetime bounds: ";
+
+  for (const auto &bound : lifetime_bounds)
+    str += "\n " + bound.as_string ();
+
+  return str;
+}
+
+std::string
+TypeBoundWhereClauseItem::as_string () const
+{
+  std::string str ("For lifetimes: ");
+
+  if (!has_for_lifetimes ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &for_lifetime : for_lifetimes)
+	str += "\n " + for_lifetime.as_string ();
+    }
+
+  str += "\nType: " + bound_type->as_string ();
+
+  str += "\nType param bounds bounds: ";
+
+  for (const auto &bound : type_param_bounds)
+    {
+      // debug null pointer check
+      if (bound == nullptr)
+	return "NULL_POINTER_MARK - type param bounds";
+
+      str += "\n " + bound->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+ArrayElemsValues::as_string () const
+{
+  std::string str;
+
+  for (const auto &expr : values)
+    {
+      // DEBUG: null pointer check
+      if (expr == nullptr)
+	{
+	  rust_debug ("something really terrible has gone wrong - null pointer "
+		      "expr in array elems values.");
+	  return "NULL_POINTER_MARK";
+	}
+
+      str += "\n  " + expr->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+MaybeNamedParam::as_string () const
+{
+  // TODO: rewrite to allow using non-linearisable types in dump
+  std::string str;
+
+  switch (param_kind)
+    {
+    case UNNAMED:
+      break;
+    case IDENTIFIER:
+      str = name + " : ";
+      break;
+    case WILDCARD:
+      str = "_ : ";
+      break;
+    default:
+      return "ERROR_MARK_STRING - maybe named param unrecognised param kind";
+    }
+
+  str += param_type->as_string ();
+
+  return str;
+}
+
+MetaItemInner::~MetaItemInner () = default;
+
+std::unique_ptr<MetaNameValueStr>
+MetaItemInner::to_meta_name_value_str () const
+{
+  if (is_key_value_pair ())
+    {
+      auto converted_item = static_cast<const MetaNameValueStr *> (this);
+      return converted_item->to_meta_name_value_str ();
+    }
+  // TODO actually parse foo = bar
+  return nullptr;
+}
+
+std::string
+MetaItemSeq::as_string () const
+{
+  std::string path_str = path.as_string () + "(";
+
+  auto i = seq.begin ();
+  auto e = seq.end ();
+
+  for (; i != e; i++)
+    {
+      path_str += (*i)->as_string ();
+      if (e != i + 1)
+	path_str += ", ";
+    }
+
+  return path_str + ")";
+}
+
+std::string
+MetaListPaths::as_string () const
+{
+  std::string str = ident + "(";
+
+  auto i = paths.begin ();
+  auto e = paths.end ();
+
+  for (; i != e; i++)
+    {
+      str += (*i).as_string ();
+      if (e != i + 1)
+	str += ", ";
+    }
+
+  return str + ")";
+}
+
+std::string
+MetaListNameValueStr::as_string () const
+{
+  std::string str = ident + "(";
+
+  auto i = strs.begin ();
+  auto e = strs.end ();
+
+  for (; i != e; i++)
+    {
+      str += (*i).as_string ();
+      if (e != i + 1)
+	str += ", ";
+    }
+
+  return str + ")";
+}
+
+std::string
+AttrInputMetaItemContainer::as_string () const
+{
+  std::string str = "(";
+
+  auto i = items.begin ();
+  auto e = items.end ();
+
+  for (; i != e; i++)
+    {
+      str += (*i)->as_string ();
+      if (e != i + 1)
+	str += ", ";
+    }
+
+  return str + ")";
+}
+
+/* Override that calls the function recursively on all items contained within
+ * the module. */
+void
+Module::add_crate_name (std::vector<std::string> &names) const
+{
+  /* TODO: test whether module has been 'cfg'-ed out to determine whether to
+   * exclude it from search */
+
+  for (const auto &item : items)
+    item->add_crate_name (names);
+}
+
+static bool
+file_exists (const std::string path)
+{
+  // Simply check if the file exists
+  // FIXME: This does not work on Windows
+  return access (path.c_str (), F_OK) != -1;
+}
+
+static std::string
+filename_from_path_attribute (std::vector<Attribute> &outer_attrs)
+{
+  // An out-of-line module cannot have inner attributes. Additionally, the
+  // default name is specified as `""` so that the caller can detect the case
+  // of "no path given" and use the default path logic (`name.rs` or
+  // `name/mod.rs`).
+  return extract_module_path ({}, outer_attrs, "");
+}
+
+void
+Module::process_file_path ()
+{
+  rust_assert (kind == Module::ModuleKind::UNLOADED);
+  rust_assert (module_file.empty ());
+
+  // This corresponds to the path of the file 'including' the module. So the
+  // file that contains the 'mod <file>;' directive
+  std::string including_fname (outer_filename);
+
+  std::string expected_file_path = module_name + ".rs";
+  std::string expected_dir_path = "mod.rs";
+
+  auto dir_slash_pos = including_fname.rfind (file_separator);
+  std::string current_directory_name;
+
+  // If we haven't found a file_separator, then we have to look for files in the
+  // current directory ('.')
+  if (dir_slash_pos == std::string::npos)
+    current_directory_name = std::string (".") + file_separator;
+  else
+    current_directory_name
+      = including_fname.substr (0, dir_slash_pos) + file_separator;
+
+  // Handle inline module declarations adding path components.
+  for (auto const &name : module_scope)
+    {
+      current_directory_name.append (name);
+      current_directory_name.append (file_separator);
+    }
+
+  auto path_string = filename_from_path_attribute (get_outer_attrs ());
+  if (!path_string.empty ())
+    {
+      module_file = current_directory_name + path_string;
+      return;
+    }
+
+  // FIXME: We also have to search for
+  // <directory>/<including_fname>/<module_name>.rs In rustc, this is done via
+  // the concept of `DirOwnernship`, which is based on whether or not the
+  // current file is titled `mod.rs`.
+
+  // First, we search for <directory>/<module_name>.rs
+  std::string file_mod_path = current_directory_name + expected_file_path;
+  bool file_mod_found = file_exists (file_mod_path);
+
+  // Then, search for <directory>/<module_name>/mod.rs
+  std::string dir_mod_path
+    = current_directory_name + module_name + file_separator + expected_dir_path;
+  bool dir_mod_found = file_exists (dir_mod_path);
+
+  bool multiple_candidates_found = file_mod_found && dir_mod_found;
+  bool no_candidates_found = !file_mod_found && !dir_mod_found;
+
+  if (multiple_candidates_found)
+    rust_error_at (locus,
+		   "two candidates found for module %s: %s.rs and %s%smod.rs",
+		   module_name.c_str (), module_name.c_str (),
+		   module_name.c_str (), file_separator);
+
+  if (no_candidates_found)
+    rust_error_at (locus, "no candidate found for module %s",
+		   module_name.c_str ());
+
+  if (no_candidates_found || multiple_candidates_found)
+    return;
+
+  module_file = std::move (file_mod_found ? file_mod_path : dir_mod_path);
+}
+
+void
+Module::load_items ()
+{
+  process_file_path ();
+
+  // We will already have errored out appropriately in the process_file_path ()
+  // method
+  if (module_file.empty ())
+    return;
+
+  RAIIFile file_wrap (module_file.c_str ());
+  Linemap *linemap = Session::get_instance ().linemap;
+  if (!file_wrap.ok ())
+    {
+      rust_error_at (get_locus (), "cannot open module file %s: %m",
+		     module_file.c_str ());
+      return;
+    }
+
+  rust_debug ("Attempting to parse file %s", module_file.c_str ());
+
+  Lexer lex (module_file.c_str (), std::move (file_wrap), linemap);
+  Parser<Lexer> parser (lex);
+
+  // we need to parse any possible inner attributes for this module
+  inner_attrs = parser.parse_inner_attributes ();
+  auto parsed_items = parser.parse_items ();
+  for (const auto &error : parser.get_errors ())
+    error.emit_error ();
+
+  items = std::move (parsed_items);
+  kind = ModuleKind::LOADED;
+}
+
+void
+Attribute::parse_attr_to_meta_item ()
+{
+  // only parse if has attribute input and not already parsed
+  if (!has_attr_input () || is_parsed_to_meta_item ())
+    return;
+
+  auto res = attr_input->parse_to_meta_item ();
+  std::unique_ptr<AttrInput> converted_input (res);
+
+  if (converted_input != nullptr)
+    attr_input = std::move (converted_input);
+}
+
+AttrInputMetaItemContainer *
+DelimTokenTree::parse_to_meta_item () const
+{
+  // must have token trees
+  if (token_trees.empty ())
+    return nullptr;
+
+  /* assume top-level delim token tree in attribute - convert all nested ones
+   * to token stream */
+  std::vector<std::unique_ptr<Token>> token_stream = to_token_stream ();
+
+  AttributeParser parser (std::move (token_stream));
+  std::vector<std::unique_ptr<MetaItemInner>> meta_items (
+    parser.parse_meta_item_seq ());
+
+  return new AttrInputMetaItemContainer (std::move (meta_items));
+}
+
+std::unique_ptr<MetaItemInner>
+AttributeParser::parse_meta_item_inner ()
+{
+  // if first tok not identifier, not a "special" case one
+  if (peek_token ()->get_id () != IDENTIFIER)
+    {
+      switch (peek_token ()->get_id ())
+	{
+	case CHAR_LITERAL:
+	case STRING_LITERAL:
+	case BYTE_CHAR_LITERAL:
+	case BYTE_STRING_LITERAL:
+	case INT_LITERAL:
+	case FLOAT_LITERAL:
+	case TRUE_LITERAL:
+	case FALSE_LITERAL:
+	  return parse_meta_item_lit ();
+
+	case SUPER:
+	case SELF:
+	case CRATE:
+	case DOLLAR_SIGN:
+	case SCOPE_RESOLUTION:
+	  return parse_path_meta_item ();
+
+	default:
+	  rust_error_at (peek_token ()->get_locus (),
+			 "unrecognised token '%s' in meta item",
+			 get_token_description (peek_token ()->get_id ()));
+	  return nullptr;
+	}
+    }
+
+  // else, check for path
+  if (peek_token (1)->get_id () == SCOPE_RESOLUTION)
+    {
+      // path
+      return parse_path_meta_item ();
+    }
+
+  auto ident = peek_token ()->as_string ();
+  auto ident_locus = peek_token ()->get_locus ();
+
+  if (is_end_meta_item_tok (peek_token (1)->get_id ()))
+    {
+      // meta word syntax
+      skip_token ();
+      return std::unique_ptr<MetaWord> (new MetaWord (ident, ident_locus));
+    }
+
+  if (peek_token (1)->get_id () == EQUAL)
+    {
+      // maybe meta name value str syntax - check next 2 tokens
+      if (peek_token (2)->get_id () == STRING_LITERAL
+	  && is_end_meta_item_tok (peek_token (3)->get_id ()))
+	{
+	  // meta name value str syntax
+	  auto &value_tok = peek_token (2);
+	  auto value = value_tok->as_string ();
+	  auto locus = value_tok->get_locus ();
+
+	  skip_token (2);
+
+	  // remove the quotes from the string value
+	  std::string raw_value = unquote_string (std::move (value));
+
+	  return std::unique_ptr<MetaNameValueStr> (
+	    new MetaNameValueStr (ident, ident_locus, std::move (raw_value),
+				  locus));
+	}
+      else
+	{
+	  // just interpret as path-based meta item
+	  return parse_path_meta_item ();
+	}
+    }
+
+  if (peek_token (1)->get_id () != LEFT_PAREN)
+    {
+      rust_error_at (peek_token (1)->get_locus (),
+		     "unexpected token '%s' after identifier in attribute",
+		     get_token_description (peek_token (1)->get_id ()));
+      return nullptr;
+    }
+
+  // is it one of those special cases like not?
+  if (peek_token ()->get_id () == IDENTIFIER)
+    {
+      return parse_path_meta_item ();
+    }
+
+  auto meta_items = parse_meta_item_seq ();
+
+  // pass for meta name value str
+  std::vector<MetaNameValueStr> meta_name_value_str_items;
+  for (const auto &item : meta_items)
+    {
+      std::unique_ptr<MetaNameValueStr> converted_item
+	= item->to_meta_name_value_str ();
+      if (converted_item == nullptr)
+	{
+	  meta_name_value_str_items.clear ();
+	  break;
+	}
+      meta_name_value_str_items.push_back (std::move (*converted_item));
+    }
+  // if valid, return this
+  if (!meta_name_value_str_items.empty ())
+    {
+      return std::unique_ptr<MetaListNameValueStr> (
+	new MetaListNameValueStr (ident, ident_locus,
+				  std::move (meta_name_value_str_items)));
+    }
+
+  // // pass for meta list idents
+  // std::vector<Identifier> ident_items;
+  // for (const auto &item : meta_items)
+  //   {
+  //     std::unique_ptr<Identifier> converted_ident (item->to_ident_item ());
+  //     if (converted_ident == nullptr)
+  //       {
+  //         ident_items.clear ();
+  //         break;
+  //       }
+  //     ident_items.push_back (std::move (*converted_ident));
+  //   }
+  // // if valid return this
+  // if (!ident_items.empty ())
+  //   {
+  //     return std::unique_ptr<MetaListIdents> (
+  //       new MetaListIdents (std::move (ident), std::move (ident_items)));
+  //   }
+  // // as currently no meta list ident, currently no path. may change in future
+
+  // pass for meta list paths
+  std::vector<SimplePath> path_items;
+  for (const auto &item : meta_items)
+    {
+      SimplePath converted_path (item->to_path_item ());
+      if (converted_path.is_empty ())
+	{
+	  path_items.clear ();
+	  break;
+	}
+      path_items.push_back (std::move (converted_path));
+    }
+  if (!path_items.empty ())
+    {
+      return std::unique_ptr<MetaListPaths> (
+	new MetaListPaths (ident, ident_locus, std::move (path_items)));
+    }
+
+  rust_error_at (Linemap::unknown_location (),
+		 "failed to parse any meta item inner");
+  return nullptr;
+}
+
+bool
+AttributeParser::is_end_meta_item_tok (TokenId id) const
+{
+  return id == COMMA || id == RIGHT_PAREN;
+}
+
+std::unique_ptr<MetaItem>
+AttributeParser::parse_path_meta_item ()
+{
+  SimplePath path = parse_simple_path ();
+  if (path.is_empty ())
+    {
+      rust_error_at (peek_token ()->get_locus (),
+		     "failed to parse simple path in attribute");
+      return nullptr;
+    }
+
+  switch (peek_token ()->get_id ())
+    {
+      case LEFT_PAREN: {
+	std::vector<std::unique_ptr<MetaItemInner>> meta_items
+	  = parse_meta_item_seq ();
+
+	return std::unique_ptr<MetaItemSeq> (
+	  new MetaItemSeq (std::move (path), std::move (meta_items)));
+      }
+      case EQUAL: {
+	skip_token ();
+
+	Location locus = peek_token ()->get_locus ();
+	Literal lit = parse_literal ();
+	if (lit.is_error ())
+	  {
+	    rust_error_at (peek_token ()->get_locus (),
+			   "failed to parse literal in attribute");
+	    return nullptr;
+	  }
+	LiteralExpr expr (std::move (lit), {}, locus);
+	// stream_pos++;
+	/* shouldn't be required anymore due to parsing literal actually
+	 * skipping the token */
+	return std::unique_ptr<MetaItemPathLit> (
+	  new MetaItemPathLit (std::move (path), std::move (expr)));
+      }
+    case COMMA:
+      // just simple path
+      return std::unique_ptr<MetaItemPath> (
+	new MetaItemPath (std::move (path)));
+    default:
+      rust_error_at (peek_token ()->get_locus (),
+		     "unrecognised token '%s' in meta item",
+		     get_token_description (peek_token ()->get_id ()));
+      return nullptr;
+    }
+}
+
+/* Parses a parenthesised sequence of meta item inners. Parentheses are
+ * required here. */
+std::vector<std::unique_ptr<MetaItemInner>>
+AttributeParser::parse_meta_item_seq ()
+{
+  int vec_length = token_stream.size ();
+  std::vector<std::unique_ptr<MetaItemInner>> meta_items;
+
+  if (peek_token ()->get_id () != LEFT_PAREN)
+    {
+      rust_error_at (peek_token ()->get_locus (),
+		     "missing left paren in delim token tree");
+      return {};
+    }
+  skip_token ();
+
+  while (stream_pos < vec_length && peek_token ()->get_id () != RIGHT_PAREN)
+    {
+      std::unique_ptr<MetaItemInner> inner = parse_meta_item_inner ();
+      if (inner == nullptr)
+	{
+	  rust_error_at (peek_token ()->get_locus (),
+			 "failed to parse inner meta item in attribute");
+	  return {};
+	}
+      meta_items.push_back (std::move (inner));
+
+      if (peek_token ()->get_id () != COMMA)
+	break;
+
+      skip_token ();
+    }
+
+  if (peek_token ()->get_id () != RIGHT_PAREN)
+    {
+      rust_error_at (peek_token ()->get_locus (),
+		     "missing right paren in delim token tree");
+      return {};
+    }
+  skip_token ();
+
+  return meta_items;
+}
+
+/* Collects any nested token trees into a flat token stream, suitable for
+ * parsing. */
+std::vector<std::unique_ptr<Token>>
+DelimTokenTree::to_token_stream () const
+{
+  std::vector<std::unique_ptr<Token>> tokens;
+  for (const auto &tree : token_trees)
+    {
+      std::vector<std::unique_ptr<Token>> stream = tree->to_token_stream ();
+
+      tokens.insert (tokens.end (), std::make_move_iterator (stream.begin ()),
+		     std::make_move_iterator (stream.end ()));
+    }
+
+  tokens.shrink_to_fit ();
+  return tokens;
+}
+
+Literal
+AttributeParser::parse_literal ()
+{
+  const std::unique_ptr<Token> &tok = peek_token ();
+  switch (tok->get_id ())
+    {
+    case CHAR_LITERAL:
+      skip_token ();
+      return Literal (tok->as_string (), Literal::CHAR, tok->get_type_hint ());
+    case STRING_LITERAL:
+      skip_token ();
+      return Literal (tok->as_string (), Literal::STRING,
+		      tok->get_type_hint ());
+    case BYTE_CHAR_LITERAL:
+      skip_token ();
+      return Literal (tok->as_string (), Literal::BYTE, tok->get_type_hint ());
+    case BYTE_STRING_LITERAL:
+      skip_token ();
+      return Literal (tok->as_string (), Literal::BYTE_STRING,
+		      tok->get_type_hint ());
+    case INT_LITERAL:
+      skip_token ();
+      return Literal (tok->as_string (), Literal::INT, tok->get_type_hint ());
+    case FLOAT_LITERAL:
+      skip_token ();
+      return Literal (tok->as_string (), Literal::FLOAT, tok->get_type_hint ());
+    case TRUE_LITERAL:
+      skip_token ();
+      return Literal ("true", Literal::BOOL, tok->get_type_hint ());
+    case FALSE_LITERAL:
+      skip_token ();
+      return Literal ("false", Literal::BOOL, tok->get_type_hint ());
+    default:
+      rust_error_at (tok->get_locus (), "expected literal - found '%s'",
+		     get_token_description (tok->get_id ()));
+      return Literal::create_error ();
+    }
+}
+
+SimplePath
+AttributeParser::parse_simple_path ()
+{
+  bool has_opening_scope_res = false;
+  if (peek_token ()->get_id () == SCOPE_RESOLUTION)
+    {
+      has_opening_scope_res = true;
+      skip_token ();
+    }
+
+  std::vector<SimplePathSegment> segments;
+
+  SimplePathSegment segment = parse_simple_path_segment ();
+  if (segment.is_error ())
+    {
+      rust_error_at (
+	peek_token ()->get_locus (),
+	"failed to parse simple path segment in attribute simple path");
+      return SimplePath::create_empty ();
+    }
+  segments.push_back (std::move (segment));
+
+  while (peek_token ()->get_id () == SCOPE_RESOLUTION)
+    {
+      skip_token ();
+
+      SimplePathSegment segment = parse_simple_path_segment ();
+      if (segment.is_error ())
+	{
+	  rust_error_at (
+	    peek_token ()->get_locus (),
+	    "failed to parse simple path segment in attribute simple path");
+	  return SimplePath::create_empty ();
+	}
+      segments.push_back (std::move (segment));
+    }
+  segments.shrink_to_fit ();
+
+  return SimplePath (std::move (segments), has_opening_scope_res);
+}
+
+SimplePathSegment
+AttributeParser::parse_simple_path_segment ()
+{
+  const std::unique_ptr<Token> &tok = peek_token ();
+  switch (tok->get_id ())
+    {
+    case IDENTIFIER:
+      skip_token ();
+      return SimplePathSegment (tok->as_string (), tok->get_locus ());
+    case SUPER:
+      skip_token ();
+      return SimplePathSegment ("super", tok->get_locus ());
+    case SELF:
+      skip_token ();
+      return SimplePathSegment ("self", tok->get_locus ());
+    case CRATE:
+      skip_token ();
+      return SimplePathSegment ("crate", tok->get_locus ());
+    case DOLLAR_SIGN:
+      if (peek_token (1)->get_id () == CRATE)
+	{
+	  skip_token (1);
+	  return SimplePathSegment ("$crate", tok->get_locus ());
+	}
+      gcc_fallthrough ();
+    default:
+      rust_error_at (tok->get_locus (),
+		     "unexpected token '%s' in simple path segment",
+		     get_token_description (tok->get_id ()));
+      return SimplePathSegment::create_error ();
+    }
+}
+
+std::unique_ptr<MetaItemLitExpr>
+AttributeParser::parse_meta_item_lit ()
+{
+  Location locus = peek_token ()->get_locus ();
+  LiteralExpr lit_expr (parse_literal (), {}, locus);
+  return std::unique_ptr<MetaItemLitExpr> (
+    new MetaItemLitExpr (std::move (lit_expr)));
+}
+
+bool
+AttrInputMetaItemContainer::check_cfg_predicate (const Session &session) const
+{
+  if (items.empty ())
+    return false;
+
+  for (const auto &inner_item : items)
+    {
+      if (!inner_item->check_cfg_predicate (session))
+	return false;
+    }
+
+  return true;
+}
+
+bool
+MetaItemLitExpr::check_cfg_predicate (const Session &) const
+{
+  /* as far as I can tell, a literal expr can never be a valid cfg body, so
+   * false */
+  return false;
+}
+
+bool
+MetaListNameValueStr::check_cfg_predicate (const Session &session) const
+{
+  if (ident == "all")
+    {
+      for (const auto &str : strs)
+	{
+	  if (!str.check_cfg_predicate (session))
+	    return false;
+	}
+      return true;
+    }
+  else if (ident == "any")
+    {
+      for (const auto &str : strs)
+	{
+	  if (str.check_cfg_predicate (session))
+	    return true;
+	}
+      return false;
+    }
+  else if (ident == "not")
+    {
+      if (strs.size () != 1)
+	{
+	  /* HACK: convert vector platform-dependent size_type to string to
+	   * use in printf */
+	  rust_error_at (Linemap::unknown_location (),
+			 "cfg predicate could not be checked for "
+			 "MetaListNameValueStr with ident of "
+			 "'not' because there are '%s' elements, not '1'",
+			 std::to_string (strs.size ()).c_str ());
+	  return false;
+	}
+
+      return !strs[0].check_cfg_predicate (session);
+    }
+  else
+    {
+      rust_error_at (Linemap::unknown_location (),
+		     "cfg predicate could not be checked for "
+		     "MetaListNameValueStr with ident of "
+		     "'%s' - ident must be 'all' or 'any'",
+		     ident.c_str ());
+      return false;
+    }
+}
+
+bool
+MetaListPaths::check_cfg_predicate (const Session &session) const
+{
+  if (ident == "all")
+    {
+      for (const auto &path : paths)
+	{
+	  if (!check_path_exists_in_cfg (session, path))
+	    return false;
+	}
+      return true;
+    }
+  else if (ident == "any")
+    {
+      for (const auto &path : paths)
+	{
+	  if (check_path_exists_in_cfg (session, path))
+	    return true;
+	}
+      return false;
+    }
+  else if (ident == "not")
+    {
+      if (paths.size () != 1)
+	{
+	  // HACK: convert vector platform-dependent size_type to string to
+	  // use in printf
+	  rust_error_at (Linemap::unknown_location (),
+			 "cfg predicate could not be checked for MetaListPaths "
+			 "with ident of 'not' "
+			 "because there are '%s' elements, not '1'",
+			 std::to_string (paths.size ()).c_str ());
+	  return false;
+	}
+
+      return !check_path_exists_in_cfg (session, paths[0]);
+    }
+  else
+    {
+      rust_error_at (Linemap::unknown_location (),
+		     "cfg predicate could not be checked for "
+		     "MetaListNameValueStr with ident of "
+		     "'%s' - ident must be 'all' or 'any'",
+		     ident.c_str ());
+      return false;
+    }
+}
+
+bool
+MetaListPaths::check_path_exists_in_cfg (const Session &session,
+					 const SimplePath &path) const
+{
+  return session.options.target_data.has_key (path.as_string ());
+}
+
+bool
+MetaItemSeq::check_cfg_predicate (const Session &session) const
+{
+  if (path.as_string () == "all")
+    {
+      for (const auto &item : seq)
+	{
+	  if (!item->check_cfg_predicate (session))
+	    return false;
+	}
+      return true;
+    }
+  else if (path.as_string () == "any")
+    {
+      for (const auto &item : seq)
+	{
+	  if (item->check_cfg_predicate (session))
+	    return true;
+	}
+      return false;
+    }
+  else if (path.as_string () == "not")
+    {
+      if (seq.size () != 1)
+	{
+	  /* HACK: convert vector platform-dependent size_type to string to
+	   * use in printf */
+	  rust_error_at (Linemap::unknown_location (),
+			 "cfg predicate could not be checked for MetaItemSeq "
+			 "with ident of 'not' "
+			 "because there are '%s' elements, not '1'",
+			 std::to_string (seq.size ()).c_str ());
+	  return false;
+	}
+
+      return !seq[0]->check_cfg_predicate (session);
+    }
+  else
+    {
+      rust_error_at (
+	Linemap::unknown_location (),
+	"cfg predicate could not be checked for MetaItemSeq with path of "
+	"'%s' - path must be 'all' or 'any'",
+	path.as_string ().c_str ());
+      return false;
+    }
+}
+
+bool
+MetaWord::check_cfg_predicate (const Session &session) const
+{
+  return session.options.target_data.has_key (ident);
+}
+
+bool
+MetaItemPath::check_cfg_predicate (const Session &session) const
+{
+  /* Strictly speaking, this should always be false, but maybe do check
+   * relating to SimplePath being identifier. Currently, it would return true
+   * if path as identifier existed, and if the path in string form existed
+   * (though this shouldn't occur). */
+  return session.options.target_data.has_key (path.as_string ());
+}
+
+bool
+MetaNameValueStr::check_cfg_predicate (const Session &session) const
+{
+  // DEBUG
+  rust_debug (
+    "checked key-value pair for cfg: '%s', '%s' - is%s in target data",
+    ident.c_str (), str.c_str (),
+    session.options.target_data.has_key_value_pair (ident, str) ? "" : " not");
+
+  return session.options.target_data.has_key_value_pair (ident, str);
+}
+
+bool
+MetaItemPathLit::check_cfg_predicate (const Session &session) const
+{
+  return session.options.target_data.has_key_value_pair (path.as_string (),
+							 lit.as_string ());
+}
+
+std::vector<std::unique_ptr<Token>>
+Token::to_token_stream () const
+{
+  /* initialisation list doesn't work as it needs copy constructor, so have to
+   * do this */
+  std::vector<std::unique_ptr<Token>> dummy_vector;
+  dummy_vector.reserve (1);
+  dummy_vector.push_back (std::unique_ptr<Token> (clone_token_impl ()));
+  return dummy_vector;
+}
+
+Attribute
+MetaNameValueStr::to_attribute () const
+{
+  LiteralExpr lit_expr (str, Literal::LitType::STRING,
+			PrimitiveCoreType::CORETYPE_UNKNOWN, {}, str_locus);
+  // FIXME: What location do we put here? Is the literal above supposed to have
+  // an empty location as well?
+  // Should MetaNameValueStr keep a location?
+  return Attribute (SimplePath::from_str (ident, ident_locus),
+		    std::unique_ptr<AttrInputLiteral> (
+		      new AttrInputLiteral (std::move (lit_expr))));
+}
+
+Attribute
+MetaItemPath::to_attribute () const
+{
+  return Attribute (path, nullptr);
+}
+
+Attribute
+MetaItemSeq::to_attribute () const
+{
+  std::vector<std::unique_ptr<MetaItemInner>> new_seq;
+  new_seq.reserve (seq.size ());
+  for (const auto &e : seq)
+    new_seq.push_back (e->clone_meta_item_inner ());
+
+  std::unique_ptr<AttrInputMetaItemContainer> new_seq_container (
+    new AttrInputMetaItemContainer (std::move (new_seq)));
+  return Attribute (path, std::move (new_seq_container));
+}
+
+Attribute
+MetaWord::to_attribute () const
+{
+  return Attribute (SimplePath::from_str (ident, ident_locus), nullptr);
+}
+
+Attribute
+MetaListPaths::to_attribute () const
+{
+  /* probably one of the most annoying conversions - have to lose specificity by
+   * turning it into just AttrInputMetaItemContainer (i.e. paths-only nature is
+   * no longer known). If conversions back are required, might have to do a
+   * "check all are paths" pass or something. */
+
+  std::vector<std::unique_ptr<MetaItemInner>> new_seq;
+  new_seq.reserve (paths.size ());
+  for (const auto &e : paths)
+    new_seq.push_back (std::unique_ptr<MetaItemPath> (new MetaItemPath (e)));
+
+  std::unique_ptr<AttrInputMetaItemContainer> new_seq_container (
+    new AttrInputMetaItemContainer (std::move (new_seq)));
+  return Attribute (SimplePath::from_str (ident, ident_locus),
+		    std::move (new_seq_container));
+}
+
+Attribute
+MetaListNameValueStr::to_attribute () const
+{
+  std::vector<std::unique_ptr<MetaItemInner>> new_seq;
+  new_seq.reserve (strs.size ());
+  for (const auto &e : strs)
+    new_seq.push_back (
+      std::unique_ptr<MetaNameValueStr> (new MetaNameValueStr (e)));
+
+  std::unique_ptr<AttrInputMetaItemContainer> new_seq_container (
+    new AttrInputMetaItemContainer (std::move (new_seq)));
+  return Attribute (SimplePath::from_str (ident, ident_locus),
+		    std::move (new_seq_container));
+}
+
+Attribute
+MetaItemPathLit::to_attribute () const
+{
+  return Attribute (path, std::unique_ptr<AttrInputLiteral> (
+			    new AttrInputLiteral (lit)));
+}
+
+std::vector<Attribute>
+AttrInputMetaItemContainer::separate_cfg_attrs () const
+{
+  rust_assert (!items.empty ());
+
+  if (items.size () == 1)
+    return {};
+
+  std::vector<Attribute> attrs;
+  attrs.reserve (items.size () - 1);
+
+  for (auto it = items.begin () + 1; it != items.end (); ++it)
+    {
+      Attribute attr = (*it)->to_attribute ();
+      if (attr.is_empty ())
+	{
+	  /* TODO should this be an error that causes us to chuck out
+	   * everything? */
+	  continue;
+	}
+      attrs.push_back (std::move (attr));
+    }
+
+  attrs.shrink_to_fit ();
+  return attrs;
+}
+
+bool
+Attribute::check_cfg_predicate (const Session &session) const
+{
+  /* assume that cfg predicate actually can exist, i.e. attribute has cfg or
+   * cfg_attr path */
+  if (!has_attr_input ()
+      || (path.as_string () != "cfg" && path.as_string () != "cfg_attr"))
+    {
+      // DEBUG message
+      rust_debug (
+	"tried to check cfg predicate on attr that either has no input "
+	"or invalid path. attr: '%s'",
+	as_string ().c_str ());
+
+      return false;
+    }
+
+  // assume that it has already been parsed
+  if (!is_parsed_to_meta_item ())
+    return false;
+
+  return attr_input->check_cfg_predicate (session);
+}
+
+std::vector<Attribute>
+Attribute::separate_cfg_attrs () const
+{
+  if (!has_attr_input () || path.as_string () != "cfg_attr")
+    return {};
+
+  // assume that it has already been parsed
+  if (!is_parsed_to_meta_item ())
+    return {};
+
+  return attr_input->separate_cfg_attrs ();
+}
+
+bool
+Attribute::is_parsed_to_meta_item () const
+{
+  return has_attr_input () && attr_input->is_meta_item ();
+}
+
+/* Visitor implementations - these are short but inlining can't happen anyway
+ * due to virtual functions and I didn't want to make the ast header includes
+ * any longer than they already are. */
+
+void
+Token::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+DelimTokenTree::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IdentifierExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Lifetime::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+LifetimeParam::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ConstGenericParam::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+PathInExpression::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TypePathSegment::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TypePathSegmentGeneric::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TypePathSegmentFunction::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TypePath::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+QualifiedPathInExpression::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+QualifiedPathInType::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+LiteralExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+AttrInputLiteral::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+MetaItemLitExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+MetaItemPathLit::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+BorrowExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+DereferenceExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ErrorPropagationExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+NegationExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ArithmeticOrLogicalExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ComparisonExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+LazyBooleanExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TypeCastExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+AssignmentExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+CompoundAssignmentExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+GroupedExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ArrayElemsValues::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ArrayElemsCopied::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ArrayExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ArrayIndexExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TupleExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TupleIndexExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructExprStruct::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructExprFieldIdentifier::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructExprFieldIdentifierValue::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructExprFieldIndexValue::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructExprStructFields::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructExprStructBase::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+CallExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+MethodCallExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+FieldAccessExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ClosureExprInner::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+BlockExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ClosureExprInnerTyped::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ContinueExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+BreakExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangeFromToExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangeFromExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangeToExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangeFullExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangeFromToInclExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangeToInclExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ReturnExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+UnsafeBlockExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+LoopExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+WhileLoopExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+WhileLetLoopExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ForLoopExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfExprConseqElse::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfExprConseqIf::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfExprConseqIfLet::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfLetExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfLetExprConseqElse::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfLetExprConseqIf::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfLetExprConseqIfLet::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+MatchExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+AwaitExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+AsyncBlockExpr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TypeParam::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+LifetimeWhereClauseItem::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TypeBoundWhereClauseItem::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Method::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Module::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ExternCrate::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+UseTreeGlob::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+UseTreeList::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+UseTreeRebind::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+UseDeclaration::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Function::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TypeAlias::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructStruct::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TupleStruct::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+EnumItem::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+EnumItemTuple::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+EnumItemStruct::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+EnumItemDiscriminant::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Enum::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Union::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ConstantItem::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StaticItem::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TraitItemFunc::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TraitItemMethod::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TraitItemConst::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TraitItemType::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Trait::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+InherentImpl::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TraitImpl::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ExternalStaticItem::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ExternalFunctionItem::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ExternBlock::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+MacroMatchFragment::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+MacroMatchRepetition::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+MacroMatcher::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+MacroRulesDefinition::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+MacroInvocation::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+LiteralPattern::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IdentifierPattern::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+WildcardPattern::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangePatternBoundLiteral::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangePatternBoundPath::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangePatternBoundQualPath::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangePattern::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ReferencePattern::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructPatternFieldTuplePat::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructPatternFieldIdentPat::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructPatternFieldIdent::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructPattern::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TupleStructItemsNoRange::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TupleStructItemsRange::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TupleStructPattern::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TuplePatternItemsMultiple::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TuplePatternItemsRanged::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TuplePattern::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+GroupedPattern::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+SlicePattern::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+EmptyStmt::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+LetStmt::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ExprStmtWithoutBlock::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ExprStmtWithBlock::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TraitBound::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ImplTraitType::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TraitObjectType::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ParenthesisedType::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ImplTraitTypeOneBound::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TraitObjectTypeOneBound::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TupleType::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+NeverType::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RawPointerType::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ReferenceType::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ArrayType::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+SliceType::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+InferredType::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+BareFunctionType::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+MetaItemSeq::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+MetaItemPath::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+MetaListPaths::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+MetaNameValueStr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+MetaListNameValueStr::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+AttrInputMetaItemContainer::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+MetaWord::accept_vis (ASTVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+GenericArg
+GenericArg::disambiguate_to_const () const
+{
+  rust_assert (get_kind () == Kind::Either);
+
+  // FIXME: is it fine to have no outer attributes?
+  return GenericArg::create_const (
+    std::unique_ptr<Expr> (new IdentifierExpr (path, {}, locus)));
+}
+
+GenericArg
+GenericArg::disambiguate_to_type () const
+{
+  rust_assert (get_kind () == Kind::Either);
+
+  auto segment = std::unique_ptr<TypePathSegment> (
+    new TypePathSegment (path, false, locus));
+  auto segments = std::vector<std::unique_ptr<TypePathSegment>> ();
+  segments.emplace_back (std::move (segment));
+
+  return GenericArg::create_type (
+    std::unique_ptr<Type> (new TypePath (std::move (segments), locus)));
+}
+
+} // namespace AST
+} // namespace Rust
diff --git a/gcc/rust/ast/rust-ast-full.h b/gcc/rust/ast/rust-ast-full.h
new file mode 100644
index 00000000000..5ab136c61b6
--- /dev/null
+++ b/gcc/rust/ast/rust-ast-full.h
@@ -0,0 +1,31 @@
+// 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_AST_FULL_H
+#define RUST_AST_FULL_H
+// Use as a fast way of including all aspects of the AST (i.e. all headers)
+#include "rust-ast.h"
+#include "rust-expr.h"
+#include "rust-item.h"
+#include "rust-path.h"
+#include "rust-pattern.h"
+#include "rust-stmt.h"
+#include "rust-type.h"
+#include "rust-macro.h"
+
+#endif
diff --git a/gcc/rust/ast/rust-ast-visitor.h b/gcc/rust/ast/rust-ast-visitor.h
new file mode 100644
index 00000000000..bbb04771fea
--- /dev/null
+++ b/gcc/rust/ast/rust-ast-visitor.h
@@ -0,0 +1,234 @@
+// 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_AST_VISITOR_H
+#define RUST_AST_VISITOR_H
+// Visitor base for AST
+
+// full include not required - only forward decls
+#include "rust-ast-full-decls.h"
+
+namespace Rust {
+namespace AST {
+/* Pure abstract class that provides an interface for accessing different
+ * classes of the AST. */
+class ASTVisitor
+{
+public:
+  // only concrete class overloads are required
+
+  // rust-ast.h
+  // virtual void visit(AttrInput& attr_input) = 0;
+  // virtual void visit(TokenTree& token_tree) = 0;
+  // virtual void visit(MacroMatch& macro_match) = 0;
+  virtual void visit (Token &tok) = 0;
+  virtual void visit (DelimTokenTree &delim_tok_tree) = 0;
+  virtual void visit (AttrInputMetaItemContainer &input) = 0;
+  // virtual void visit(MetaItem& meta_item) = 0;
+  // virtual void visit(Stmt& stmt) = 0;
+  // virtual void visit(Expr& expr) = 0;
+  virtual void visit (IdentifierExpr &ident_expr) = 0;
+  // virtual void visit(Pattern& pattern) = 0;
+  // virtual void visit(Type& type) = 0;
+  // virtual void visit(TypeParamBound& type_param_bound) = 0;
+  virtual void visit (Lifetime &lifetime) = 0;
+  // virtual void visit(GenericParam& generic_param) = 0;
+  virtual void visit (LifetimeParam &lifetime_param) = 0;
+  virtual void visit (ConstGenericParam &const_param) = 0;
+  // virtual void visit(TraitItem& trait_item) = 0;
+  // virtual void visit(InherentImplItem& inherent_impl_item) = 0;
+  // virtual void visit(TraitImplItem& trait_impl_item) = 0;
+
+  // rust-path.h
+  virtual void visit (PathInExpression &path) = 0;
+  virtual void visit (TypePathSegment &segment) = 0;
+  virtual void visit (TypePathSegmentGeneric &segment) = 0;
+  virtual void visit (TypePathSegmentFunction &segment) = 0;
+  virtual void visit (TypePath &path) = 0;
+  virtual void visit (QualifiedPathInExpression &path) = 0;
+  virtual void visit (QualifiedPathInType &path) = 0;
+
+  // rust-expr.h
+  virtual void visit (LiteralExpr &expr) = 0;
+  virtual void visit (AttrInputLiteral &attr_input) = 0;
+  virtual void visit (MetaItemLitExpr &meta_item) = 0;
+  virtual void visit (MetaItemPathLit &meta_item) = 0;
+  virtual void visit (BorrowExpr &expr) = 0;
+  virtual void visit (DereferenceExpr &expr) = 0;
+  virtual void visit (ErrorPropagationExpr &expr) = 0;
+  virtual void visit (NegationExpr &expr) = 0;
+  virtual void visit (ArithmeticOrLogicalExpr &expr) = 0;
+  virtual void visit (ComparisonExpr &expr) = 0;
+  virtual void visit (LazyBooleanExpr &expr) = 0;
+  virtual void visit (TypeCastExpr &expr) = 0;
+  virtual void visit (AssignmentExpr &expr) = 0;
+  virtual void visit (CompoundAssignmentExpr &expr) = 0;
+  virtual void visit (GroupedExpr &expr) = 0;
+  // virtual void visit(ArrayElems& elems) = 0;
+  virtual void visit (ArrayElemsValues &elems) = 0;
+  virtual void visit (ArrayElemsCopied &elems) = 0;
+  virtual void visit (ArrayExpr &expr) = 0;
+  virtual void visit (ArrayIndexExpr &expr) = 0;
+  virtual void visit (TupleExpr &expr) = 0;
+  virtual void visit (TupleIndexExpr &expr) = 0;
+  virtual void visit (StructExprStruct &expr) = 0;
+  // virtual void visit(StructExprField& field) = 0;
+  virtual void visit (StructExprFieldIdentifier &field) = 0;
+  virtual void visit (StructExprFieldIdentifierValue &field) = 0;
+  virtual void visit (StructExprFieldIndexValue &field) = 0;
+  virtual void visit (StructExprStructFields &expr) = 0;
+  virtual void visit (StructExprStructBase &expr) = 0;
+  virtual void visit (CallExpr &expr) = 0;
+  virtual void visit (MethodCallExpr &expr) = 0;
+  virtual void visit (FieldAccessExpr &expr) = 0;
+  virtual void visit (ClosureExprInner &expr) = 0;
+  virtual void visit (BlockExpr &expr) = 0;
+  virtual void visit (ClosureExprInnerTyped &expr) = 0;
+  virtual void visit (ContinueExpr &expr) = 0;
+  virtual void visit (BreakExpr &expr) = 0;
+  virtual void visit (RangeFromToExpr &expr) = 0;
+  virtual void visit (RangeFromExpr &expr) = 0;
+  virtual void visit (RangeToExpr &expr) = 0;
+  virtual void visit (RangeFullExpr &expr) = 0;
+  virtual void visit (RangeFromToInclExpr &expr) = 0;
+  virtual void visit (RangeToInclExpr &expr) = 0;
+  virtual void visit (ReturnExpr &expr) = 0;
+  virtual void visit (UnsafeBlockExpr &expr) = 0;
+  virtual void visit (LoopExpr &expr) = 0;
+  virtual void visit (WhileLoopExpr &expr) = 0;
+  virtual void visit (WhileLetLoopExpr &expr) = 0;
+  virtual void visit (ForLoopExpr &expr) = 0;
+  virtual void visit (IfExpr &expr) = 0;
+  virtual void visit (IfExprConseqElse &expr) = 0;
+  virtual void visit (IfExprConseqIf &expr) = 0;
+  virtual void visit (IfExprConseqIfLet &expr) = 0;
+  virtual void visit (IfLetExpr &expr) = 0;
+  virtual void visit (IfLetExprConseqElse &expr) = 0;
+  virtual void visit (IfLetExprConseqIf &expr) = 0;
+  virtual void visit (IfLetExprConseqIfLet &expr) = 0;
+  // virtual void visit(MatchCase& match_case) = 0;
+  // virtual void visit (MatchCaseBlockExpr &match_case) = 0;
+  // virtual void visit (MatchCaseExpr &match_case) = 0;
+  virtual void visit (MatchExpr &expr) = 0;
+  virtual void visit (AwaitExpr &expr) = 0;
+  virtual void visit (AsyncBlockExpr &expr) = 0;
+
+  // rust-item.h
+  virtual void visit (TypeParam &param) = 0;
+  // virtual void visit(WhereClauseItem& item) = 0;
+  virtual void visit (LifetimeWhereClauseItem &item) = 0;
+  virtual void visit (TypeBoundWhereClauseItem &item) = 0;
+  virtual void visit (Method &method) = 0;
+  virtual void visit (Module &module) = 0;
+  virtual void visit (ExternCrate &crate) = 0;
+  // virtual void visit(UseTree& use_tree) = 0;
+  virtual void visit (UseTreeGlob &use_tree) = 0;
+  virtual void visit (UseTreeList &use_tree) = 0;
+  virtual void visit (UseTreeRebind &use_tree) = 0;
+  virtual void visit (UseDeclaration &use_decl) = 0;
+  virtual void visit (Function &function) = 0;
+  virtual void visit (TypeAlias &type_alias) = 0;
+  virtual void visit (StructStruct &struct_item) = 0;
+  virtual void visit (TupleStruct &tuple_struct) = 0;
+  virtual void visit (EnumItem &item) = 0;
+  virtual void visit (EnumItemTuple &item) = 0;
+  virtual void visit (EnumItemStruct &item) = 0;
+  virtual void visit (EnumItemDiscriminant &item) = 0;
+  virtual void visit (Enum &enum_item) = 0;
+  virtual void visit (Union &union_item) = 0;
+  virtual void visit (ConstantItem &const_item) = 0;
+  virtual void visit (StaticItem &static_item) = 0;
+  virtual void visit (TraitItemFunc &item) = 0;
+  virtual void visit (TraitItemMethod &item) = 0;
+  virtual void visit (TraitItemConst &item) = 0;
+  virtual void visit (TraitItemType &item) = 0;
+  virtual void visit (Trait &trait) = 0;
+  virtual void visit (InherentImpl &impl) = 0;
+  virtual void visit (TraitImpl &impl) = 0;
+  // virtual void visit(ExternalItem& item) = 0;
+  virtual void visit (ExternalStaticItem &item) = 0;
+  virtual void visit (ExternalFunctionItem &item) = 0;
+  virtual void visit (ExternBlock &block) = 0;
+
+  // rust-macro.h
+  virtual void visit (MacroMatchFragment &match) = 0;
+  virtual void visit (MacroMatchRepetition &match) = 0;
+  virtual void visit (MacroMatcher &matcher) = 0;
+  virtual void visit (MacroRulesDefinition &rules_def) = 0;
+  virtual void visit (MacroInvocation &macro_invoc) = 0;
+  virtual void visit (MetaItemPath &meta_item) = 0;
+  virtual void visit (MetaItemSeq &meta_item) = 0;
+  virtual void visit (MetaWord &meta_item) = 0;
+  virtual void visit (MetaNameValueStr &meta_item) = 0;
+  virtual void visit (MetaListPaths &meta_item) = 0;
+  virtual void visit (MetaListNameValueStr &meta_item) = 0;
+
+  // rust-pattern.h
+  virtual void visit (LiteralPattern &pattern) = 0;
+  virtual void visit (IdentifierPattern &pattern) = 0;
+  virtual void visit (WildcardPattern &pattern) = 0;
+  // virtual void visit(RangePatternBound& bound) = 0;
+  virtual void visit (RangePatternBoundLiteral &bound) = 0;
+  virtual void visit (RangePatternBoundPath &bound) = 0;
+  virtual void visit (RangePatternBoundQualPath &bound) = 0;
+  virtual void visit (RangePattern &pattern) = 0;
+  virtual void visit (ReferencePattern &pattern) = 0;
+  // virtual void visit(StructPatternField& field) = 0;
+  virtual void visit (StructPatternFieldTuplePat &field) = 0;
+  virtual void visit (StructPatternFieldIdentPat &field) = 0;
+  virtual void visit (StructPatternFieldIdent &field) = 0;
+  virtual void visit (StructPattern &pattern) = 0;
+  // virtual void visit(TupleStructItems& tuple_items) = 0;
+  virtual void visit (TupleStructItemsNoRange &tuple_items) = 0;
+  virtual void visit (TupleStructItemsRange &tuple_items) = 0;
+  virtual void visit (TupleStructPattern &pattern) = 0;
+  // virtual void visit(TuplePatternItems& tuple_items) = 0;
+  virtual void visit (TuplePatternItemsMultiple &tuple_items) = 0;
+  virtual void visit (TuplePatternItemsRanged &tuple_items) = 0;
+  virtual void visit (TuplePattern &pattern) = 0;
+  virtual void visit (GroupedPattern &pattern) = 0;
+  virtual void visit (SlicePattern &pattern) = 0;
+
+  // rust-stmt.h
+  virtual void visit (EmptyStmt &stmt) = 0;
+  virtual void visit (LetStmt &stmt) = 0;
+  virtual void visit (ExprStmtWithoutBlock &stmt) = 0;
+  virtual void visit (ExprStmtWithBlock &stmt) = 0;
+
+  // rust-type.h
+  virtual void visit (TraitBound &bound) = 0;
+  virtual void visit (ImplTraitType &type) = 0;
+  virtual void visit (TraitObjectType &type) = 0;
+  virtual void visit (ParenthesisedType &type) = 0;
+  virtual void visit (ImplTraitTypeOneBound &type) = 0;
+  virtual void visit (TraitObjectTypeOneBound &type) = 0;
+  virtual void visit (TupleType &type) = 0;
+  virtual void visit (NeverType &type) = 0;
+  virtual void visit (RawPointerType &type) = 0;
+  virtual void visit (ReferenceType &type) = 0;
+  virtual void visit (ArrayType &type) = 0;
+  virtual void visit (SliceType &type) = 0;
+  virtual void visit (InferredType &type) = 0;
+  virtual void visit (BareFunctionType &type) = 0;
+
+  // TODO: rust-cond-compilation.h visiting? not currently used
+};
+} // namespace AST
+} // namespace Rust
+
+#endif
diff --git a/gcc/rust/ast/rust-ast.h b/gcc/rust/ast/rust-ast.h
new file mode 100644
index 00000000000..461a2460f8f
--- /dev/null
+++ b/gcc/rust/ast/rust-ast.h
@@ -0,0 +1,2007 @@
+// 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_AST_BASE_H
+#define RUST_AST_BASE_H
+// Base for AST used in gccrs, basically required by all specific ast things
+
+#include "rust-system.h"
+#include "rust-hir-map.h"
+#include "rust-token.h"
+#include "rust-location.h"
+
+namespace Rust {
+// TODO: remove typedefs and make actual types for these
+typedef std::string Identifier;
+typedef int TupleIndex;
+struct Session;
+
+namespace AST {
+// foward decl: ast visitor
+class ASTVisitor;
+using AttrVec = std::vector<Attribute>;
+
+// The available kinds of AST Nodes
+enum Kind
+{
+  UNKNOWN,
+  MACRO_RULES_DEFINITION,
+  MACRO_INVOCATION,
+};
+
+// Abstract base class for all AST elements
+class Node
+{
+public:
+  /**
+   * Get the kind of Node this is. This is used to differentiate various AST
+   * elements with very little overhead when extracting the derived type through
+   * static casting is not necessary.
+   */
+  // FIXME: Mark this as `= 0` in the future to make sure every node implements
+  // it
+  virtual Kind get_ast_kind () const { return Kind::UNKNOWN; }
+};
+
+// Delimiter types - used in macros and whatever.
+enum DelimType
+{
+  PARENS,
+  SQUARE,
+  CURLY
+};
+
+// forward decl for use in token tree method
+class Token;
+
+// A tree of tokens (or a single token) - abstract base class
+class TokenTree
+{
+public:
+  virtual ~TokenTree () {}
+
+  // Unique pointer custom clone function
+  std::unique_ptr<TokenTree> clone_token_tree () const
+  {
+    return std::unique_ptr<TokenTree> (clone_token_tree_impl ());
+  }
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (ASTVisitor &vis) = 0;
+
+  /* Converts token tree to a flat token stream. Tokens must be pointer to avoid
+   * mutual dependency with Token. */
+  virtual std::vector<std::unique_ptr<Token> > to_token_stream () const = 0;
+
+protected:
+  // pure virtual clone implementation
+  virtual TokenTree *clone_token_tree_impl () const = 0;
+};
+
+// Abstract base class for a macro match
+class MacroMatch
+{
+public:
+  enum MacroMatchType
+  {
+    Fragment,
+    Repetition,
+    Matcher,
+    Tok
+  };
+
+  virtual ~MacroMatch () {}
+
+  virtual std::string as_string () const = 0;
+  virtual Location get_match_locus () const = 0;
+
+  // Unique pointer custom clone function
+  std::unique_ptr<MacroMatch> clone_macro_match () const
+  {
+    return std::unique_ptr<MacroMatch> (clone_macro_match_impl ());
+  }
+
+  virtual void accept_vis (ASTVisitor &vis) = 0;
+
+  virtual MacroMatchType get_macro_match_type () const = 0;
+
+protected:
+  // pure virtual clone implementation
+  virtual MacroMatch *clone_macro_match_impl () const = 0;
+};
+
+// A token is a kind of token tree (except delimiter tokens)
+class Token : public TokenTree, public MacroMatch
+{
+  // A token is a kind of token tree (except delimiter tokens)
+  // A token is a kind of MacroMatch (except $ and delimiter tokens)
+#if 0
+  // TODO: improve member variables - current ones are the same as lexer token
+  // Token kind.
+  TokenId token_id;
+  // Token location.
+  Location locus;
+  // Associated text (if any) of token.
+  std::string str;
+  // Token type hint (if any).
+  PrimitiveCoreType type_hint;
+#endif
+
+  const_TokenPtr tok_ref;
+
+  /* new idea: wrapper around const_TokenPtr used for heterogeneuous storage in
+   * token trees. rather than convert back and forth when parsing macros, just
+   * wrap it. */
+
+public:
+  // Unique pointer custom clone function
+  std::unique_ptr<Token> clone_token () const
+  {
+    return std::unique_ptr<Token> (clone_token_impl ());
+  }
+
+#if 0
+  /* constructor from general text - avoid using if lexer const_TokenPtr is
+   * available */
+  Token (TokenId token_id, Location locus, std::string str,
+	 PrimitiveCoreType type_hint)
+    : token_id (token_id), locus (locus), str (std::move (str)),
+      type_hint (type_hint)
+  {}
+#endif
+  // not doable with new implementation - will have to make a const_TokenPtr
+
+  // Constructor from lexer const_TokenPtr
+#if 0
+  /* TODO: find workaround for std::string being nullptr - probably have to
+   * introduce new method in lexer Token, or maybe make conversion method
+   * there */
+  Token (const_TokenPtr lexer_token_ptr)
+    : token_id (lexer_token_ptr->get_id ()),
+      locus (lexer_token_ptr->get_locus ()), str (""),
+      type_hint (lexer_token_ptr->get_type_hint ())
+  {
+    // FIXME: change to "should have str" later?
+    if (lexer_token_ptr->has_str ())
+      {
+	str = lexer_token_ptr->get_str ();
+
+	// DEBUG
+	rust_debug ("ast token created with str '%s'", str.c_str ());
+      }
+    else
+      {
+	// FIXME: is this returning correct thing?
+	str = lexer_token_ptr->get_token_description ();
+
+	// DEBUG
+	rust_debug ("ast token created with string '%s'", str.c_str ());
+      }
+
+    // DEBUG
+    if (lexer_token_ptr->should_have_str () && !lexer_token_ptr->has_str ())
+      {
+	rust_debug (
+		 "BAD: for token '%s', should have string but does not!",
+		 lexer_token_ptr->get_token_description ());
+      }
+  }
+#endif
+  Token (const_TokenPtr lexer_tok_ptr) : tok_ref (std::move (lexer_tok_ptr)) {}
+
+  bool is_string_lit () const
+  {
+    switch (get_id ())
+      {
+      case STRING_LITERAL:
+      case BYTE_STRING_LITERAL:
+	return true;
+      default:
+	return false;
+      }
+  }
+
+  std::string as_string () const override;
+  Location get_match_locus () const override { return tok_ref->get_locus (); };
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Return copy of itself but in token stream form.
+  std::vector<std::unique_ptr<Token> > to_token_stream () const override;
+
+  TokenId get_id () const { return tok_ref->get_id (); }
+  const std::string &get_str () const { return tok_ref->get_str (); }
+
+  Location get_locus () const { return tok_ref->get_locus (); }
+
+  PrimitiveCoreType get_type_hint () const { return tok_ref->get_type_hint (); }
+
+  // Get a new token pointer copy.
+  const_TokenPtr get_tok_ptr () const { return tok_ref; }
+
+  MacroMatchType get_macro_match_type () const override
+  {
+    return MacroMatchType::Tok;
+  }
+
+protected:
+  // No virtual for now as not polymorphic but can be in future
+  /*virtual*/ Token *clone_token_impl () const { return new Token (*this); }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  Token *clone_token_tree_impl () const final override
+  {
+    return clone_token_impl ();
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  Token *clone_macro_match_impl () const final override
+  {
+    return clone_token_impl ();
+  }
+};
+
+// A literal - value with a type. Used in LiteralExpr and LiteralPattern.
+struct Literal
+{
+public:
+  enum LitType
+  {
+    CHAR,
+    STRING,
+    BYTE,
+    BYTE_STRING,
+    INT,
+    FLOAT,
+    BOOL,
+    ERROR
+  };
+
+private:
+  /* TODO: maybe make subclasses of each type of literal with their typed values
+   * (or generics) */
+  std::string value_as_string;
+  LitType type;
+  PrimitiveCoreType type_hint;
+
+public:
+  std::string as_string () const { return value_as_string; }
+
+  LitType get_lit_type () const { return type; }
+
+  PrimitiveCoreType get_type_hint () const { return type_hint; }
+
+  Literal (std::string value_as_string, LitType type,
+	   PrimitiveCoreType type_hint)
+    : value_as_string (std::move (value_as_string)), type (type),
+      type_hint (type_hint)
+  {}
+
+  static Literal create_error ()
+  {
+    return Literal ("", ERROR, PrimitiveCoreType::CORETYPE_UNKNOWN);
+  }
+
+  // Returns whether literal is in an invalid state.
+  bool is_error () const { return type == ERROR; }
+};
+
+/* Forward decl - definition moved to rust-expr.h as it requires LiteralExpr to
+ * be defined */
+class AttrInputLiteral;
+
+/* TODO: move applicable stuff into here or just don't include it because
+ * nothing uses it A segment of a path (maybe) */
+class PathSegment
+{
+public:
+  virtual ~PathSegment () {}
+
+  virtual std::string as_string () const = 0;
+
+  // TODO: add visitor here?
+};
+
+// A segment of a simple path without generic or type arguments
+class SimplePathSegment : public PathSegment
+{
+  std::string segment_name;
+  Location locus;
+  NodeId node_id;
+
+  // only allow identifiers, "super", "self", "crate", or "$crate"
+public:
+  // TODO: put checks in constructor to enforce this rule?
+  SimplePathSegment (std::string segment_name, Location locus)
+    : segment_name (std::move (segment_name)), locus (locus),
+      node_id (Analysis::Mappings::get ()->get_next_node_id ())
+  {}
+
+  /* Returns whether simple path segment is in an invalid state (currently, if
+   * empty). */
+  bool is_error () const { return segment_name.empty (); }
+
+  // Creates an error SimplePathSegment
+  static SimplePathSegment create_error ()
+  {
+    return SimplePathSegment (std::string (""), Location ());
+  }
+
+  std::string as_string () const override;
+
+  Location get_locus () const { return locus; }
+  NodeId get_node_id () const { return node_id; }
+  const std::string &get_segment_name () const { return segment_name; }
+  bool is_super_path_seg () const
+  {
+    return as_string ().compare ("super") == 0;
+  }
+  bool is_crate_path_seg () const
+  {
+    return as_string ().compare ("crate") == 0;
+  }
+  bool is_lower_self () const { return as_string ().compare ("self") == 0; }
+  bool is_big_self () const { return as_string ().compare ("Self") == 0; }
+};
+
+// A simple path without generic or type arguments
+class SimplePath
+{
+  bool has_opening_scope_resolution;
+  std::vector<SimplePathSegment> segments;
+  Location locus;
+  NodeId node_id;
+
+public:
+  // Constructor
+  SimplePath (std::vector<SimplePathSegment> path_segments,
+	      bool has_opening_scope_resolution = false,
+	      Location locus = Location ())
+    : has_opening_scope_resolution (has_opening_scope_resolution),
+      segments (std::move (path_segments)), locus (locus),
+      node_id (Analysis::Mappings::get ()->get_next_node_id ())
+  {}
+
+  // Creates an empty SimplePath.
+  static SimplePath create_empty ()
+  {
+    return SimplePath (std::vector<SimplePathSegment> ());
+  }
+
+  // Returns whether the SimplePath is empty, i.e. has path segments.
+  bool is_empty () const { return segments.empty (); }
+
+  std::string as_string () const;
+
+  Location get_locus () const { return locus; }
+  NodeId get_node_id () const { return node_id; }
+
+  // does this need visitor if not polymorphic? probably not
+
+  // path-to-string comparison operator
+  bool operator== (const std::string &rhs) const
+  {
+    return !has_opening_scope_resolution && segments.size () == 1
+	   && segments[0].as_string () == rhs;
+  }
+
+  /* Creates a single-segment SimplePath from a string. This will not check to
+   * ensure that this is a valid identifier in path, so be careful. Also, this
+   * will have no location data.
+   * TODO have checks? */
+  static SimplePath from_str (std::string str, Location locus)
+  {
+    std::vector<AST::SimplePathSegment> single_segments
+      = {AST::SimplePathSegment (std::move (str), locus)};
+    return SimplePath (std::move (single_segments));
+  }
+
+  const std::vector<SimplePathSegment> &get_segments () const
+  {
+    return segments;
+  }
+
+  std::vector<SimplePathSegment> &get_segments () { return segments; }
+};
+
+// path-to-string inverse comparison operator
+inline bool
+operator!= (const SimplePath &lhs, const std::string &rhs)
+{
+  return !(lhs == rhs);
+}
+
+// forward decl for Attribute
+class AttrInput;
+
+// aka Attr
+// Attribute AST representation
+struct Attribute
+{
+private:
+  SimplePath path;
+
+  // bool has_attr_input;
+  std::unique_ptr<AttrInput> attr_input;
+
+  Location locus;
+
+  // TODO: maybe a variable storing whether attr input is parsed or not
+
+public:
+  // Returns whether Attribute has AttrInput
+  bool has_attr_input () const { return attr_input != nullptr; }
+
+  // Constructor has pointer AttrInput for polymorphism reasons
+  Attribute (SimplePath path, std::unique_ptr<AttrInput> input,
+	     Location locus = Location ())
+    : path (std::move (path)), attr_input (std::move (input)), locus (locus)
+  {}
+
+  // default destructor
+  ~Attribute () = default;
+
+  // no point in being defined inline as requires virtual call anyway
+  Attribute (const Attribute &other);
+
+  // no point in being defined inline as requires virtual call anyway
+  Attribute &operator= (const Attribute &other);
+
+  // default move semantics
+  Attribute (Attribute &&other) = default;
+  Attribute &operator= (Attribute &&other) = default;
+
+  // Unique pointer custom clone function
+  std::unique_ptr<Attribute> clone_attribute () const
+  {
+    return std::unique_ptr<Attribute> (clone_attribute_impl ());
+  }
+
+  // Creates an empty attribute (which is invalid)
+  static Attribute create_empty ()
+  {
+    return Attribute (SimplePath::create_empty (), nullptr);
+  }
+
+  // Returns whether the attribute is considered an "empty" attribute.
+  bool is_empty () const { return attr_input == nullptr && path.is_empty (); }
+
+  Location get_locus () const { return locus; }
+
+  AttrInput &get_attr_input () const { return *attr_input; }
+
+  /* e.g.:
+      #![crate_type = "lib"]
+      #[test]
+      #[cfg(target_os = "linux")]
+      #[allow(non_camel_case_types)]
+      #![allow(unused_variables)]
+  */
+
+  // Full built-in attribute list:
+  /*   cfg
+   *   cfg_attr
+   *   test
+   *   ignore
+   *   should_panic
+   *   derive
+   *   macro_export
+   *   macro_use
+   *   proc_macro
+   *   proc_macro_derive
+   *   proc_macro_attribute
+   *   allow
+   *   warn
+   *   deny
+   *   forbid
+   *   deprecated
+   *   must_use
+   *   link
+   *   link_name
+   *   no_link
+   *   repr
+   *   crate_type
+   *   no_main
+   *   export_name
+   *   link_section
+   *   no_mangle
+   *   used
+   *   crate_name
+   *   inline
+   *   cold
+   *   no_builtins
+   *   target_feature
+   *   doc
+   *   no_std
+   *   no_implicit_prelude
+   *   path
+   *   recursion_limit
+   *   type_length_limit
+   *   panic_handler
+   *   global_allocator
+   *   windows_subsystem
+   *   feature     */
+
+  std::string as_string () const;
+
+  // no visitor pattern as not currently polymorphic
+
+  const SimplePath &get_path () const { return path; }
+  SimplePath &get_path () { return path; }
+
+  // Call to parse attribute body to meta item syntax.
+  void parse_attr_to_meta_item ();
+
+  /* Determines whether cfg predicate is true and item with attribute should not
+   * be stripped. Attribute body must already be parsed to meta item. */
+  bool check_cfg_predicate (const Session &session) const;
+
+  // Returns whether body has been parsed to meta item form or not.
+  bool is_parsed_to_meta_item () const;
+
+  /* Returns any attributes generated from cfg_attr attributes. Attribute body
+   * must already be parsed to meta item. */
+  std::vector<Attribute> separate_cfg_attrs () const;
+
+protected:
+  // not virtual as currently no subclasses of Attribute, but could be in future
+  /*virtual*/ Attribute *clone_attribute_impl () const
+  {
+    return new Attribute (*this);
+  }
+};
+
+// Attribute body - abstract base class
+class AttrInput
+{
+public:
+  enum AttrInputType
+  {
+    LITERAL,
+    META_ITEM,
+    TOKEN_TREE,
+  };
+
+  virtual ~AttrInput () {}
+
+  // Unique pointer custom clone function
+  std::unique_ptr<AttrInput> clone_attr_input () const
+  {
+    return std::unique_ptr<AttrInput> (clone_attr_input_impl ());
+  }
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (ASTVisitor &vis) = 0;
+
+  virtual bool check_cfg_predicate (const Session &session) const = 0;
+
+  // Parse attribute input to meta item, if possible
+  virtual AttrInput *parse_to_meta_item () const { return nullptr; }
+
+  virtual std::vector<Attribute> separate_cfg_attrs () const { return {}; }
+
+  // Returns whether attr input has been parsed to meta item syntax.
+  virtual bool is_meta_item () const = 0;
+
+  virtual AttrInputType get_attr_input_type () const = 0;
+
+protected:
+  // pure virtual clone implementation
+  virtual AttrInput *clone_attr_input_impl () const = 0;
+};
+
+// Forward decl - defined in rust-macro.h
+class MetaNameValueStr;
+
+// abstract base meta item inner class
+class MetaItemInner
+{
+protected:
+  // pure virtual as MetaItemInner
+  virtual MetaItemInner *clone_meta_item_inner_impl () const = 0;
+
+public:
+  // Unique pointer custom clone function
+  std::unique_ptr<MetaItemInner> clone_meta_item_inner () const
+  {
+    return std::unique_ptr<MetaItemInner> (clone_meta_item_inner_impl ());
+  }
+
+  virtual ~MetaItemInner ();
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (ASTVisitor &vis) = 0;
+
+  /* HACK: used to simplify parsing - creates a copy of that type, or returns
+   * null */
+  virtual std::unique_ptr<MetaNameValueStr> to_meta_name_value_str () const;
+
+  // HACK: used to simplify parsing - same thing
+  virtual SimplePath to_path_item () const
+  {
+    return SimplePath::create_empty ();
+  }
+
+  virtual Attribute to_attribute () const { return Attribute::create_empty (); }
+
+  virtual bool check_cfg_predicate (const Session &session) const = 0;
+
+  virtual bool is_key_value_pair () const { return false; }
+};
+
+// Container used to store MetaItems as AttrInput (bridge-ish kinda thing)
+class AttrInputMetaItemContainer : public AttrInput
+{
+  std::vector<std::unique_ptr<MetaItemInner> > items;
+
+public:
+  AttrInputMetaItemContainer (
+    std::vector<std::unique_ptr<MetaItemInner> > items)
+    : items (std::move (items))
+  {}
+
+  // copy constructor with vector clone
+  AttrInputMetaItemContainer (const AttrInputMetaItemContainer &other)
+  {
+    items.reserve (other.items.size ());
+    for (const auto &e : other.items)
+      items.push_back (e->clone_meta_item_inner ());
+  }
+
+  // copy assignment operator with vector clone
+  AttrInputMetaItemContainer &
+  operator= (const AttrInputMetaItemContainer &other)
+  {
+    AttrInput::operator= (other);
+
+    items.reserve (other.items.size ());
+    for (const auto &e : other.items)
+      items.push_back (e->clone_meta_item_inner ());
+
+    return *this;
+  }
+
+  // default move constructors
+  AttrInputMetaItemContainer (AttrInputMetaItemContainer &&other) = default;
+  AttrInputMetaItemContainer &operator= (AttrInputMetaItemContainer &&other)
+    = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  bool check_cfg_predicate (const Session &session) const override;
+
+  AttrInputType get_attr_input_type () const final override
+  {
+    return AttrInput::AttrInputType::META_ITEM;
+  }
+
+  // Clones this object.
+  std::unique_ptr<AttrInputMetaItemContainer>
+  clone_attr_input_meta_item_container () const
+  {
+    return std::unique_ptr<AttrInputMetaItemContainer> (
+      clone_attr_input_meta_item_container_impl ());
+  }
+
+  std::vector<Attribute> separate_cfg_attrs () const override;
+
+  bool is_meta_item () const override { return true; }
+
+  // TODO: this mutable getter seems dodgy
+  std::vector<std::unique_ptr<MetaItemInner> > &get_items () { return items; }
+  const std::vector<std::unique_ptr<MetaItemInner> > &get_items () const
+  {
+    return items;
+  }
+
+protected:
+  // Use covariance to implement clone function as returning this type
+  AttrInputMetaItemContainer *clone_attr_input_impl () const final override
+  {
+    return clone_attr_input_meta_item_container_impl ();
+  }
+
+  AttrInputMetaItemContainer *clone_attr_input_meta_item_container_impl () const
+  {
+    return new AttrInputMetaItemContainer (*this);
+  }
+};
+
+// A token tree with delimiters
+class DelimTokenTree : public TokenTree, public AttrInput
+{
+  DelimType delim_type;
+  std::vector<std::unique_ptr<TokenTree> > token_trees;
+  Location locus;
+
+protected:
+  DelimTokenTree *clone_delim_tok_tree_impl () const
+  {
+    return new DelimTokenTree (*this);
+  }
+
+  /* Use covariance to implement clone function as returning a DelimTokenTree
+   * object */
+  DelimTokenTree *clone_attr_input_impl () const final override
+  {
+    return clone_delim_tok_tree_impl ();
+  }
+
+  /* Use covariance to implement clone function as returning a DelimTokenTree
+   * object */
+  DelimTokenTree *clone_token_tree_impl () const final override
+  {
+    return clone_delim_tok_tree_impl ();
+  }
+
+public:
+  DelimTokenTree (DelimType delim_type,
+		  std::vector<std::unique_ptr<TokenTree> > token_trees
+		  = std::vector<std::unique_ptr<TokenTree> > (),
+		  Location locus = Location ())
+    : delim_type (delim_type), token_trees (std::move (token_trees)),
+      locus (locus)
+  {}
+
+  // Copy constructor with vector clone
+  DelimTokenTree (DelimTokenTree const &other)
+    : delim_type (other.delim_type), locus (other.locus)
+  {
+    token_trees.reserve (other.token_trees.size ());
+    for (const auto &e : other.token_trees)
+      token_trees.push_back (e->clone_token_tree ());
+  }
+
+  // overloaded assignment operator with vector clone
+  DelimTokenTree &operator= (DelimTokenTree const &other)
+  {
+    delim_type = other.delim_type;
+    locus = other.locus;
+
+    token_trees.reserve (other.token_trees.size ());
+    for (const auto &e : other.token_trees)
+      token_trees.push_back (e->clone_token_tree ());
+
+    return *this;
+  }
+
+  // move constructors
+  DelimTokenTree (DelimTokenTree &&other) = default;
+  DelimTokenTree &operator= (DelimTokenTree &&other) = default;
+
+  static DelimTokenTree create_empty () { return DelimTokenTree (PARENS); }
+
+  std::string as_string () const override;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  bool check_cfg_predicate (const Session &) const override
+  {
+    // this should never be called - should be converted first
+    rust_assert (false);
+    return false;
+  }
+
+  AttrInputMetaItemContainer *parse_to_meta_item () const override;
+
+  std::vector<std::unique_ptr<Token> > to_token_stream () const override;
+
+  std::unique_ptr<DelimTokenTree> clone_delim_token_tree () const
+  {
+    return std::unique_ptr<DelimTokenTree> (clone_delim_tok_tree_impl ());
+  }
+
+  bool is_meta_item () const override { return false; }
+
+  AttrInputType get_attr_input_type () const final override
+  {
+    return AttrInput::AttrInputType::TOKEN_TREE;
+  }
+
+  std::vector<std::unique_ptr<TokenTree> > &get_token_trees ()
+  {
+    return token_trees;
+  }
+
+  DelimType get_delim_type () const { return delim_type; }
+};
+
+/* Forward decl - definition moved to rust-expr.h as it requires LiteralExpr to
+ * be defined */
+class AttrInputLiteral;
+
+// abstract base meta item class
+class MetaItem : public MetaItemInner
+{
+};
+
+// Forward decl - defined in rust-expr.h
+class MetaItemLitExpr;
+
+// Forward decl - defined in rust-expr.h
+class MetaItemPathLit;
+
+// Forward decl - defined in rust-macro.h
+class MetaItemPath;
+
+// Forward decl - defined in rust-macro.h
+class MetaItemSeq;
+
+// Forward decl - defined in rust-macro.h
+class MetaWord;
+
+// Forward decl - defined in rust-macro.h
+class MetaListPaths;
+
+// Forward decl - defined in rust-macro.h
+class MetaListNameValueStr;
+
+/* Base statement abstract class. Note that most "statements" are not allowed in
+ * top-level module scope - only a subclass of statements called "items" are. */
+class Stmt : public Node
+{
+public:
+  // Unique pointer custom clone function
+  std::unique_ptr<Stmt> clone_stmt () const
+  {
+    return std::unique_ptr<Stmt> (clone_stmt_impl ());
+  }
+
+  virtual ~Stmt () {}
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (ASTVisitor &vis) = 0;
+
+  virtual Location get_locus () const = 0;
+
+  virtual void mark_for_strip () = 0;
+  virtual bool is_marked_for_strip () const = 0;
+  NodeId get_node_id () const { return node_id; }
+
+  virtual bool is_item () const = 0;
+
+protected:
+  Stmt () : node_id (Analysis::Mappings::get ()->get_next_node_id ()) {}
+
+  // Clone function implementation as pure virtual method
+  virtual Stmt *clone_stmt_impl () const = 0;
+
+  NodeId node_id;
+};
+
+// Rust "item" AST node (declaration of top-level/module-level allowed stuff)
+class Item : public Stmt
+{
+public:
+  // Unique pointer custom clone function
+  std::unique_ptr<Item> clone_item () const
+  {
+    return std::unique_ptr<Item> (clone_item_impl ());
+  }
+
+  /* Adds crate names to the vector passed by reference, if it can
+   * (polymorphism). TODO: remove, unused. */
+  virtual void
+  add_crate_name (std::vector<std::string> &names ATTRIBUTE_UNUSED) const
+  {}
+
+  // FIXME: ARTHUR: Is it okay to have removed that final? Is it *required*
+  // behavior that we have items that can also be expressions?
+  bool is_item () const override { return true; }
+
+protected:
+  // Clone function implementation as pure virtual method
+  virtual Item *clone_item_impl () const = 0;
+
+  /* Save having to specify two clone methods in derived classes by making
+   * statement clone return item clone. Hopefully won't affect performance too
+   * much. */
+  Item *clone_stmt_impl () const final override { return clone_item_impl (); }
+};
+
+// forward decl of ExprWithoutBlock
+class ExprWithoutBlock;
+
+// Base expression AST node - abstract
+class Expr : public Node
+{
+public:
+  // Unique pointer custom clone function
+  std::unique_ptr<Expr> clone_expr () const
+  {
+    return std::unique_ptr<Expr> (clone_expr_impl ());
+  }
+
+  /* TODO: public methods that could be useful:
+   *  - get_type() - returns type of expression. set_type() may also be useful
+   * for some?
+   *  - evaluate() - evaluates expression if constant? can_evaluate()? */
+
+  /* HACK: downcasting without dynamic_cast (if possible) via polymorphism -
+   * overrided in subclasses of ExprWithoutBlock */
+  virtual ExprWithoutBlock *as_expr_without_block () const { return nullptr; }
+
+  virtual std::string as_string () const = 0;
+
+  virtual ~Expr () {}
+
+  virtual Location get_locus () const = 0;
+
+  // HACK: strictly not needed, but faster than full downcast clone
+  virtual bool is_expr_without_block () const = 0;
+
+  virtual void accept_vis (ASTVisitor &vis) = 0;
+
+  virtual void mark_for_strip () = 0;
+  virtual bool is_marked_for_strip () const = 0;
+
+  virtual NodeId get_node_id () const { return node_id; }
+
+  virtual void set_node_id (NodeId id) { node_id = id; }
+
+protected:
+  // Constructor
+  Expr () : node_id (Analysis::Mappings::get ()->get_next_node_id ()) {}
+
+  // Clone function implementation as pure virtual method
+  virtual Expr *clone_expr_impl () const = 0;
+
+  // TODO: think of less hacky way to implement this kind of thing
+  // Sets outer attributes.
+  virtual void set_outer_attrs (std::vector<Attribute>) = 0;
+
+  NodeId node_id;
+};
+
+// AST node for an expression without an accompanying block - abstract
+class ExprWithoutBlock : public Expr
+{
+protected:
+  // pure virtual clone implementation
+  virtual ExprWithoutBlock *clone_expr_without_block_impl () const = 0;
+
+  /* Save having to specify two clone methods in derived classes by making expr
+   * clone return exprwithoutblock clone. Hopefully won't affect performance too
+   * much. */
+  ExprWithoutBlock *clone_expr_impl () const final override
+  {
+    return clone_expr_without_block_impl ();
+  }
+
+  bool is_expr_without_block () const final override { return true; };
+
+public:
+  // Unique pointer custom clone function
+  std::unique_ptr<ExprWithoutBlock> clone_expr_without_block () const
+  {
+    return std::unique_ptr<ExprWithoutBlock> (clone_expr_without_block_impl ());
+  }
+
+  /* downcasting hack from expr to use pratt parsing with
+   * parse_expr_without_block */
+  ExprWithoutBlock *as_expr_without_block () const final override
+  {
+    return clone_expr_without_block_impl ();
+  }
+
+  virtual ExprWithoutBlock *to_stmt () const { return clone_expr_impl (); }
+};
+
+/* HACK: IdentifierExpr, delete when figure out identifier vs expr problem in
+ * Pratt parser */
+/* Alternatively, identifiers could just be represented as single-segment paths
+ */
+class IdentifierExpr : public ExprWithoutBlock
+{
+  std::vector<Attribute> outer_attrs;
+  Identifier ident;
+  Location locus;
+
+public:
+  IdentifierExpr (Identifier ident, std::vector<Attribute> outer_attrs,
+		  Location locus)
+    : outer_attrs (std::move (outer_attrs)), ident (std::move (ident)),
+      locus (locus)
+  {}
+
+  std::string as_string () const override { return ident; }
+
+  Location get_locus () const override final { return locus; }
+
+  Identifier get_ident () const { return ident; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Clones this object.
+  std::unique_ptr<IdentifierExpr> clone_identifier_expr () const
+  {
+    return std::unique_ptr<IdentifierExpr> (clone_identifier_expr_impl ());
+  }
+
+  // "Error state" if ident is empty, so base stripping on this.
+  void mark_for_strip () override { ident = {}; }
+  bool is_marked_for_strip () const override { return ident.empty (); }
+
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+  void set_outer_attrs (std::vector<Attribute> new_attrs) override
+  {
+    outer_attrs = std::move (new_attrs);
+  }
+
+protected:
+  // Clone method implementation
+  IdentifierExpr *clone_expr_without_block_impl () const final override
+  {
+    return clone_identifier_expr_impl ();
+  }
+
+  IdentifierExpr *clone_identifier_expr_impl () const
+  {
+    return new IdentifierExpr (*this);
+  }
+};
+
+// Pattern base AST node
+class Pattern
+{
+public:
+  // Unique pointer custom clone function
+  std::unique_ptr<Pattern> clone_pattern () const
+  {
+    return std::unique_ptr<Pattern> (clone_pattern_impl ());
+  }
+
+  // possible virtual methods: is_refutable()
+
+  virtual ~Pattern () {}
+
+  virtual std::string as_string () const = 0;
+  virtual void accept_vis (ASTVisitor &vis) = 0;
+
+  // as only one kind of pattern can be stripped, have default of nothing
+  virtual void mark_for_strip () {}
+  virtual bool is_marked_for_strip () const { return false; }
+
+  virtual Location get_locus () const = 0;
+  virtual NodeId get_pattern_node_id () const = 0;
+
+protected:
+  // Clone pattern implementation as pure virtual method
+  virtual Pattern *clone_pattern_impl () const = 0;
+};
+
+// forward decl for Type
+class TraitBound;
+
+// Base class for types as represented in AST - abstract
+class Type : public Node
+{
+public:
+  // Unique pointer custom clone function
+  std::unique_ptr<Type> clone_type () const
+  {
+    return std::unique_ptr<Type> (clone_type_impl ());
+  }
+
+  // virtual destructor
+  virtual ~Type () {}
+
+  virtual std::string as_string () const = 0;
+
+  /* HACK: convert to trait bound. Virtual method overriden by classes that
+   * enable this. */
+  virtual TraitBound *to_trait_bound (bool) const { return nullptr; }
+  /* as pointer, shouldn't require definition beforehand, only forward
+   * declaration. */
+
+  virtual void accept_vis (ASTVisitor &vis) = 0;
+
+  // as only two kinds of types can be stripped, have default of nothing
+  virtual void mark_for_strip () {}
+  virtual bool is_marked_for_strip () const { return false; }
+
+  virtual Location get_locus () const = 0;
+
+  NodeId get_node_id () const { return node_id; }
+
+protected:
+  Type () : node_id (Analysis::Mappings::get ()->get_next_node_id ()) {}
+
+  // Clone function implementation as pure virtual method
+  virtual Type *clone_type_impl () const = 0;
+
+  NodeId node_id;
+};
+
+// A type without parentheses? - abstract
+class TypeNoBounds : public Type
+{
+public:
+  // Unique pointer custom clone function
+  std::unique_ptr<TypeNoBounds> clone_type_no_bounds () const
+  {
+    return std::unique_ptr<TypeNoBounds> (clone_type_no_bounds_impl ());
+  }
+
+protected:
+  // Clone function implementation as pure virtual method
+  virtual TypeNoBounds *clone_type_no_bounds_impl () const = 0;
+
+  /* Save having to specify two clone methods in derived classes by making type
+   * clone return typenobounds clone. Hopefully won't affect performance too
+   * much. */
+  TypeNoBounds *clone_type_impl () const final override
+  {
+    return clone_type_no_bounds_impl ();
+  }
+
+  TypeNoBounds () : Type () {}
+};
+
+/* Abstract base class representing a type param bound - Lifetime and TraitBound
+ * extends it */
+class TypeParamBound
+{
+public:
+  virtual ~TypeParamBound () {}
+
+  // Unique pointer custom clone function
+  std::unique_ptr<TypeParamBound> clone_type_param_bound () const
+  {
+    return std::unique_ptr<TypeParamBound> (clone_type_param_bound_impl ());
+  }
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (ASTVisitor &vis) = 0;
+
+  NodeId get_node_id () const { return node_id; }
+
+  virtual Location get_locus () const = 0;
+
+protected:
+  // Clone function implementation as pure virtual method
+  virtual TypeParamBound *clone_type_param_bound_impl () const = 0;
+
+  TypeParamBound (NodeId node_id) : node_id (node_id) {}
+
+  NodeId node_id;
+};
+
+// Represents a lifetime (and is also a kind of type param bound)
+class Lifetime : public TypeParamBound
+{
+public:
+  enum LifetimeType
+  {
+    NAMED,   // corresponds to LIFETIME_OR_LABEL
+    STATIC,  // corresponds to 'static
+    WILDCARD // corresponds to '_
+  };
+
+private:
+  LifetimeType lifetime_type;
+  std::string lifetime_name;
+  Location locus;
+  NodeId node_id;
+
+public:
+  // Constructor
+  Lifetime (LifetimeType type, std::string name = std::string (),
+	    Location locus = Location ())
+    : TypeParamBound (Analysis::Mappings::get ()->get_next_node_id ()),
+      lifetime_type (type), lifetime_name (std::move (name)), locus (locus)
+  {}
+
+  Lifetime (NodeId id, LifetimeType type, std::string name = std::string (),
+	    Location locus = Location ())
+    : TypeParamBound (id), lifetime_type (type),
+      lifetime_name (std::move (name)), locus (locus)
+  {}
+
+  // Creates an "error" lifetime.
+  static Lifetime error () { return Lifetime (NAMED, ""); }
+
+  // Returns true if the lifetime is in an error state.
+  bool is_error () const
+  {
+    return lifetime_type == NAMED && lifetime_name.empty ();
+  }
+
+  std::string as_string () const override;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  LifetimeType get_lifetime_type () { return lifetime_type; }
+
+  Location get_locus () const override final { return locus; }
+
+  std::string get_lifetime_name () const { return lifetime_name; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  Lifetime *clone_type_param_bound_impl () const override
+  {
+    return new Lifetime (node_id, lifetime_type, lifetime_name, locus);
+  }
+};
+
+/* Base generic parameter in AST. Abstract - can be represented by a Lifetime or
+ * Type param */
+class GenericParam
+{
+public:
+  enum class Kind
+  {
+    Lifetime,
+    Type,
+    Const,
+  };
+
+  virtual ~GenericParam () {}
+
+  // Unique pointer custom clone function
+  std::unique_ptr<GenericParam> clone_generic_param () const
+  {
+    return std::unique_ptr<GenericParam> (clone_generic_param_impl ());
+  }
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (ASTVisitor &vis) = 0;
+
+  virtual Location get_locus () const = 0;
+
+  virtual Kind get_kind () const = 0;
+
+  NodeId get_node_id () { return node_id; }
+
+protected:
+  GenericParam () : node_id (Analysis::Mappings::get ()->get_next_node_id ()) {}
+  GenericParam (NodeId node_id) : node_id (node_id) {}
+
+  // Clone function implementation as pure virtual method
+  virtual GenericParam *clone_generic_param_impl () const = 0;
+
+  NodeId node_id;
+};
+
+// A lifetime generic parameter (as opposed to a type generic parameter)
+class LifetimeParam : public GenericParam
+{
+  Lifetime lifetime;
+  std::vector<Lifetime> lifetime_bounds;
+  Attribute outer_attr;
+  Location locus;
+
+public:
+  Lifetime get_lifetime () const { return lifetime; }
+
+  // Returns whether the lifetime param has any lifetime bounds.
+  bool has_lifetime_bounds () const { return !lifetime_bounds.empty (); }
+
+  // Returns whether the lifetime param has an outer attribute.
+  bool has_outer_attribute () const { return !outer_attr.is_empty (); }
+
+  // Creates an error state lifetime param.
+  static LifetimeParam create_error ()
+  {
+    return LifetimeParam (Lifetime::error (), {}, Attribute::create_empty (),
+			  Location ());
+  }
+
+  // Returns whether the lifetime param is in an error state.
+  bool is_error () const { return lifetime.is_error (); }
+
+  // Constructor
+  LifetimeParam (Lifetime lifetime, std::vector<Lifetime> lifetime_bounds,
+		 Attribute outer_attr, Location locus)
+    : lifetime (std::move (lifetime)),
+      lifetime_bounds (std::move (lifetime_bounds)),
+      outer_attr (std::move (outer_attr)), locus (locus)
+  {}
+
+  std::string as_string () const override;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  Location get_locus () const override final { return locus; }
+
+  Kind get_kind () const override final { return Kind::Lifetime; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  LifetimeParam *clone_generic_param_impl () const override
+  {
+    return new LifetimeParam (*this);
+  }
+};
+
+// A macro item AST node - abstract base class
+class MacroItem : public Item
+{
+};
+
+// Item used in trait declarations - abstract base class
+class TraitItem
+{
+protected:
+  TraitItem () : node_id (Analysis::Mappings::get ()->get_next_node_id ()) {}
+
+  // Clone function implementation as pure virtual method
+  virtual TraitItem *clone_trait_item_impl () const = 0;
+
+  NodeId node_id;
+
+public:
+  virtual ~TraitItem () {}
+
+  // Unique pointer custom clone function
+  std::unique_ptr<TraitItem> clone_trait_item () const
+  {
+    return std::unique_ptr<TraitItem> (clone_trait_item_impl ());
+  }
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (ASTVisitor &vis) = 0;
+
+  virtual void mark_for_strip () = 0;
+  virtual bool is_marked_for_strip () const = 0;
+
+  NodeId get_node_id () const { return node_id; }
+};
+
+/* Abstract base class for items used within an inherent impl block (the impl
+ * name {} one) */
+class InherentImplItem
+{
+protected:
+  // Clone function implementation as pure virtual method
+  virtual InherentImplItem *clone_inherent_impl_item_impl () const = 0;
+
+public:
+  virtual ~InherentImplItem () {}
+
+  // Unique pointer custom clone function
+  std::unique_ptr<InherentImplItem> clone_inherent_impl_item () const
+  {
+    return std::unique_ptr<InherentImplItem> (clone_inherent_impl_item_impl ());
+  }
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (ASTVisitor &vis) = 0;
+
+  virtual void mark_for_strip () = 0;
+  virtual bool is_marked_for_strip () const = 0;
+
+  virtual Location get_locus () const = 0;
+};
+
+// Abstract base class for items used in a trait impl
+class TraitImplItem
+{
+protected:
+  virtual TraitImplItem *clone_trait_impl_item_impl () const = 0;
+
+public:
+  virtual ~TraitImplItem (){};
+
+  // Unique pointer custom clone function
+  std::unique_ptr<TraitImplItem> clone_trait_impl_item () const
+  {
+    return std::unique_ptr<TraitImplItem> (clone_trait_impl_item_impl ());
+  }
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (ASTVisitor &vis) = 0;
+
+  virtual void mark_for_strip () = 0;
+  virtual bool is_marked_for_strip () const = 0;
+};
+
+// Abstract base class for an item used inside an extern block
+class ExternalItem
+{
+public:
+  ExternalItem () : node_id (Analysis::Mappings::get ()->get_next_node_id ()) {}
+
+  virtual ~ExternalItem () {}
+
+  // Unique pointer custom clone function
+  std::unique_ptr<ExternalItem> clone_external_item () const
+  {
+    return std::unique_ptr<ExternalItem> (clone_external_item_impl ());
+  }
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (ASTVisitor &vis) = 0;
+
+  virtual void mark_for_strip () = 0;
+  virtual bool is_marked_for_strip () const = 0;
+
+  NodeId get_node_id () const { return node_id; }
+
+protected:
+  // Clone function implementation as pure virtual method
+  virtual ExternalItem *clone_external_item_impl () const = 0;
+
+  NodeId node_id;
+};
+
+/* Data structure to store the data used in macro invocations and macro
+ * invocations with semicolons. */
+struct MacroInvocData
+{
+private:
+  SimplePath path;
+  DelimTokenTree token_tree;
+
+  // One way of parsing the macro. Probably not applicable for all macros.
+  std::vector<std::unique_ptr<MetaItemInner> > parsed_items;
+  bool parsed_to_meta_item = false;
+
+public:
+  std::string as_string () const;
+
+  MacroInvocData (SimplePath path, DelimTokenTree token_tree)
+    : path (std::move (path)), token_tree (std::move (token_tree))
+  {}
+
+  // Copy constructor with vector clone
+  MacroInvocData (const MacroInvocData &other)
+    : path (other.path), token_tree (other.token_tree),
+      parsed_to_meta_item (other.parsed_to_meta_item)
+  {
+    parsed_items.reserve (other.parsed_items.size ());
+    for (const auto &e : other.parsed_items)
+      parsed_items.push_back (e->clone_meta_item_inner ());
+  }
+
+  // Copy assignment operator with vector clone
+  MacroInvocData &operator= (const MacroInvocData &other)
+  {
+    path = other.path;
+    token_tree = other.token_tree;
+    parsed_to_meta_item = other.parsed_to_meta_item;
+
+    parsed_items.reserve (other.parsed_items.size ());
+    for (const auto &e : other.parsed_items)
+      parsed_items.push_back (e->clone_meta_item_inner ());
+
+    return *this;
+  }
+
+  // Move constructors
+  MacroInvocData (MacroInvocData &&other) = default;
+  MacroInvocData &operator= (MacroInvocData &&other) = default;
+
+  // Invalid if path is empty, so base stripping on that.
+  void mark_for_strip () { path = SimplePath::create_empty (); }
+  bool is_marked_for_strip () const { return path.is_empty (); }
+
+  // Returns whether the macro has been parsed already.
+  bool is_parsed () const { return parsed_to_meta_item; }
+  // TODO: update on other ways of parsing it
+
+  // TODO: this mutable getter seems kinda dodgy
+  DelimTokenTree &get_delim_tok_tree () { return token_tree; }
+  const DelimTokenTree &get_delim_tok_tree () const { return token_tree; }
+
+  // TODO: this mutable getter seems kinda dodgy
+  SimplePath &get_path () { return path; }
+  const SimplePath &get_path () const { return path; }
+
+  void
+  set_meta_item_output (std::vector<std::unique_ptr<MetaItemInner> > new_items)
+  {
+    parsed_items = std::move (new_items);
+  }
+  // TODO: mutable getter seems kinda dodgy
+  std::vector<std::unique_ptr<MetaItemInner> > &get_meta_items ()
+  {
+    return parsed_items;
+  }
+  const std::vector<std::unique_ptr<MetaItemInner> > &get_meta_items () const
+  {
+    return parsed_items;
+  }
+};
+
+class SingleASTNode
+{
+public:
+  enum NodeType
+  {
+    EXPRESSION,
+    ITEM,
+    STMT,
+    EXTERN,
+    TRAIT,
+    IMPL,
+    TRAIT_IMPL,
+    TYPE,
+  };
+
+private:
+  NodeType kind;
+
+  // FIXME make this a union
+  std::unique_ptr<Expr> expr;
+  std::unique_ptr<Item> item;
+  std::unique_ptr<Stmt> stmt;
+  std::unique_ptr<ExternalItem> external_item;
+  std::unique_ptr<TraitItem> trait_item;
+  std::unique_ptr<InherentImplItem> impl_item;
+  std::unique_ptr<TraitImplItem> trait_impl_item;
+  std::unique_ptr<Type> type;
+
+public:
+  SingleASTNode (std::unique_ptr<Expr> expr)
+    : kind (EXPRESSION), expr (std::move (expr))
+  {}
+
+  SingleASTNode (std::unique_ptr<Item> item)
+    : kind (ITEM), item (std::move (item))
+  {}
+
+  SingleASTNode (std::unique_ptr<Stmt> stmt)
+    : kind (STMT), stmt (std::move (stmt))
+  {}
+
+  SingleASTNode (std::unique_ptr<ExternalItem> item)
+    : kind (EXTERN), external_item (std::move (item))
+  {}
+
+  SingleASTNode (std::unique_ptr<TraitItem> item)
+    : kind (TRAIT), trait_item (std::move (item))
+  {}
+
+  SingleASTNode (std::unique_ptr<InherentImplItem> item)
+    : kind (IMPL), impl_item (std::move (item))
+  {}
+
+  SingleASTNode (std::unique_ptr<TraitImplItem> trait_impl_item)
+    : kind (TRAIT_IMPL), trait_impl_item (std::move (trait_impl_item))
+  {}
+
+  SingleASTNode (std::unique_ptr<Type> type)
+    : kind (TYPE), type (std::move (type))
+  {}
+
+  SingleASTNode (SingleASTNode const &other)
+  {
+    kind = other.kind;
+    switch (kind)
+      {
+      case EXPRESSION:
+	expr = other.expr->clone_expr ();
+	break;
+
+      case ITEM:
+	item = other.item->clone_item ();
+	break;
+
+      case STMT:
+	stmt = other.stmt->clone_stmt ();
+	break;
+
+      case EXTERN:
+	external_item = other.external_item->clone_external_item ();
+	break;
+
+      case TRAIT:
+	trait_item = other.trait_item->clone_trait_item ();
+	break;
+
+      case IMPL:
+	impl_item = other.impl_item->clone_inherent_impl_item ();
+	break;
+
+      case TRAIT_IMPL:
+	trait_impl_item = other.trait_impl_item->clone_trait_impl_item ();
+	break;
+
+      case TYPE:
+	type = other.type->clone_type ();
+	break;
+      }
+  }
+
+  SingleASTNode operator= (SingleASTNode const &other)
+  {
+    kind = other.kind;
+    switch (kind)
+      {
+      case EXPRESSION:
+	expr = other.expr->clone_expr ();
+	break;
+
+      case ITEM:
+	item = other.item->clone_item ();
+	break;
+
+      case STMT:
+	stmt = other.stmt->clone_stmt ();
+	break;
+
+      case EXTERN:
+	external_item = other.external_item->clone_external_item ();
+	break;
+
+      case TRAIT:
+	trait_item = other.trait_item->clone_trait_item ();
+	break;
+
+      case IMPL:
+	impl_item = other.impl_item->clone_inherent_impl_item ();
+	break;
+
+      case TRAIT_IMPL:
+	trait_impl_item = other.trait_impl_item->clone_trait_impl_item ();
+	break;
+
+      case TYPE:
+	type = other.type->clone_type ();
+	break;
+      }
+    return *this;
+  }
+
+  SingleASTNode (SingleASTNode &&other) = default;
+  SingleASTNode &operator= (SingleASTNode &&other) = default;
+
+  NodeType get_kind () const { return kind; }
+
+  std::unique_ptr<Expr> &get_expr ()
+  {
+    rust_assert (kind == EXPRESSION);
+    return expr;
+  }
+
+  std::unique_ptr<Item> &get_item ()
+  {
+    rust_assert (kind == ITEM);
+    return item;
+  }
+
+  std::unique_ptr<Stmt> &get_stmt ()
+  {
+    rust_assert (kind == STMT);
+    return stmt;
+  }
+
+  /**
+   * Access the inner nodes and take ownership of them.
+   * You can only call these functions once per node
+   */
+
+  std::unique_ptr<Stmt> take_stmt ()
+  {
+    rust_assert (!is_error ());
+    return std::move (stmt);
+  }
+
+  std::unique_ptr<Expr> take_expr ()
+  {
+    rust_assert (!is_error ());
+    return std::move (expr);
+  }
+
+  std::unique_ptr<Item> take_item ()
+  {
+    rust_assert (!is_error ());
+    return std::move (item);
+  }
+
+  std::unique_ptr<TraitItem> take_trait_item ()
+  {
+    rust_assert (!is_error ());
+    return std::move (trait_item);
+  }
+
+  std::unique_ptr<ExternalItem> take_external_item ()
+  {
+    rust_assert (!is_error ());
+    return std::move (external_item);
+  }
+
+  std::unique_ptr<InherentImplItem> take_impl_item ()
+  {
+    rust_assert (!is_error ());
+    return std::move (impl_item);
+  }
+
+  std::unique_ptr<TraitImplItem> take_trait_impl_item ()
+  {
+    rust_assert (!is_error ());
+    return std::move (trait_impl_item);
+  }
+
+  std::unique_ptr<Type> take_type ()
+  {
+    rust_assert (!is_error ());
+    return std::move (type);
+  }
+
+  void accept_vis (ASTVisitor &vis)
+  {
+    switch (kind)
+      {
+      case EXPRESSION:
+	expr->accept_vis (vis);
+	break;
+
+      case ITEM:
+	item->accept_vis (vis);
+	break;
+
+      case STMT:
+	stmt->accept_vis (vis);
+	break;
+
+      case EXTERN:
+	external_item->accept_vis (vis);
+	break;
+
+      case TRAIT:
+	trait_item->accept_vis (vis);
+	break;
+
+      case IMPL:
+	impl_item->accept_vis (vis);
+	break;
+
+      case TRAIT_IMPL:
+	trait_impl_item->accept_vis (vis);
+	break;
+
+      case TYPE:
+	type->accept_vis (vis);
+	break;
+      }
+  }
+
+  bool is_error ()
+  {
+    switch (kind)
+      {
+      case EXPRESSION:
+	return expr == nullptr;
+      case ITEM:
+	return item == nullptr;
+      case STMT:
+	return stmt == nullptr;
+      case EXTERN:
+	return external_item == nullptr;
+      case TRAIT:
+	return trait_item == nullptr;
+      case IMPL:
+	return impl_item == nullptr;
+      case TRAIT_IMPL:
+	return trait_impl_item == nullptr;
+      case TYPE:
+	return type == nullptr;
+      }
+
+    gcc_unreachable ();
+    return true;
+  }
+
+  std::string as_string ()
+  {
+    switch (kind)
+      {
+      case EXPRESSION:
+	return "Expr: " + expr->as_string ();
+      case ITEM:
+	return "Item: " + item->as_string ();
+      case STMT:
+	return "Stmt: " + stmt->as_string ();
+      case EXTERN:
+	return "External Item: " + external_item->as_string ();
+      case TRAIT:
+	return "Trait Item: " + trait_item->as_string ();
+      case IMPL:
+	return "Impl Item: " + impl_item->as_string ();
+      case TRAIT_IMPL:
+	return "Trait Impl Item: " + trait_impl_item->as_string ();
+      case TYPE:
+	return "Type: " + type->as_string ();
+      }
+
+    gcc_unreachable ();
+    return "";
+  }
+};
+
+/* Basically, a "fragment" that can be incorporated into the AST, created as
+ * a result of macro expansion. Really annoying to work with due to the fact
+ * that macros can really expand to anything. As such, horrible representation
+ * at the moment. */
+class ASTFragment
+{
+private:
+  /* basic idea: essentially, a vector of tagged unions of different AST node
+   * types. Now, this could actually be stored without a tagged union if the
+   * different AST node types had a unified parent, but that would create
+   * issues with the diamond problem or significant performance penalties. So
+   * a tagged union had to be used instead. A vector is used to represent the
+   * ability for a macro to expand to two statements, for instance. */
+
+  std::vector<SingleASTNode> nodes;
+  bool fragment_is_error;
+
+  /**
+   * We need to make a special case for Expression and Type fragments as only
+   * one Node will be extracted from the `nodes` vector
+   */
+
+  bool is_single_fragment () const { return nodes.size () == 1; }
+
+  bool is_single_fragment_kind (SingleASTNode::NodeType kind) const
+  {
+    return is_single_fragment () && nodes[0].get_kind () == kind;
+  }
+
+public:
+  ASTFragment (std::vector<SingleASTNode> nodes, bool fragment_is_error = false)
+    : nodes (std::move (nodes)), fragment_is_error (fragment_is_error)
+  {
+    if (fragment_is_error)
+      rust_assert (nodes.empty ());
+  }
+
+  ASTFragment (ASTFragment const &other)
+    : fragment_is_error (other.fragment_is_error)
+  {
+    nodes.clear ();
+    nodes.reserve (other.nodes.size ());
+    for (auto &n : other.nodes)
+      {
+	nodes.push_back (n);
+      }
+  }
+
+  ASTFragment &operator= (ASTFragment const &other)
+  {
+    fragment_is_error = other.fragment_is_error;
+    nodes.clear ();
+    nodes.reserve (other.nodes.size ());
+    for (auto &n : other.nodes)
+      {
+	nodes.push_back (n);
+      }
+
+    return *this;
+  }
+
+  static ASTFragment create_error () { return ASTFragment ({}, true); }
+
+  std::vector<SingleASTNode> &get_nodes () { return nodes; }
+  bool is_error () const { return fragment_is_error; }
+
+  bool should_expand () const { return !is_error (); }
+
+  std::unique_ptr<Expr> take_expression_fragment ()
+  {
+    rust_assert (is_single_fragment_kind (SingleASTNode::NodeType::EXPRESSION));
+    return nodes[0].take_expr ();
+  }
+
+  std::unique_ptr<Type> take_type_fragment ()
+  {
+    rust_assert (is_single_fragment_kind (SingleASTNode::NodeType::TYPE));
+    return nodes[0].take_type ();
+  }
+
+  void accept_vis (ASTVisitor &vis)
+  {
+    for (auto &node : nodes)
+      node.accept_vis (vis);
+  }
+};
+
+// A crate AST object - holds all the data for a single compilation unit
+struct Crate
+{
+  std::vector<Attribute> inner_attrs;
+  // dodgy spacing required here
+  /* TODO: is it better to have a vector of items here or a module (implicit
+   * top-level one)? */
+  std::vector<std::unique_ptr<Item> > items;
+
+  NodeId node_id;
+
+public:
+  // Constructor
+  Crate (std::vector<std::unique_ptr<Item> > items,
+	 std::vector<Attribute> inner_attrs)
+    : inner_attrs (std::move (inner_attrs)), items (std::move (items)),
+      node_id (Analysis::Mappings::get ()->get_next_node_id ())
+  {}
+
+  // Copy constructor with vector clone
+  Crate (Crate const &other)
+    : inner_attrs (other.inner_attrs), node_id (other.node_id)
+  {
+    items.reserve (other.items.size ());
+    for (const auto &e : other.items)
+      items.push_back (e->clone_item ());
+  }
+
+  ~Crate () = default;
+
+  // Overloaded assignment operator with vector clone
+  Crate &operator= (Crate const &other)
+  {
+    inner_attrs = other.inner_attrs;
+    node_id = other.node_id;
+
+    items.reserve (other.items.size ());
+    for (const auto &e : other.items)
+      items.push_back (e->clone_item ());
+
+    return *this;
+  }
+
+  // Move constructors
+  Crate (Crate &&other) = default;
+  Crate &operator= (Crate &&other) = default;
+
+  // Get crate representation as string (e.g. for debugging).
+  std::string as_string () const;
+
+  // Delete all crate information, e.g. if fails cfg.
+  void strip_crate ()
+  {
+    inner_attrs.clear ();
+    inner_attrs.shrink_to_fit ();
+
+    items.clear ();
+    items.shrink_to_fit ();
+    // TODO: is this the best way to do this?
+  }
+
+  NodeId get_node_id () const { return node_id; }
+  const std::vector<Attribute> &get_inner_attrs () const { return inner_attrs; }
+};
+
+// Base path expression AST node - abstract
+class PathExpr : public ExprWithoutBlock
+{
+};
+} // namespace AST
+} // namespace Rust
+
+#endif
diff --git a/gcc/rust/ast/rust-cond-compilation.h b/gcc/rust/ast/rust-cond-compilation.h
new file mode 100644
index 00000000000..71188ef3b4b
--- /dev/null
+++ b/gcc/rust/ast/rust-cond-compilation.h
@@ -0,0 +1,249 @@
+// 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_AST_CONDCOMPILATION
+#define RUST_AST_CONDCOMPILATION
+// Conditional compilation-related AST stuff
+
+#include "rust-ast.h"
+
+namespace Rust {
+namespace AST {
+// Base conditional compilation configuration predicate thing - abstract
+class ConfigurationPredicate
+{
+public:
+  virtual ~ConfigurationPredicate () {}
+
+  // Unique pointer custom clone function
+  std::unique_ptr<ConfigurationPredicate> clone_configuration_predicate () const
+  {
+    return std::unique_ptr<ConfigurationPredicate> (
+      clone_configuration_predicate_impl ());
+  }
+
+  // not sure if I'll use this but here anyway
+  virtual void accept_vis (ASTVisitor &vis) = 0;
+
+protected:
+  // Clone function impl to be overriden in base classes
+  virtual ConfigurationPredicate *
+  clone_configuration_predicate_impl () const = 0;
+};
+
+// A configuration option - true if option is set, false if option is not set.
+class ConfigurationOption : public ConfigurationPredicate
+{
+  Identifier option_name;
+
+  // bool has_string_literal_option_body;
+  std::string option_value; // technically a string or raw string literal
+
+public:
+  /* Returns whether the configuration option has a "value" part of the
+   * key-value pair. */
+  bool has_option_value () const { return !option_value.empty (); }
+
+  // Key-value pair constructor
+  ConfigurationOption (Identifier option_name, std::string option_value)
+    : option_name (option_name), option_value (option_value)
+  {}
+
+  // Name-only constructor
+  ConfigurationOption (Identifier option_name) : option_name (option_name) {}
+
+  void accept_vis (ASTVisitor &vis) override;
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ConfigurationOption *clone_configuration_predicate_impl () const override
+  {
+    return new ConfigurationOption (*this);
+  }
+};
+
+// TODO: inline
+struct ConfigurationPredicateList
+{
+  std::vector<std::unique_ptr<ConfigurationPredicate>> predicate_list;
+};
+
+// Predicate that returns true if all of the supplied predicates return true.
+class ConfigurationAll : public ConfigurationPredicate
+{
+  std::vector<std::unique_ptr<ConfigurationPredicate>>
+    predicate_list; // inlined form
+
+public:
+  ConfigurationAll (
+    std::vector<std::unique_ptr<ConfigurationPredicate>> predicate_list)
+    : predicate_list (predicate_list)
+  {}
+
+  void accept_vis (ASTVisitor &vis) override;
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ConfigurationAll *clone_configuration_predicate_impl () const override
+  {
+    return new ConfigurationAll (*this);
+  }
+};
+
+// Predicate that returns true if any of the supplied predicates are true.
+class ConfigurationAny : public ConfigurationPredicate
+{
+  std::vector<std::unique_ptr<ConfigurationPredicate>>
+    predicate_list; // inlined form
+
+public:
+  ConfigurationAny (
+    std::vector<std::unique_ptr<ConfigurationPredicate>> predicate_list)
+    : predicate_list (predicate_list)
+  {}
+
+  void accept_vis (ASTVisitor &vis) override;
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ConfigurationAny *clone_configuration_predicate_impl () const override
+  {
+    return new ConfigurationAny (*this);
+  }
+};
+
+/* Predicate that produces the negation of a supplied other configuration
+ * predicate. */
+class ConfigurationNot : public ConfigurationPredicate
+{
+  std::unique_ptr<ConfigurationPredicate> config_to_negate;
+
+public:
+  ConfigurationNot (ConfigurationPredicate *config_to_negate)
+    : config_to_negate (config_to_negate)
+  {}
+
+  // Copy constructor with clone
+  ConfigurationNot (ConfigurationNot const &other)
+    : config_to_negate (
+      other.config_to_negate->clone_configuration_predicate ())
+  {}
+
+  // Overloaded assignment operator to clone
+  ConfigurationNot &operator= (ConfigurationNot const &other)
+  {
+    config_to_negate = other.config_to_negate->clone_configuration_predicate ();
+
+    return *this;
+  }
+
+  // move constructors
+  ConfigurationNot (ConfigurationNot &&other) = default;
+  ConfigurationNot &operator= (ConfigurationNot &&other) = default;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ConfigurationNot *clone_configuration_predicate_impl () const override
+  {
+    return new ConfigurationNot (*this);
+  }
+};
+
+// TODO: relationship to other attributes?
+class CfgAttribute
+{
+  std::unique_ptr<ConfigurationPredicate> config_to_include;
+
+public:
+  CfgAttribute (ConfigurationPredicate *config_to_include)
+    : config_to_include (config_to_include)
+  {}
+
+  // Copy constructor with clone
+  CfgAttribute (CfgAttribute const &other)
+    : config_to_include (
+      other.config_to_include->clone_configuration_predicate ())
+  {}
+
+  // Overloaded assignment operator to clone
+  CfgAttribute &operator= (CfgAttribute const &other)
+  {
+    config_to_include
+      = other.config_to_include->clone_configuration_predicate ();
+
+    return *this;
+  }
+
+  // move constructors
+  CfgAttribute (CfgAttribute &&other) = default;
+  CfgAttribute &operator= (CfgAttribute &&other) = default;
+};
+/* TODO: ok, best thing to do would be eliminating this class, making Attribute
+ * has a "is_cfg()" method, and having attribute path as "cfg" and AttrInput as
+ * ConfigurationPredicate (so make ConfigurationPredicate a subclass of
+ * AttrInput?). Would need special handling in parser, however. */
+
+// TODO: inline
+struct CfgAttrs
+{
+  std::vector<Attribute> cfg_attrs;
+};
+
+// TODO: relationship to other attributes?
+class CfgAttrAttribute
+{
+  std::unique_ptr<ConfigurationPredicate> config_to_include;
+  std::vector<Attribute> cfg_attrs;
+
+public:
+  CfgAttrAttribute (ConfigurationPredicate *config_to_include,
+		    std::vector<Attribute> cfg_attrs)
+    : config_to_include (config_to_include), cfg_attrs (cfg_attrs)
+  {}
+
+  // Copy constructor with clone
+  CfgAttrAttribute (CfgAttrAttribute const &other)
+    : config_to_include (
+      other.config_to_include->clone_configuration_predicate ()),
+      cfg_attrs (cfg_attrs)
+  {}
+
+  // Overloaded assignment operator to clone
+  CfgAttrAttribute &operator= (CfgAttrAttribute const &other)
+  {
+    config_to_include
+      = other.config_to_include->clone_configuration_predicate ();
+    cfg_attrs = other.cfg_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  CfgAttrAttribute (CfgAttrAttribute &&other) = default;
+  CfgAttrAttribute &operator= (CfgAttrAttribute &&other) = default;
+};
+} // namespace AST
+} // namespace Rust
+
+#endif
diff --git a/gcc/rust/ast/rust-expr.h b/gcc/rust/ast/rust-expr.h
new file mode 100644
index 00000000000..1966a590c94
--- /dev/null
+++ b/gcc/rust/ast/rust-expr.h
@@ -0,0 +1,4631 @@
+#ifndef RUST_AST_EXPR_H
+#define RUST_AST_EXPR_H
+
+#include "rust-ast.h"
+#include "rust-path.h"
+#include "operator.h"
+
+namespace Rust {
+namespace AST {
+/* TODO: if GCC moves to C++17 or allows boost, replace some boolean
+ * "has_whatever" pairs with
+ * optional types (std::optional or boost::optional)? */
+
+// AST node for an expression with an accompanying block - abstract
+class ExprWithBlock : public Expr
+{
+protected:
+  // pure virtual clone implementation
+  virtual ExprWithBlock *clone_expr_with_block_impl () const = 0;
+
+  // prevent having to define multiple clone expressions
+  ExprWithBlock *clone_expr_impl () const final override
+  {
+    return clone_expr_with_block_impl ();
+  }
+
+  bool is_expr_without_block () const final override { return false; };
+
+public:
+  // Unique pointer custom clone function
+  std::unique_ptr<ExprWithBlock> clone_expr_with_block () const
+  {
+    return std::unique_ptr<ExprWithBlock> (clone_expr_with_block_impl ());
+  }
+};
+
+// Literals? Or literal base?
+class LiteralExpr : public ExprWithoutBlock
+{
+  std::vector<Attribute> outer_attrs;
+  Literal literal;
+  Location locus;
+
+public:
+  std::string as_string () const override { return literal.as_string (); }
+
+  Literal::LitType get_lit_type () const { return literal.get_lit_type (); }
+
+  LiteralExpr (std::string value_as_string, Literal::LitType type,
+	       PrimitiveCoreType type_hint, std::vector<Attribute> outer_attrs,
+	       Location locus)
+    : outer_attrs (std::move (outer_attrs)),
+      literal (std::move (value_as_string), type, type_hint), locus (locus)
+  {}
+
+  LiteralExpr (Literal literal, std::vector<Attribute> outer_attrs,
+	       Location locus)
+    : outer_attrs (std::move (outer_attrs)), literal (std::move (literal)),
+      locus (locus)
+  {}
+
+  // Unique pointer custom clone function
+  std::unique_ptr<LiteralExpr> clone_literal_expr () const
+  {
+    return std::unique_ptr<LiteralExpr> (clone_literal_expr_impl ());
+  }
+
+  Location get_locus () const override final { return locus; }
+
+  Literal get_literal () const { return literal; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Invalid if literal is in error state, so base stripping on that.
+  void mark_for_strip () override { literal = Literal::create_error (); }
+  bool is_marked_for_strip () const override { return literal.is_error (); }
+
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+  void set_outer_attrs (std::vector<Attribute> new_attrs) override
+  {
+    outer_attrs = std::move (new_attrs);
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  LiteralExpr *clone_expr_without_block_impl () const final override
+  {
+    return clone_literal_expr_impl ();
+  }
+
+  /* not virtual as currently no subclasses of LiteralExpr, but could be in
+   * future */
+  /*virtual*/ LiteralExpr *clone_literal_expr_impl () const
+  {
+    return new LiteralExpr (*this);
+  }
+};
+
+// Literal expression attribute body (non-macro attribute)
+class AttrInputLiteral : public AttrInput
+{
+  LiteralExpr literal_expr;
+
+public:
+  AttrInputLiteral (LiteralExpr lit_expr) : literal_expr (std::move (lit_expr))
+  {}
+
+  std::string as_string () const override
+  {
+    return " = " + literal_expr.as_string ();
+  }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  /* this can never be a cfg predicate - cfg and cfg_attr require a token-tree
+   * cfg */
+  bool check_cfg_predicate (const Session &) const override { return false; }
+
+  bool is_meta_item () const override { return false; }
+
+  LiteralExpr &get_literal () { return literal_expr; }
+
+  AttrInputType get_attr_input_type () const final override
+  {
+    return AttrInput::AttrInputType::LITERAL;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  AttrInputLiteral *clone_attr_input_impl () const override
+  {
+    return new AttrInputLiteral (*this);
+  }
+};
+
+/* literal expr only meta item inner - TODO possibly replace with inheritance of
+ * LiteralExpr itself? */
+class MetaItemLitExpr : public MetaItemInner
+{
+  LiteralExpr lit_expr;
+
+public:
+  MetaItemLitExpr (LiteralExpr lit_expr) : lit_expr (std::move (lit_expr)) {}
+
+  std::string as_string () const override { return lit_expr.as_string (); }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  bool check_cfg_predicate (const Session &session) const override;
+
+protected:
+  // Use covariance to implement clone function as returning this type
+  MetaItemLitExpr *clone_meta_item_inner_impl () const override
+  {
+    return new MetaItemLitExpr (*this);
+  }
+};
+
+// more generic meta item "path = lit" form
+class MetaItemPathLit : public MetaItem
+{
+  SimplePath path;
+  LiteralExpr lit;
+
+public:
+  MetaItemPathLit (SimplePath path, LiteralExpr lit_expr)
+    : path (std::move (path)), lit (std::move (lit_expr))
+  {}
+
+  std::string as_string () const override
+  {
+    return path.as_string () + " = " + lit.as_string ();
+  }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  bool check_cfg_predicate (const Session &session) const override;
+  /* TODO: return true if "ident" is defined and value of it is "lit", return
+   * false otherwise */
+
+  Attribute to_attribute () const override;
+
+protected:
+  // Use covariance to implement clone function as returning this type
+  MetaItemPathLit *clone_meta_item_inner_impl () const override
+  {
+    return new MetaItemPathLit (*this);
+  }
+};
+
+/* Represents an expression using unary or binary operators as AST node. Can be
+ * overloaded. */
+class OperatorExpr : public ExprWithoutBlock
+{
+  // TODO: create binary and unary operator subclasses?
+public:
+  Location locus;
+
+protected:
+  /* Variables must be protected to allow derived classes to use them as first
+   * class citizens */
+  std::vector<Attribute> outer_attrs;
+  std::unique_ptr<Expr> main_or_left_expr;
+
+  // Constructor (only for initialisation of expr purposes)
+  OperatorExpr (std::unique_ptr<Expr> main_or_left_expr,
+		std::vector<Attribute> outer_attribs, Location locus)
+    : locus (locus), outer_attrs (std::move (outer_attribs)),
+      main_or_left_expr (std::move (main_or_left_expr))
+  {}
+
+  // Copy constructor (only for initialisation of expr purposes)
+  OperatorExpr (OperatorExpr const &other)
+    : locus (other.locus), outer_attrs (other.outer_attrs)
+  {
+    // guard to prevent null dereference (only required if error state)
+    if (other.main_or_left_expr != nullptr)
+      main_or_left_expr = other.main_or_left_expr->clone_expr ();
+  }
+
+  // Overload assignment operator to deep copy expr
+  OperatorExpr &operator= (OperatorExpr const &other)
+  {
+    ExprWithoutBlock::operator= (other);
+    locus = other.locus;
+    outer_attrs = other.outer_attrs;
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.main_or_left_expr != nullptr)
+      main_or_left_expr = other.main_or_left_expr->clone_expr ();
+    else
+      main_or_left_expr = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  OperatorExpr (OperatorExpr &&other) = default;
+  OperatorExpr &operator= (OperatorExpr &&other) = default;
+
+public:
+  Location get_locus () const override final { return locus; }
+
+  // Invalid if expr is null, so base stripping on that.
+  void mark_for_strip () override { main_or_left_expr = nullptr; }
+  bool is_marked_for_strip () const override
+  {
+    return main_or_left_expr == nullptr;
+  }
+
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+  void set_outer_attrs (std::vector<Attribute> new_attrs) override
+  {
+    outer_attrs = std::move (new_attrs);
+  }
+};
+
+/* Unary prefix & or &mut (or && and &&mut) borrow operator. Cannot be
+ * overloaded. */
+class BorrowExpr : public OperatorExpr
+{
+  bool is_mut;
+  bool double_borrow;
+
+public:
+  std::string as_string () const override;
+
+  BorrowExpr (std::unique_ptr<Expr> borrow_lvalue, bool is_mut_borrow,
+	      bool is_double_borrow, std::vector<Attribute> outer_attribs,
+	      Location locus)
+    : OperatorExpr (std::move (borrow_lvalue), std::move (outer_attribs),
+		    locus),
+      is_mut (is_mut_borrow), double_borrow (is_double_borrow)
+  {}
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_borrowed_expr ()
+  {
+    rust_assert (main_or_left_expr != nullptr);
+    return main_or_left_expr;
+  }
+
+  bool get_is_mut () const { return is_mut; }
+
+  bool get_is_double_borrow () const { return double_borrow; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  BorrowExpr *clone_expr_without_block_impl () const override
+  {
+    return new BorrowExpr (*this);
+  }
+};
+
+// Unary prefix * deference operator
+class DereferenceExpr : public OperatorExpr
+{
+public:
+  std::string as_string () const override;
+
+  // Constructor calls OperatorExpr's protected constructor
+  DereferenceExpr (std::unique_ptr<Expr> deref_lvalue,
+		   std::vector<Attribute> outer_attribs, Location locus)
+    : OperatorExpr (std::move (deref_lvalue), std::move (outer_attribs), locus)
+  {}
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_dereferenced_expr ()
+  {
+    rust_assert (main_or_left_expr != nullptr);
+    return main_or_left_expr;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  DereferenceExpr *clone_expr_without_block_impl () const override
+  {
+    return new DereferenceExpr (*this);
+  }
+};
+
+// Unary postfix ? error propogation operator. Cannot be overloaded.
+class ErrorPropagationExpr : public OperatorExpr
+{
+public:
+  std::string as_string () const override;
+
+  // Constructor calls OperatorExpr's protected constructor
+  ErrorPropagationExpr (std::unique_ptr<Expr> potential_error_value,
+			std::vector<Attribute> outer_attribs, Location locus)
+    : OperatorExpr (std::move (potential_error_value),
+		    std::move (outer_attribs), locus)
+  {}
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_propagating_expr ()
+  {
+    rust_assert (main_or_left_expr != nullptr);
+    return main_or_left_expr;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ErrorPropagationExpr *clone_expr_without_block_impl () const override
+  {
+    return new ErrorPropagationExpr (*this);
+  }
+};
+
+// Unary prefix - or ! negation or NOT operators.
+class NegationExpr : public OperatorExpr
+{
+public:
+  using ExprType = NegationOperator;
+
+private:
+  /* Note: overload negation via std::ops::Neg and not via std::ops::Not
+   * Negation only works for signed integer and floating-point types, NOT only
+   * works for boolean and integer types (via bitwise NOT) */
+  ExprType expr_type;
+
+public:
+  std::string as_string () const override;
+
+  ExprType get_expr_type () const { return expr_type; }
+
+  // Constructor calls OperatorExpr's protected constructor
+  NegationExpr (std::unique_ptr<Expr> negated_value, ExprType expr_kind,
+		std::vector<Attribute> outer_attribs, Location locus)
+    : OperatorExpr (std::move (negated_value), std::move (outer_attribs),
+		    locus),
+      expr_type (expr_kind)
+  {}
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_negated_expr ()
+  {
+    rust_assert (main_or_left_expr != nullptr);
+    return main_or_left_expr;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  NegationExpr *clone_expr_without_block_impl () const override
+  {
+    return new NegationExpr (*this);
+  }
+};
+
+// Infix binary operators. +, -, *, /, %, &, |, ^, <<, >>
+class ArithmeticOrLogicalExpr : public OperatorExpr
+{
+public:
+  using ExprType = ArithmeticOrLogicalOperator;
+
+private:
+  // Note: overloading trait specified in comments
+  ExprType expr_type;
+
+  std::unique_ptr<Expr> right_expr;
+
+public:
+  std::string as_string () const override;
+
+  ExprType get_expr_type () const { return expr_type; }
+
+  // Constructor calls OperatorExpr's protected constructor
+  ArithmeticOrLogicalExpr (std::unique_ptr<Expr> left_value,
+			   std::unique_ptr<Expr> right_value,
+			   ExprType expr_kind, Location locus)
+    : OperatorExpr (std::move (left_value), std::vector<Attribute> (), locus),
+      expr_type (expr_kind), right_expr (std::move (right_value))
+  {}
+  // outer attributes not allowed
+
+  // Copy constructor - probably required due to unique pointer
+  ArithmeticOrLogicalExpr (ArithmeticOrLogicalExpr const &other)
+    : OperatorExpr (other), expr_type (other.expr_type),
+      right_expr (other.right_expr->clone_expr ())
+  {}
+
+  // Overload assignment operator
+  ArithmeticOrLogicalExpr &operator= (ArithmeticOrLogicalExpr const &other)
+  {
+    OperatorExpr::operator= (other);
+    // main_or_left_expr = other.main_or_left_expr->clone_expr();
+    right_expr = other.right_expr->clone_expr ();
+    expr_type = other.expr_type;
+
+    return *this;
+  }
+
+  // move constructors
+  ArithmeticOrLogicalExpr (ArithmeticOrLogicalExpr &&other) = default;
+  ArithmeticOrLogicalExpr &operator= (ArithmeticOrLogicalExpr &&other)
+    = default;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_left_expr ()
+  {
+    rust_assert (main_or_left_expr != nullptr);
+    return main_or_left_expr;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_right_expr ()
+  {
+    rust_assert (right_expr != nullptr);
+    return right_expr;
+  }
+
+  void visit_lhs (ASTVisitor &vis) { main_or_left_expr->accept_vis (vis); }
+  void visit_rhs (ASTVisitor &vis) { right_expr->accept_vis (vis); }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ArithmeticOrLogicalExpr *clone_expr_without_block_impl () const override
+  {
+    return new ArithmeticOrLogicalExpr (*this);
+  }
+};
+
+// Infix binary comparison operators. ==, !=, <, <=, >, >=
+class ComparisonExpr : public OperatorExpr
+{
+public:
+  using ExprType = ComparisonOperator;
+
+private:
+  // Note: overloading trait specified in comments
+  ExprType expr_type;
+
+  std::unique_ptr<Expr> right_expr;
+
+public:
+  std::string as_string () const override;
+
+  ExprType get_expr_type () const { return expr_type; }
+
+  // Constructor requires pointers for polymorphism
+  ComparisonExpr (std::unique_ptr<Expr> left_value,
+		  std::unique_ptr<Expr> right_value, ExprType comparison_kind,
+		  Location locus)
+    : OperatorExpr (std::move (left_value), std::vector<Attribute> (), locus),
+      expr_type (comparison_kind), right_expr (std::move (right_value))
+  {}
+  // outer attributes not allowed
+
+  // Copy constructor also calls OperatorExpr's protected constructor
+  ComparisonExpr (ComparisonExpr const &other)
+    : OperatorExpr (other), expr_type (other.expr_type),
+      right_expr (other.right_expr->clone_expr ())
+  {}
+
+  // Overload assignment operator to deep copy
+  ComparisonExpr &operator= (ComparisonExpr const &other)
+  {
+    OperatorExpr::operator= (other);
+    // main_or_left_expr = other.main_or_left_expr->clone_expr();
+    right_expr = other.right_expr->clone_expr ();
+    expr_type = other.expr_type;
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  ComparisonExpr (ComparisonExpr &&other) = default;
+  ComparisonExpr &operator= (ComparisonExpr &&other) = default;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_left_expr ()
+  {
+    rust_assert (main_or_left_expr != nullptr);
+    return main_or_left_expr;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_right_expr ()
+  {
+    rust_assert (right_expr != nullptr);
+    return right_expr;
+  }
+
+  ExprType get_kind () { return expr_type; }
+
+  /* TODO: implement via a function call to std::cmp::PartialEq::eq(&op1, &op2)
+   * maybe? */
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ComparisonExpr *clone_expr_without_block_impl () const override
+  {
+    return new ComparisonExpr (*this);
+  }
+};
+
+// Infix binary lazy boolean logical operators && and ||.
+class LazyBooleanExpr : public OperatorExpr
+{
+public:
+  using ExprType = LazyBooleanOperator;
+
+private:
+  ExprType expr_type;
+
+  std::unique_ptr<Expr> right_expr;
+
+public:
+  // Constructor calls OperatorExpr's protected constructor
+  LazyBooleanExpr (std::unique_ptr<Expr> left_bool_expr,
+		   std::unique_ptr<Expr> right_bool_expr, ExprType expr_kind,
+		   Location locus)
+    : OperatorExpr (std::move (left_bool_expr), std::vector<Attribute> (),
+		    locus),
+      expr_type (expr_kind), right_expr (std::move (right_bool_expr))
+  {}
+  // outer attributes not allowed
+
+  // Copy constructor also calls OperatorExpr's protected constructor
+  LazyBooleanExpr (LazyBooleanExpr const &other)
+    : OperatorExpr (other), expr_type (other.expr_type),
+      right_expr (other.right_expr->clone_expr ())
+  {}
+
+  // Overload assignment operator to deep copy
+  LazyBooleanExpr &operator= (LazyBooleanExpr const &other)
+  {
+    OperatorExpr::operator= (other);
+    // main_or_left_expr = other.main_or_left_expr->clone_expr();
+    right_expr = other.right_expr->clone_expr ();
+    expr_type = other.expr_type;
+
+    return *this;
+  }
+
+  // move constructors
+  LazyBooleanExpr (LazyBooleanExpr &&other) = default;
+  LazyBooleanExpr &operator= (LazyBooleanExpr &&other) = default;
+
+  std::string as_string () const override;
+
+  ExprType get_expr_type () const { return expr_type; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_left_expr ()
+  {
+    rust_assert (main_or_left_expr != nullptr);
+    return main_or_left_expr;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_right_expr ()
+  {
+    rust_assert (right_expr != nullptr);
+    return right_expr;
+  }
+
+  ExprType get_kind () { return expr_type; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  LazyBooleanExpr *clone_expr_without_block_impl () const override
+  {
+    return new LazyBooleanExpr (*this);
+  }
+};
+
+// Binary infix "as" cast expression.
+class TypeCastExpr : public OperatorExpr
+{
+  std::unique_ptr<TypeNoBounds> type_to_convert_to;
+
+  // Note: only certain type casts allowed, outlined in reference
+public:
+  std::string as_string () const override;
+
+  // Constructor requires calling protected constructor of OperatorExpr
+  TypeCastExpr (std::unique_ptr<Expr> expr_to_cast,
+		std::unique_ptr<TypeNoBounds> type_to_cast_to, Location locus)
+    : OperatorExpr (std::move (expr_to_cast), std::vector<Attribute> (), locus),
+      type_to_convert_to (std::move (type_to_cast_to))
+  {}
+  // outer attributes not allowed
+
+  // Copy constructor also requires calling protected constructor
+  TypeCastExpr (TypeCastExpr const &other)
+    : OperatorExpr (other),
+      type_to_convert_to (other.type_to_convert_to->clone_type_no_bounds ())
+  {}
+
+  // Overload assignment operator to deep copy
+  TypeCastExpr &operator= (TypeCastExpr const &other)
+  {
+    OperatorExpr::operator= (other);
+    // main_or_left_expr = other.main_or_left_expr->clone_expr();
+    type_to_convert_to = other.type_to_convert_to->clone_type_no_bounds ();
+
+    return *this;
+  }
+
+  // move constructors
+  TypeCastExpr (TypeCastExpr &&other) = default;
+  TypeCastExpr &operator= (TypeCastExpr &&other) = default;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_casted_expr ()
+  {
+    rust_assert (main_or_left_expr != nullptr);
+    return main_or_left_expr;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<TypeNoBounds> &get_type_to_cast_to ()
+  {
+    rust_assert (type_to_convert_to != nullptr);
+    return type_to_convert_to;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TypeCastExpr *clone_expr_without_block_impl () const override
+  {
+    return new TypeCastExpr (*this);
+  }
+};
+
+// Binary assignment expression.
+class AssignmentExpr : public OperatorExpr
+{
+  std::unique_ptr<Expr> right_expr;
+
+public:
+  std::string as_string () const override;
+
+  // Call OperatorExpr constructor to initialise left_expr
+  AssignmentExpr (std::unique_ptr<Expr> value_to_assign_to,
+		  std::unique_ptr<Expr> value_to_assign,
+		  std::vector<Attribute> outer_attribs, Location locus)
+    : OperatorExpr (std::move (value_to_assign_to), std::move (outer_attribs),
+		    locus),
+      right_expr (std::move (value_to_assign))
+  {}
+  // outer attributes not allowed
+
+  // Call OperatorExpr constructor in copy constructor, as well as clone
+  AssignmentExpr (AssignmentExpr const &other)
+    : OperatorExpr (other), right_expr (other.right_expr->clone_expr ())
+  {}
+
+  // Overload assignment operator to clone unique_ptr right_expr
+  AssignmentExpr &operator= (AssignmentExpr const &other)
+  {
+    OperatorExpr::operator= (other);
+    // main_or_left_expr = other.main_or_left_expr->clone_expr();
+    right_expr = other.right_expr->clone_expr ();
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  AssignmentExpr (AssignmentExpr &&other) = default;
+  AssignmentExpr &operator= (AssignmentExpr &&other) = default;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  void visit_lhs (ASTVisitor &vis) { main_or_left_expr->accept_vis (vis); }
+  void visit_rhs (ASTVisitor &vis) { right_expr->accept_vis (vis); }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_left_expr ()
+  {
+    rust_assert (main_or_left_expr != nullptr);
+    return main_or_left_expr;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_right_expr ()
+  {
+    rust_assert (right_expr != nullptr);
+    return right_expr;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  AssignmentExpr *clone_expr_without_block_impl () const override
+  {
+    return new AssignmentExpr (*this);
+  }
+};
+
+/* Binary infix compound assignment (arithmetic or logic then assignment)
+ * expressions. */
+class CompoundAssignmentExpr : public OperatorExpr
+{
+public:
+  using ExprType = CompoundAssignmentOperator;
+
+private:
+  // Note: overloading trait specified in comments
+  ExprType expr_type;
+  std::unique_ptr<Expr> right_expr;
+
+public:
+  std::string as_string () const override;
+
+  ExprType get_expr_type () const { return expr_type; }
+
+  // Use pointers in constructor to enable polymorphism
+  CompoundAssignmentExpr (std::unique_ptr<Expr> value_to_assign_to,
+			  std::unique_ptr<Expr> value_to_assign,
+			  ExprType expr_kind, Location locus)
+    : OperatorExpr (std::move (value_to_assign_to), std::vector<Attribute> (),
+		    locus),
+      expr_type (expr_kind), right_expr (std::move (value_to_assign))
+  {}
+  // outer attributes not allowed
+
+  // Have clone in copy constructor
+  CompoundAssignmentExpr (CompoundAssignmentExpr const &other)
+    : OperatorExpr (other), expr_type (other.expr_type),
+      right_expr (other.right_expr->clone_expr ())
+  {}
+
+  // Overload assignment operator to clone
+  CompoundAssignmentExpr &operator= (CompoundAssignmentExpr const &other)
+  {
+    OperatorExpr::operator= (other);
+    // main_or_left_expr = other.main_or_left_expr->clone_expr();
+    right_expr = other.right_expr->clone_expr ();
+    expr_type = other.expr_type;
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  CompoundAssignmentExpr (CompoundAssignmentExpr &&other) = default;
+  CompoundAssignmentExpr &operator= (CompoundAssignmentExpr &&other) = default;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_left_expr ()
+  {
+    rust_assert (main_or_left_expr != nullptr);
+    return main_or_left_expr;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_right_expr ()
+  {
+    rust_assert (right_expr != nullptr);
+    return right_expr;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  CompoundAssignmentExpr *clone_expr_without_block_impl () const override
+  {
+    return new CompoundAssignmentExpr (*this);
+  }
+};
+
+// Expression in parentheses (i.e. like literally just any 3 + (2 * 6))
+class GroupedExpr : public ExprWithoutBlock
+{
+  std::vector<Attribute> outer_attrs;
+  std::vector<Attribute> inner_attrs;
+  std::unique_ptr<Expr> expr_in_parens;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  const std::vector<Attribute> &get_inner_attrs () const { return inner_attrs; }
+  std::vector<Attribute> &get_inner_attrs () { return inner_attrs; }
+
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+  void set_outer_attrs (std::vector<Attribute> new_attrs) override
+  {
+    outer_attrs = std::move (new_attrs);
+  }
+
+  GroupedExpr (std::unique_ptr<Expr> parenthesised_expr,
+	       std::vector<Attribute> inner_attribs,
+	       std::vector<Attribute> outer_attribs, Location locus)
+    : outer_attrs (std::move (outer_attribs)),
+      inner_attrs (std::move (inner_attribs)),
+      expr_in_parens (std::move (parenthesised_expr)), locus (locus)
+  {}
+
+  // Copy constructor includes clone for expr_in_parens
+  GroupedExpr (GroupedExpr const &other)
+    : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
+      inner_attrs (other.inner_attrs), locus (other.locus)
+  {
+    // guard to prevent null dereference (only required if error state)
+    if (other.expr_in_parens != nullptr)
+      expr_in_parens = other.expr_in_parens->clone_expr ();
+  }
+
+  // Overloaded assignment operator to clone expr_in_parens
+  GroupedExpr &operator= (GroupedExpr const &other)
+  {
+    ExprWithoutBlock::operator= (other);
+    inner_attrs = other.inner_attrs;
+    locus = other.locus;
+    outer_attrs = other.outer_attrs;
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.expr_in_parens != nullptr)
+      expr_in_parens = other.expr_in_parens->clone_expr ();
+    else
+      expr_in_parens = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  GroupedExpr (GroupedExpr &&other) = default;
+  GroupedExpr &operator= (GroupedExpr &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Invalid if inner expr is null, so base stripping on that.
+  void mark_for_strip () override { expr_in_parens = nullptr; }
+  bool is_marked_for_strip () const override
+  {
+    return expr_in_parens == nullptr;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_expr_in_parens ()
+  {
+    rust_assert (expr_in_parens != nullptr);
+    return expr_in_parens;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  GroupedExpr *clone_expr_without_block_impl () const override
+  {
+    return new GroupedExpr (*this);
+  }
+};
+
+// Base array initialisation internal element representation thing (abstract)
+// aka ArrayElements
+class ArrayElems
+{
+public:
+  virtual ~ArrayElems () {}
+
+  // Unique pointer custom clone ArrayElems function
+  std::unique_ptr<ArrayElems> clone_array_elems () const
+  {
+    return std::unique_ptr<ArrayElems> (clone_array_elems_impl ());
+  }
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (ASTVisitor &vis) = 0;
+
+  NodeId get_node_id () const { return node_id; }
+
+protected:
+  ArrayElems () : node_id (Analysis::Mappings::get ()->get_next_node_id ()) {}
+
+  // pure virtual clone implementation
+  virtual ArrayElems *clone_array_elems_impl () const = 0;
+
+  NodeId node_id;
+};
+
+// Value array elements
+class ArrayElemsValues : public ArrayElems
+{
+  std::vector<std::unique_ptr<Expr> > values;
+  Location locus;
+
+public:
+  ArrayElemsValues (std::vector<std::unique_ptr<Expr> > elems, Location locus)
+    : ArrayElems (), values (std::move (elems)), locus (locus)
+  {}
+
+  // copy constructor with vector clone
+  ArrayElemsValues (ArrayElemsValues const &other)
+  {
+    values.reserve (other.values.size ());
+    for (const auto &e : other.values)
+      values.push_back (e->clone_expr ());
+  }
+
+  // overloaded assignment operator with vector clone
+  ArrayElemsValues &operator= (ArrayElemsValues const &other)
+  {
+    values.reserve (other.values.size ());
+    for (const auto &e : other.values)
+      values.push_back (e->clone_expr ());
+
+    return *this;
+  }
+
+  // move constructors
+  ArrayElemsValues (ArrayElemsValues &&other) = default;
+  ArrayElemsValues &operator= (ArrayElemsValues &&other) = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: this mutable getter seems really dodgy. Think up better way.
+  const std::vector<std::unique_ptr<Expr> > &get_values () const
+  {
+    return values;
+  }
+  std::vector<std::unique_ptr<Expr> > &get_values () { return values; }
+
+  size_t get_num_values () const { return values.size (); }
+
+protected:
+  ArrayElemsValues *clone_array_elems_impl () const override
+  {
+    return new ArrayElemsValues (*this);
+  }
+};
+
+// Copied array element and number of copies
+class ArrayElemsCopied : public ArrayElems
+{
+  std::unique_ptr<Expr> elem_to_copy;
+  std::unique_ptr<Expr> num_copies;
+  Location locus;
+
+public:
+  // Constructor requires pointers for polymorphism
+  ArrayElemsCopied (std::unique_ptr<Expr> copied_elem,
+		    std::unique_ptr<Expr> copy_amount, Location locus)
+    : ArrayElems (), elem_to_copy (std::move (copied_elem)),
+      num_copies (std::move (copy_amount)), locus (locus)
+  {}
+
+  // Copy constructor required due to unique_ptr - uses custom clone
+  ArrayElemsCopied (ArrayElemsCopied const &other)
+    : elem_to_copy (other.elem_to_copy->clone_expr ()),
+      num_copies (other.num_copies->clone_expr ())
+  {}
+
+  // Overloaded assignment operator for deep copying
+  ArrayElemsCopied &operator= (ArrayElemsCopied const &other)
+  {
+    elem_to_copy = other.elem_to_copy->clone_expr ();
+    num_copies = other.num_copies->clone_expr ();
+
+    return *this;
+  }
+
+  // move constructors
+  ArrayElemsCopied (ArrayElemsCopied &&other) = default;
+  ArrayElemsCopied &operator= (ArrayElemsCopied &&other) = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_elem_to_copy ()
+  {
+    rust_assert (elem_to_copy != nullptr);
+    return elem_to_copy;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_num_copies ()
+  {
+    rust_assert (num_copies != nullptr);
+    return num_copies;
+  }
+
+protected:
+  ArrayElemsCopied *clone_array_elems_impl () const override
+  {
+    return new ArrayElemsCopied (*this);
+  }
+};
+
+// Array definition-ish expression
+class ArrayExpr : public ExprWithoutBlock
+{
+  std::vector<Attribute> outer_attrs;
+  std::vector<Attribute> inner_attrs;
+  std::unique_ptr<ArrayElems> internal_elements;
+  Location locus;
+
+  // TODO: find another way to store this to save memory?
+  bool marked_for_strip = false;
+
+public:
+  std::string as_string () const override;
+
+  const std::vector<Attribute> &get_inner_attrs () const { return inner_attrs; }
+  std::vector<Attribute> &get_inner_attrs () { return inner_attrs; }
+
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+  void set_outer_attrs (std::vector<Attribute> new_attrs) override
+  {
+    outer_attrs = std::move (new_attrs);
+  }
+
+  // Constructor requires ArrayElems pointer
+  ArrayExpr (std::unique_ptr<ArrayElems> array_elems,
+	     std::vector<Attribute> inner_attribs,
+	     std::vector<Attribute> outer_attribs, Location locus)
+    : outer_attrs (std::move (outer_attribs)),
+      inner_attrs (std::move (inner_attribs)),
+      internal_elements (std::move (array_elems)), locus (locus)
+  {
+    rust_assert (internal_elements != nullptr);
+  }
+
+  // Copy constructor requires cloning ArrayElems for polymorphism to hold
+  ArrayExpr (ArrayExpr const &other)
+    : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
+      inner_attrs (other.inner_attrs), locus (other.locus),
+      marked_for_strip (other.marked_for_strip)
+  {
+    internal_elements = other.internal_elements->clone_array_elems ();
+    rust_assert (internal_elements != nullptr);
+  }
+
+  // Overload assignment operator to clone internal_elements
+  ArrayExpr &operator= (ArrayExpr const &other)
+  {
+    ExprWithoutBlock::operator= (other);
+    inner_attrs = other.inner_attrs;
+    locus = other.locus;
+    marked_for_strip = other.marked_for_strip;
+    outer_attrs = other.outer_attrs;
+
+    internal_elements = other.internal_elements->clone_array_elems ();
+
+    rust_assert (internal_elements != nullptr);
+    return *this;
+  }
+
+  // move constructors
+  ArrayExpr (ArrayExpr &&other) = default;
+  ArrayExpr &operator= (ArrayExpr &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Can't think of any invalid invariants, so store boolean.
+  void mark_for_strip () override { marked_for_strip = true; }
+  bool is_marked_for_strip () const override { return marked_for_strip; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<ArrayElems> &get_array_elems ()
+  {
+    rust_assert (internal_elements != nullptr);
+    return internal_elements;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ArrayExpr *clone_expr_without_block_impl () const override
+  {
+    return new ArrayExpr (*this);
+  }
+};
+
+// Aka IndexExpr (also applies to slices)
+/* Apparently a[b] is equivalent to *std::ops::Index::index(&a, b) or
+ * *std::ops::Index::index_mut(&mut a, b) */
+/* Also apparently deref operations on a will be repeatedly applied to find an
+ * implementation */
+class ArrayIndexExpr : public ExprWithoutBlock
+{
+  std::vector<Attribute> outer_attrs;
+  std::unique_ptr<Expr> array_expr;
+  std::unique_ptr<Expr> index_expr;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  ArrayIndexExpr (std::unique_ptr<Expr> array_expr,
+		  std::unique_ptr<Expr> array_index_expr,
+		  std::vector<Attribute> outer_attribs, Location locus)
+    : outer_attrs (std::move (outer_attribs)),
+      array_expr (std::move (array_expr)),
+      index_expr (std::move (array_index_expr)), locus (locus)
+  {}
+
+  // Copy constructor requires special cloning due to unique_ptr
+  ArrayIndexExpr (ArrayIndexExpr const &other)
+    : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
+      locus (other.locus)
+  {
+    // guard to prevent null dereference (only required if error state)
+    if (other.array_expr != nullptr)
+      array_expr = other.array_expr->clone_expr ();
+    if (other.index_expr != nullptr)
+      index_expr = other.index_expr->clone_expr ();
+  }
+
+  // Overload assignment operator to clone unique_ptrs
+  ArrayIndexExpr &operator= (ArrayIndexExpr const &other)
+  {
+    ExprWithoutBlock::operator= (other);
+    outer_attrs = other.outer_attrs;
+    locus = other.locus;
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.array_expr != nullptr)
+      array_expr = other.array_expr->clone_expr ();
+    else
+      array_expr = nullptr;
+    if (other.index_expr != nullptr)
+      index_expr = other.index_expr->clone_expr ();
+    else
+      index_expr = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  ArrayIndexExpr (ArrayIndexExpr &&other) = default;
+  ArrayIndexExpr &operator= (ArrayIndexExpr &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Invalid if either expr is null, so base stripping on that.
+  void mark_for_strip () override
+  {
+    array_expr = nullptr;
+    index_expr = nullptr;
+  }
+  bool is_marked_for_strip () const override
+  {
+    return array_expr == nullptr && index_expr == nullptr;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_array_expr ()
+  {
+    rust_assert (array_expr != nullptr);
+    return array_expr;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_index_expr ()
+  {
+    rust_assert (index_expr != nullptr);
+    return index_expr;
+  }
+
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+  void set_outer_attrs (std::vector<Attribute> new_attrs) override
+  {
+    outer_attrs = std::move (new_attrs);
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ArrayIndexExpr *clone_expr_without_block_impl () const override
+  {
+    return new ArrayIndexExpr (*this);
+  }
+};
+
+// AST representation of a tuple
+class TupleExpr : public ExprWithoutBlock
+{
+  std::vector<Attribute> outer_attrs;
+  std::vector<Attribute> inner_attrs;
+  std::vector<std::unique_ptr<Expr> > tuple_elems;
+  Location locus;
+
+  // TODO: find another way to store this to save memory?
+  bool marked_for_strip = false;
+
+public:
+  std::string as_string () const override;
+
+  const std::vector<Attribute> &get_inner_attrs () const { return inner_attrs; }
+  std::vector<Attribute> &get_inner_attrs () { return inner_attrs; }
+
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+  void set_outer_attrs (std::vector<Attribute> new_attrs) override
+  {
+    outer_attrs = std::move (new_attrs);
+  }
+
+  TupleExpr (std::vector<std::unique_ptr<Expr> > tuple_elements,
+	     std::vector<Attribute> inner_attribs,
+	     std::vector<Attribute> outer_attribs, Location locus)
+    : outer_attrs (std::move (outer_attribs)),
+      inner_attrs (std::move (inner_attribs)),
+      tuple_elems (std::move (tuple_elements)), locus (locus)
+  {}
+
+  // copy constructor with vector clone
+  TupleExpr (TupleExpr const &other)
+    : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
+      inner_attrs (other.inner_attrs), locus (other.locus),
+      marked_for_strip (other.marked_for_strip)
+  {
+    tuple_elems.reserve (other.tuple_elems.size ());
+    for (const auto &e : other.tuple_elems)
+      tuple_elems.push_back (e->clone_expr ());
+  }
+
+  // overloaded assignment operator to vector clone
+  TupleExpr &operator= (TupleExpr const &other)
+  {
+    ExprWithoutBlock::operator= (other);
+    outer_attrs = other.outer_attrs;
+    inner_attrs = other.inner_attrs;
+    locus = other.locus;
+    marked_for_strip = other.marked_for_strip;
+
+    tuple_elems.reserve (other.tuple_elems.size ());
+    for (const auto &e : other.tuple_elems)
+      tuple_elems.push_back (e->clone_expr ());
+
+    return *this;
+  }
+
+  // move constructors
+  TupleExpr (TupleExpr &&other) = default;
+  TupleExpr &operator= (TupleExpr &&other) = default;
+
+  /* Note: syntactically, can disambiguate single-element tuple from parens with
+   * comma, i.e. (0,) rather than (0) */
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Can't think of any invalid invariants, so store boolean.
+  void mark_for_strip () override { marked_for_strip = true; }
+  bool is_marked_for_strip () const override { return marked_for_strip; }
+
+  // TODO: this mutable getter seems really dodgy. Think up better way.
+  const std::vector<std::unique_ptr<Expr> > &get_tuple_elems () const
+  {
+    return tuple_elems;
+  }
+  std::vector<std::unique_ptr<Expr> > &get_tuple_elems ()
+  {
+    return tuple_elems;
+  }
+
+  bool is_unit () const { return tuple_elems.size () == 0; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TupleExpr *clone_expr_without_block_impl () const override
+  {
+    return new TupleExpr (*this);
+  }
+};
+
+// aka TupleIndexingExpr
+// AST representation of a tuple indexing expression
+class TupleIndexExpr : public ExprWithoutBlock
+{
+  std::vector<Attribute> outer_attrs;
+  std::unique_ptr<Expr> tuple_expr;
+  // TupleIndex is a decimal int literal with no underscores or suffix
+  TupleIndex tuple_index;
+
+  Location locus;
+
+  // i.e. pair.0
+
+public:
+  std::string as_string () const override;
+
+  TupleIndex get_tuple_index () const { return tuple_index; }
+
+  TupleIndexExpr (std::unique_ptr<Expr> tuple_expr, TupleIndex index,
+		  std::vector<Attribute> outer_attribs, Location locus)
+    : outer_attrs (std::move (outer_attribs)),
+      tuple_expr (std::move (tuple_expr)), tuple_index (index), locus (locus)
+  {}
+
+  // Copy constructor requires a clone for tuple_expr
+  TupleIndexExpr (TupleIndexExpr const &other)
+    : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
+      tuple_index (other.tuple_index), locus (other.locus)
+  {
+    // guard to prevent null dereference (only required if error state)
+    if (other.tuple_expr != nullptr)
+      tuple_expr = other.tuple_expr->clone_expr ();
+  }
+
+  // Overload assignment operator in order to clone
+  TupleIndexExpr &operator= (TupleIndexExpr const &other)
+  {
+    ExprWithoutBlock::operator= (other);
+    tuple_index = other.tuple_index;
+    locus = other.locus;
+    outer_attrs = other.outer_attrs;
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.tuple_expr != nullptr)
+      tuple_expr = other.tuple_expr->clone_expr ();
+    else
+      tuple_expr = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  TupleIndexExpr (TupleIndexExpr &&other) = default;
+  TupleIndexExpr &operator= (TupleIndexExpr &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Invalid if tuple expr is null, so base stripping on that.
+  void mark_for_strip () override { tuple_expr = nullptr; }
+  bool is_marked_for_strip () const override { return tuple_expr == nullptr; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_tuple_expr ()
+  {
+    rust_assert (tuple_expr != nullptr);
+    return tuple_expr;
+  }
+
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+  void set_outer_attrs (std::vector<Attribute> new_attrs) override
+  {
+    outer_attrs = std::move (new_attrs);
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TupleIndexExpr *clone_expr_without_block_impl () const override
+  {
+    return new TupleIndexExpr (*this);
+  }
+};
+
+// Base struct/tuple/union value creator AST node (abstract)
+class StructExpr : public ExprWithoutBlock
+{
+  std::vector<Attribute> outer_attrs;
+  PathInExpression struct_name;
+
+protected:
+  // Protected constructor to allow initialising struct_name
+  StructExpr (PathInExpression struct_path,
+	      std::vector<Attribute> outer_attribs)
+    : outer_attrs (std::move (outer_attribs)),
+      struct_name (std::move (struct_path))
+  {}
+
+public:
+  const PathInExpression &get_struct_name () const { return struct_name; }
+  PathInExpression &get_struct_name () { return struct_name; }
+
+  std::string as_string () const override;
+
+  // Invalid if path is empty, so base stripping on that.
+  void mark_for_strip () override
+  {
+    struct_name = PathInExpression::create_error ();
+  }
+  bool is_marked_for_strip () const override { return struct_name.is_error (); }
+
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+  void set_outer_attrs (std::vector<Attribute> new_attrs) override
+  {
+    outer_attrs = std::move (new_attrs);
+  }
+};
+
+// Actual AST node of the struct creator (with no fields). Not abstract!
+class StructExprStruct : public StructExpr
+{
+  std::vector<Attribute> inner_attrs;
+
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  const std::vector<Attribute> &get_inner_attrs () const { return inner_attrs; }
+  std::vector<Attribute> &get_inner_attrs () { return inner_attrs; }
+
+  // Constructor has to call protected constructor of base class
+  StructExprStruct (PathInExpression struct_path,
+		    std::vector<Attribute> inner_attribs,
+		    std::vector<Attribute> outer_attribs, Location locus)
+    : StructExpr (std::move (struct_path), std::move (outer_attribs)),
+      inner_attrs (std::move (inner_attribs)), locus (locus)
+  {}
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  StructExprStruct *clone_expr_without_block_impl () const override
+  {
+    return new StructExprStruct (*this);
+  }
+};
+
+/* AST node representing expression used to fill a struct's fields from another
+ * struct */
+struct StructBase
+{
+private:
+  std::unique_ptr<Expr> base_struct;
+  Location locus;
+
+public:
+  StructBase (std::unique_ptr<Expr> base_struct_ptr, Location locus)
+    : base_struct (std::move (base_struct_ptr)), locus (locus)
+  {}
+
+  // Copy constructor requires clone
+  StructBase (StructBase const &other)
+  {
+    /* HACK: gets around base_struct pointer being null (e.g. if no struct base
+     * exists) */
+    if (other.base_struct != nullptr)
+      base_struct = other.base_struct->clone_expr ();
+  }
+
+  // Destructor
+  ~StructBase () = default;
+
+  // Overload assignment operator to clone base_struct
+  StructBase &operator= (StructBase const &other)
+  {
+    // prevent null pointer dereference
+    if (other.base_struct != nullptr)
+      base_struct = other.base_struct->clone_expr ();
+    else
+      base_struct = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  StructBase (StructBase &&other) = default;
+  StructBase &operator= (StructBase &&other) = default;
+
+  // Returns a null expr-ed StructBase - error state
+  static StructBase error () { return StructBase (nullptr, Location ()); }
+
+  // Returns whether StructBase is in error state
+  bool is_invalid () const { return base_struct == nullptr; }
+
+  std::string as_string () const;
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_base_struct ()
+  {
+    rust_assert (base_struct != nullptr);
+    return base_struct;
+  }
+};
+
+/* Base AST node for a single struct expression field (in struct instance
+ * creation) - abstract */
+class StructExprField
+{
+public:
+  virtual ~StructExprField () {}
+
+  // Unique pointer custom clone function
+  std::unique_ptr<StructExprField> clone_struct_expr_field () const
+  {
+    return std::unique_ptr<StructExprField> (clone_struct_expr_field_impl ());
+  }
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (ASTVisitor &vis) = 0;
+
+  virtual Location get_locus () const = 0;
+
+  NodeId get_node_id () const { return node_id; }
+
+protected:
+  // pure virtual clone implementation
+  virtual StructExprField *clone_struct_expr_field_impl () const = 0;
+
+  StructExprField () : node_id (Analysis::Mappings::get ()->get_next_node_id ())
+  {}
+
+  NodeId node_id;
+};
+
+// Identifier-only variant of StructExprField AST node
+class StructExprFieldIdentifier : public StructExprField
+{
+  Identifier field_name;
+  Location locus;
+
+public:
+  StructExprFieldIdentifier (Identifier field_identifier, Location locus)
+    : StructExprField (), field_name (std::move (field_identifier)),
+      locus (locus)
+  {}
+
+  std::string as_string () const override { return field_name; }
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  Identifier get_field_name () const { return field_name; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  StructExprFieldIdentifier *clone_struct_expr_field_impl () const override
+  {
+    return new StructExprFieldIdentifier (*this);
+  }
+};
+
+/* Base AST node for a single struct expression field with an assigned value -
+ * abstract */
+class StructExprFieldWithVal : public StructExprField
+{
+  std::unique_ptr<Expr> value;
+
+protected:
+  StructExprFieldWithVal (std::unique_ptr<Expr> field_value)
+    : StructExprField (), value (std::move (field_value))
+  {}
+
+  // Copy constructor requires clone
+  StructExprFieldWithVal (StructExprFieldWithVal const &other)
+    : value (other.value->clone_expr ())
+  {}
+
+  // Overload assignment operator to clone unique_ptr
+  StructExprFieldWithVal &operator= (StructExprFieldWithVal const &other)
+  {
+    value = other.value->clone_expr ();
+
+    return *this;
+  }
+
+  // move constructors
+  StructExprFieldWithVal (StructExprFieldWithVal &&other) = default;
+  StructExprFieldWithVal &operator= (StructExprFieldWithVal &&other) = default;
+
+public:
+  std::string as_string () const override;
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_value ()
+  {
+    rust_assert (value != nullptr);
+    return value;
+  }
+};
+
+// Identifier and value variant of StructExprField AST node
+class StructExprFieldIdentifierValue : public StructExprFieldWithVal
+{
+  Identifier field_name;
+  Location locus;
+
+public:
+  StructExprFieldIdentifierValue (Identifier field_identifier,
+				  std::unique_ptr<Expr> field_value,
+				  Location locus)
+    : StructExprFieldWithVal (std::move (field_value)),
+      field_name (std::move (field_identifier)), locus (locus)
+  {}
+
+  std::string as_string () const override;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  std::string get_field_name () const { return field_name; }
+
+  Location get_locus () const override final { return locus; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  StructExprFieldIdentifierValue *clone_struct_expr_field_impl () const override
+  {
+    return new StructExprFieldIdentifierValue (*this);
+  }
+};
+
+// Tuple index and value variant of StructExprField AST node
+class StructExprFieldIndexValue : public StructExprFieldWithVal
+{
+  TupleIndex index;
+  Location locus;
+
+public:
+  StructExprFieldIndexValue (TupleIndex tuple_index,
+			     std::unique_ptr<Expr> field_value, Location locus)
+    : StructExprFieldWithVal (std::move (field_value)), index (tuple_index),
+      locus (locus)
+  {}
+
+  std::string as_string () const override;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  TupleIndex get_index () const { return index; }
+
+  Location get_locus () const override final { return locus; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  StructExprFieldIndexValue *clone_struct_expr_field_impl () const override
+  {
+    return new StructExprFieldIndexValue (*this);
+  }
+};
+
+// AST node of a struct creator with fields
+class StructExprStructFields : public StructExprStruct
+{
+  // std::vector<StructExprField> fields;
+  std::vector<std::unique_ptr<StructExprField> > fields;
+
+  // bool has_struct_base;
+  StructBase struct_base;
+
+public:
+  std::string as_string () const override;
+
+  bool has_struct_base () const { return !struct_base.is_invalid (); }
+
+  // Constructor for StructExprStructFields when no struct base is used
+  StructExprStructFields (
+    PathInExpression struct_path,
+    std::vector<std::unique_ptr<StructExprField> > expr_fields, Location locus,
+    StructBase base_struct = StructBase::error (),
+    std::vector<Attribute> inner_attribs = std::vector<Attribute> (),
+    std::vector<Attribute> outer_attribs = std::vector<Attribute> ())
+    : StructExprStruct (std::move (struct_path), std::move (inner_attribs),
+			std::move (outer_attribs), locus),
+      fields (std::move (expr_fields)), struct_base (std::move (base_struct))
+  {}
+
+  // copy constructor with vector clone
+  StructExprStructFields (StructExprStructFields const &other)
+    : StructExprStruct (other), struct_base (other.struct_base)
+  {
+    fields.reserve (other.fields.size ());
+    for (const auto &e : other.fields)
+      fields.push_back (e->clone_struct_expr_field ());
+  }
+
+  // overloaded assignment operator with vector clone
+  StructExprStructFields &operator= (StructExprStructFields const &other)
+  {
+    StructExprStruct::operator= (other);
+    struct_base = other.struct_base;
+
+    fields.reserve (other.fields.size ());
+    for (const auto &e : other.fields)
+      fields.push_back (e->clone_struct_expr_field ());
+
+    return *this;
+  }
+
+  // move constructors
+  StructExprStructFields (StructExprStructFields &&other) = default;
+  StructExprStructFields &operator= (StructExprStructFields &&other) = default;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: this mutable getter seems really dodgy. Think up better way.
+  std::vector<std::unique_ptr<StructExprField> > &get_fields ()
+  {
+    return fields;
+  }
+  const std::vector<std::unique_ptr<StructExprField> > &get_fields () const
+  {
+    return fields;
+  }
+
+  StructBase &get_struct_base () { return struct_base; }
+  const StructBase &get_struct_base () const { return struct_base; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  StructExprStructFields *clone_expr_without_block_impl () const override
+  {
+    return new StructExprStructFields (*this);
+  }
+};
+
+// AST node of the functional update struct creator
+/* TODO: remove and replace with StructExprStructFields, except with empty
+ * vector of fields? */
+class StructExprStructBase : public StructExprStruct
+{
+  StructBase struct_base;
+
+public:
+  std::string as_string () const override;
+
+  StructExprStructBase (PathInExpression struct_path, StructBase base_struct,
+			std::vector<Attribute> inner_attribs,
+			std::vector<Attribute> outer_attribs, Location locus)
+    : StructExprStruct (std::move (struct_path), std::move (inner_attribs),
+			std::move (outer_attribs), locus),
+      struct_base (std::move (base_struct))
+  {}
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  StructBase &get_struct_base () { return struct_base; }
+  const StructBase &get_struct_base () const { return struct_base; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  StructExprStructBase *clone_expr_without_block_impl () const override
+  {
+    return new StructExprStructBase (*this);
+  }
+};
+
+// Forward decl for Function - used in CallExpr
+class Function;
+
+// Function call expression AST node
+class CallExpr : public ExprWithoutBlock
+{
+  std::vector<Attribute> outer_attrs;
+  std::unique_ptr<Expr> function;
+  std::vector<std::unique_ptr<Expr> > params;
+  Location locus;
+
+public:
+  Function *fndeclRef;
+
+  std::string as_string () const override;
+
+  CallExpr (std::unique_ptr<Expr> function_expr,
+	    std::vector<std::unique_ptr<Expr> > function_params,
+	    std::vector<Attribute> outer_attribs, Location locus)
+    : outer_attrs (std::move (outer_attribs)),
+      function (std::move (function_expr)),
+      params (std::move (function_params)), locus (locus)
+  {}
+
+  // copy constructor requires clone
+  CallExpr (CallExpr const &other)
+    : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
+      locus (other.locus)
+  {
+    // guard to prevent null dereference (only required if error state)
+    if (other.function != nullptr)
+      function = other.function->clone_expr ();
+
+    params.reserve (other.params.size ());
+    for (const auto &e : other.params)
+      params.push_back (e->clone_expr ());
+  }
+
+  // Overload assignment operator to clone
+  CallExpr &operator= (CallExpr const &other)
+  {
+    ExprWithoutBlock::operator= (other);
+    locus = other.locus;
+    outer_attrs = other.outer_attrs;
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.function != nullptr)
+      function = other.function->clone_expr ();
+    else
+      function = nullptr;
+
+    params.reserve (other.params.size ());
+    for (const auto &e : other.params)
+      params.push_back (e->clone_expr ());
+
+    return *this;
+  }
+
+  // move constructors
+  CallExpr (CallExpr &&other) = default;
+  CallExpr &operator= (CallExpr &&other) = default;
+
+  // Returns whether function call has parameters.
+  bool has_params () const { return !params.empty (); }
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Invalid if function expr is null, so base stripping on that.
+  void mark_for_strip () override { function = nullptr; }
+  bool is_marked_for_strip () const override { return function == nullptr; }
+
+  // TODO: this mutable getter seems really dodgy. Think up better way.
+  const std::vector<std::unique_ptr<Expr> > &get_params () const
+  {
+    return params;
+  }
+  std::vector<std::unique_ptr<Expr> > &get_params () { return params; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_function_expr ()
+  {
+    rust_assert (function != nullptr);
+    return function;
+  }
+
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+  void set_outer_attrs (std::vector<Attribute> new_attrs) override
+  {
+    outer_attrs = std::move (new_attrs);
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  CallExpr *clone_expr_without_block_impl () const override
+  {
+    return new CallExpr (*this);
+  }
+};
+
+// Method call expression AST node
+class MethodCallExpr : public ExprWithoutBlock
+{
+  std::vector<Attribute> outer_attrs;
+  std::unique_ptr<Expr> receiver;
+  PathExprSegment method_name;
+  std::vector<std::unique_ptr<Expr> > params;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  MethodCallExpr (std::unique_ptr<Expr> call_receiver,
+		  PathExprSegment method_path,
+		  std::vector<std::unique_ptr<Expr> > method_params,
+		  std::vector<Attribute> outer_attribs, Location locus)
+    : outer_attrs (std::move (outer_attribs)),
+      receiver (std::move (call_receiver)),
+      method_name (std::move (method_path)), params (std::move (method_params)),
+      locus (locus)
+  {}
+
+  // copy constructor required due to cloning
+  MethodCallExpr (MethodCallExpr const &other)
+    : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
+      method_name (other.method_name), locus (other.locus)
+  {
+    // guard to prevent null dereference (only required if error state)
+    if (other.receiver != nullptr)
+      receiver = other.receiver->clone_expr ();
+
+    params.reserve (other.params.size ());
+    for (const auto &e : other.params)
+      params.push_back (e->clone_expr ());
+  }
+
+  // Overload assignment operator to clone receiver object
+  MethodCallExpr &operator= (MethodCallExpr const &other)
+  {
+    ExprWithoutBlock::operator= (other);
+    method_name = other.method_name;
+    locus = other.locus;
+    outer_attrs = other.outer_attrs;
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.receiver != nullptr)
+      receiver = other.receiver->clone_expr ();
+    else
+      receiver = nullptr;
+
+    params.reserve (other.params.size ());
+    for (const auto &e : other.params)
+      params.push_back (e->clone_expr ());
+
+    return *this;
+  }
+
+  // move constructors
+  MethodCallExpr (MethodCallExpr &&other) = default;
+  MethodCallExpr &operator= (MethodCallExpr &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Invalid if receiver expr is null, so base stripping on that.
+  void mark_for_strip () override { receiver = nullptr; }
+  bool is_marked_for_strip () const override { return receiver == nullptr; }
+
+  // TODO: this mutable getter seems really dodgy. Think up better way.
+  const std::vector<std::unique_ptr<Expr> > &get_params () const
+  {
+    return params;
+  }
+  std::vector<std::unique_ptr<Expr> > &get_params () { return params; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_receiver_expr ()
+  {
+    rust_assert (receiver != nullptr);
+    return receiver;
+  }
+
+  const PathExprSegment &get_method_name () const { return method_name; }
+  PathExprSegment &get_method_name () { return method_name; }
+
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+  void set_outer_attrs (std::vector<Attribute> new_attrs) override
+  {
+    outer_attrs = std::move (new_attrs);
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  MethodCallExpr *clone_expr_without_block_impl () const override
+  {
+    return new MethodCallExpr (*this);
+  }
+};
+
+// aka FieldExpression
+// Struct or union field access expression AST node
+class FieldAccessExpr : public ExprWithoutBlock
+{
+  std::vector<Attribute> outer_attrs;
+  std::unique_ptr<Expr> receiver;
+  Identifier field;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  FieldAccessExpr (std::unique_ptr<Expr> field_access_receiver,
+		   Identifier field_name, std::vector<Attribute> outer_attribs,
+		   Location locus)
+    : outer_attrs (std::move (outer_attribs)),
+      receiver (std::move (field_access_receiver)),
+      field (std::move (field_name)), locus (locus)
+  {}
+
+  // Copy constructor required due to unique_ptr cloning
+  FieldAccessExpr (FieldAccessExpr const &other)
+    : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
+      field (other.field), locus (other.locus)
+  {
+    // guard to prevent null dereference (only required if error state)
+    if (other.receiver != nullptr)
+      receiver = other.receiver->clone_expr ();
+  }
+
+  // Overload assignment operator to clone unique_ptr
+  FieldAccessExpr &operator= (FieldAccessExpr const &other)
+  {
+    ExprWithoutBlock::operator= (other);
+    field = other.field;
+    locus = other.locus;
+    outer_attrs = other.outer_attrs;
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.receiver != nullptr)
+      receiver = other.receiver->clone_expr ();
+    else
+      receiver = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  FieldAccessExpr (FieldAccessExpr &&other) = default;
+  FieldAccessExpr &operator= (FieldAccessExpr &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Invalid if receiver expr is null, so base stripping on that.
+  void mark_for_strip () override { receiver = nullptr; }
+  bool is_marked_for_strip () const override { return receiver == nullptr; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_receiver_expr ()
+  {
+    rust_assert (receiver != nullptr);
+    return receiver;
+  }
+
+  Identifier get_field_name () const { return field; }
+
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+  void set_outer_attrs (std::vector<Attribute> new_attrs) override
+  {
+    outer_attrs = std::move (new_attrs);
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  FieldAccessExpr *clone_expr_without_block_impl () const override
+  {
+    return new FieldAccessExpr (*this);
+  }
+};
+
+// Closure parameter data structure
+struct ClosureParam
+{
+private:
+  std::vector<Attribute> outer_attrs;
+  std::unique_ptr<Pattern> pattern;
+
+  // bool has_type_given;
+  std::unique_ptr<Type> type;
+  Location locus;
+
+public:
+  // Returns whether the type of the parameter has been given.
+  bool has_type_given () const { return type != nullptr; }
+
+  bool has_outer_attrs () const { return !outer_attrs.empty (); }
+
+  // Constructor for closure parameter
+  ClosureParam (std::unique_ptr<Pattern> param_pattern, Location locus,
+		std::unique_ptr<Type> param_type = nullptr,
+		std::vector<Attribute> outer_attrs = {})
+    : outer_attrs (std::move (outer_attrs)),
+      pattern (std::move (param_pattern)), type (std::move (param_type)),
+      locus (locus)
+  {}
+
+  // Copy constructor required due to cloning as a result of unique_ptrs
+  ClosureParam (ClosureParam const &other) : outer_attrs (other.outer_attrs)
+  {
+    // guard to protect from null pointer dereference
+    if (other.pattern != nullptr)
+      pattern = other.pattern->clone_pattern ();
+    if (other.type != nullptr)
+      type = other.type->clone_type ();
+  }
+
+  ~ClosureParam () = default;
+
+  // Assignment operator must be overloaded to clone as well
+  ClosureParam &operator= (ClosureParam const &other)
+  {
+    outer_attrs = other.outer_attrs;
+
+    // guard to protect from null pointer dereference
+    if (other.pattern != nullptr)
+      pattern = other.pattern->clone_pattern ();
+    else
+      pattern = nullptr;
+    if (other.type != nullptr)
+      type = other.type->clone_type ();
+    else
+      type = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  ClosureParam (ClosureParam &&other) = default;
+  ClosureParam &operator= (ClosureParam &&other) = default;
+
+  // Returns whether closure parameter is in an error state.
+  bool is_error () const { return pattern == nullptr; }
+
+  // Creates an error state closure parameter.
+  static ClosureParam create_error ()
+  {
+    return ClosureParam (nullptr, Location ());
+  }
+
+  std::string as_string () const;
+
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Pattern> &get_pattern ()
+  {
+    rust_assert (pattern != nullptr);
+    return pattern;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Type> &get_type ()
+  {
+    rust_assert (has_type_given ());
+    return type;
+  }
+};
+
+// Base closure definition expression AST node - abstract
+class ClosureExpr : public ExprWithoutBlock
+{
+  std::vector<Attribute> outer_attrs;
+  bool has_move;
+  std::vector<ClosureParam> params; // may be empty
+  Location locus;
+
+protected:
+  ClosureExpr (std::vector<ClosureParam> closure_params, bool has_move,
+	       std::vector<Attribute> outer_attribs, Location locus)
+    : outer_attrs (std::move (outer_attribs)), has_move (has_move),
+      params (std::move (closure_params)), locus (locus)
+  {}
+
+public:
+  std::string as_string () const override;
+
+  Location get_locus () const override final { return locus; }
+
+  // TODO: this mutable getter seems really dodgy. Think up better way.
+  const std::vector<ClosureParam> &get_params () const { return params; }
+  std::vector<ClosureParam> &get_params () { return params; }
+
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+  void set_outer_attrs (std::vector<Attribute> new_attrs) override
+  {
+    outer_attrs = std::move (new_attrs);
+  }
+};
+
+// Represents a non-type-specified closure expression AST node
+class ClosureExprInner : public ClosureExpr
+{
+  std::unique_ptr<Expr> closure_inner;
+
+public:
+  std::string as_string () const override;
+
+  // Constructor for a ClosureExprInner
+  ClosureExprInner (std::unique_ptr<Expr> closure_inner_expr,
+		    std::vector<ClosureParam> closure_params, Location locus,
+		    bool is_move = false,
+		    std::vector<Attribute> outer_attribs
+		    = std::vector<Attribute> ())
+    : ClosureExpr (std::move (closure_params), is_move,
+		   std::move (outer_attribs), locus),
+      closure_inner (std::move (closure_inner_expr))
+  {}
+
+  // Copy constructor must be defined to allow copying via cloning of unique_ptr
+  ClosureExprInner (ClosureExprInner const &other) : ClosureExpr (other)
+  {
+    // guard to prevent null dereference (only required if error state)
+    if (other.closure_inner != nullptr)
+      closure_inner = other.closure_inner->clone_expr ();
+  }
+
+  // Overload assignment operator to clone closure_inner
+  ClosureExprInner &operator= (ClosureExprInner const &other)
+  {
+    ClosureExpr::operator= (other);
+    // params = other.params;
+    // has_move = other.has_move;
+    // outer_attrs = other.outer_attrs;
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.closure_inner != nullptr)
+      closure_inner = other.closure_inner->clone_expr ();
+    else
+      closure_inner = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  ClosureExprInner (ClosureExprInner &&other) = default;
+  ClosureExprInner &operator= (ClosureExprInner &&other) = default;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Invalid if inner expr is null, so base stripping on that.
+  void mark_for_strip () override { closure_inner = nullptr; }
+  bool is_marked_for_strip () const override
+  {
+    return closure_inner == nullptr;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_definition_expr ()
+  {
+    rust_assert (closure_inner != nullptr);
+    return closure_inner;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ClosureExprInner *clone_expr_without_block_impl () const override
+  {
+    return new ClosureExprInner (*this);
+  }
+};
+
+// A block AST node
+class BlockExpr : public ExprWithBlock
+{
+  std::vector<Attribute> outer_attrs;
+  std::vector<Attribute> inner_attrs;
+  std::vector<std::unique_ptr<Stmt> > statements;
+  std::unique_ptr<Expr> expr;
+  Location start_locus;
+  Location end_locus;
+  bool marked_for_strip = false;
+
+public:
+  std::string as_string () const override;
+
+  // Returns whether the block contains statements.
+  bool has_statements () const { return !statements.empty (); }
+
+  // Returns whether the block contains a final expression.
+  bool has_tail_expr () const { return expr != nullptr; }
+
+  BlockExpr (std::vector<std::unique_ptr<Stmt> > block_statements,
+	     std::unique_ptr<Expr> block_expr,
+	     std::vector<Attribute> inner_attribs,
+	     std::vector<Attribute> outer_attribs, Location start_locus,
+	     Location end_locus)
+    : outer_attrs (std::move (outer_attribs)),
+      inner_attrs (std::move (inner_attribs)),
+      statements (std::move (block_statements)), expr (std::move (block_expr)),
+      start_locus (start_locus), end_locus (end_locus)
+  {}
+
+  // Copy constructor with clone
+  BlockExpr (BlockExpr const &other)
+    : ExprWithBlock (other), outer_attrs (other.outer_attrs),
+      inner_attrs (other.inner_attrs), start_locus (other.start_locus),
+      end_locus (other.end_locus), marked_for_strip (other.marked_for_strip)
+  {
+    // guard to protect from null pointer dereference
+    if (other.expr != nullptr)
+      expr = other.expr->clone_expr ();
+
+    statements.reserve (other.statements.size ());
+    for (const auto &e : other.statements)
+      statements.push_back (e->clone_stmt ());
+  }
+
+  // Overloaded assignment operator to clone pointer
+  BlockExpr &operator= (BlockExpr const &other)
+  {
+    ExprWithBlock::operator= (other);
+    inner_attrs = other.inner_attrs;
+    start_locus = other.start_locus;
+    end_locus = other.end_locus;
+    marked_for_strip = other.marked_for_strip;
+    outer_attrs = other.outer_attrs;
+
+    // guard to protect from null pointer dereference
+    if (other.expr != nullptr)
+      expr = other.expr->clone_expr ();
+    else
+      expr = nullptr;
+
+    statements.reserve (other.statements.size ());
+    for (const auto &e : other.statements)
+      statements.push_back (e->clone_stmt ());
+
+    return *this;
+  }
+
+  // move constructors
+  BlockExpr (BlockExpr &&other) = default;
+  BlockExpr &operator= (BlockExpr &&other) = default;
+
+  // Unique pointer custom clone function
+  std::unique_ptr<BlockExpr> clone_block_expr () const
+  {
+    return std::unique_ptr<BlockExpr> (clone_block_expr_impl ());
+  }
+
+  Location get_locus () const override final { return start_locus; }
+
+  Location get_start_locus () const { return start_locus; }
+  Location get_end_locus () const { return end_locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Can be completely empty, so have to have a separate flag.
+  void mark_for_strip () override { marked_for_strip = true; }
+  bool is_marked_for_strip () const override { return marked_for_strip; }
+
+  size_t num_statements () const { return statements.size (); }
+
+  // TODO: this mutable getter seems really dodgy. Think up better way.
+  const std::vector<Attribute> &get_inner_attrs () const { return inner_attrs; }
+  std::vector<Attribute> &get_inner_attrs () { return inner_attrs; }
+
+  const std::vector<std::unique_ptr<Stmt> > &get_statements () const
+  {
+    return statements;
+  }
+  std::vector<std::unique_ptr<Stmt> > &get_statements () { return statements; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_tail_expr ()
+  {
+    rust_assert (has_tail_expr ());
+    return expr;
+  }
+
+  // Removes the tail expression from the block.
+  void strip_tail_expr () { expr = nullptr; }
+
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+  void set_outer_attrs (std::vector<Attribute> new_attrs) override
+  {
+    outer_attrs = std::move (new_attrs);
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  BlockExpr *clone_expr_with_block_impl () const final override
+  {
+    return clone_block_expr_impl ();
+  }
+
+  /* This is the base method as not an abstract class - not virtual but could be
+   * in future if required. */
+  /*virtual*/ BlockExpr *clone_block_expr_impl () const
+  {
+    return new BlockExpr (*this);
+  }
+};
+
+// Represents a type-specified closure expression AST node
+class ClosureExprInnerTyped : public ClosureExpr
+{
+  // TODO: spec says typenobounds
+  std::unique_ptr<Type> return_type;
+  std::unique_ptr<BlockExpr>
+    expr; // only used because may be polymorphic in future
+
+public:
+  std::string as_string () const override;
+
+  // Constructor potentially with a move
+  ClosureExprInnerTyped (std::unique_ptr<Type> closure_return_type,
+			 std::unique_ptr<BlockExpr> closure_expr,
+			 std::vector<ClosureParam> closure_params,
+			 Location locus, bool is_move = false,
+			 std::vector<Attribute> outer_attribs
+			 = std::vector<Attribute> ())
+    : ClosureExpr (std::move (closure_params), is_move,
+		   std::move (outer_attribs), locus),
+      return_type (std::move (closure_return_type)),
+      expr (std::move (closure_expr))
+  {}
+
+  // Copy constructor requires cloning
+  ClosureExprInnerTyped (ClosureExprInnerTyped const &other)
+    : ClosureExpr (other)
+  {
+    // guard to prevent null dereference (only required if error state)
+    if (other.expr != nullptr)
+      expr = other.expr->clone_block_expr ();
+    if (other.return_type != nullptr)
+      return_type = other.return_type->clone_type ();
+  }
+
+  // Overload assignment operator to clone unique_ptrs
+  ClosureExprInnerTyped &operator= (ClosureExprInnerTyped const &other)
+  {
+    ClosureExpr::operator= (other);
+    // params = other.params;
+    // has_move = other.has_move;
+    // outer_attrs = other.outer_attrs;
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.expr != nullptr)
+      expr = other.expr->clone_block_expr ();
+    else
+      expr = nullptr;
+    if (other.return_type != nullptr)
+      return_type = other.return_type->clone_type ();
+    else
+      return_type = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  ClosureExprInnerTyped (ClosureExprInnerTyped &&other) = default;
+  ClosureExprInnerTyped &operator= (ClosureExprInnerTyped &&other) = default;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  /* Invalid if inner expr is null, so base stripping on that. Technically,
+   * type should also not be null. */
+  void mark_for_strip () override { expr = nullptr; }
+  bool is_marked_for_strip () const override { return expr == nullptr; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<BlockExpr> &get_definition_block ()
+  {
+    rust_assert (expr != nullptr);
+    return expr;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Type> &get_return_type ()
+  {
+    rust_assert (return_type != nullptr);
+    return return_type;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ClosureExprInnerTyped *clone_expr_without_block_impl () const override
+  {
+    return new ClosureExprInnerTyped (*this);
+  }
+};
+
+// AST node representing continue expression within loops
+class ContinueExpr : public ExprWithoutBlock
+{
+  std::vector<Attribute> outer_attrs;
+  Lifetime label;
+  Location locus;
+
+  // TODO: find another way to store this to save memory?
+  bool marked_for_strip = false;
+
+public:
+  std::string as_string () const override;
+
+  // Returns true if the continue expr has a label.
+  bool has_label () const { return !label.is_error (); }
+
+  // Constructor for a ContinueExpr with a label.
+  ContinueExpr (Lifetime label, std::vector<Attribute> outer_attribs,
+		Location locus)
+    : outer_attrs (std::move (outer_attribs)), label (std::move (label)),
+      locus (locus)
+  {}
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Can't think of any invalid invariants, so store boolean.
+  void mark_for_strip () override { marked_for_strip = true; }
+  bool is_marked_for_strip () const override { return marked_for_strip; }
+
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+  void set_outer_attrs (std::vector<Attribute> new_attrs) override
+  {
+    outer_attrs = std::move (new_attrs);
+  }
+
+  Lifetime &get_label () { return label; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ContinueExpr *clone_expr_without_block_impl () const override
+  {
+    return new ContinueExpr (*this);
+  }
+};
+// TODO: merge "break" and "continue"? Or even merge in "return"?
+
+// AST node representing break expression within loops
+class BreakExpr : public ExprWithoutBlock
+{
+  std::vector<Attribute> outer_attrs;
+  Lifetime label;
+  std::unique_ptr<Expr> break_expr;
+  Location locus;
+
+  // TODO: find another way to store this to save memory?
+  bool marked_for_strip = false;
+
+public:
+  std::string as_string () const override;
+
+  // Returns whether the break expression has a label or not.
+  bool has_label () const { return !label.is_error (); }
+
+  /* Returns whether the break expression has an expression used in the break or
+   * not. */
+  bool has_break_expr () const { return break_expr != nullptr; }
+
+  // Constructor for a break expression
+  BreakExpr (Lifetime break_label, std::unique_ptr<Expr> expr_in_break,
+	     std::vector<Attribute> outer_attribs, Location locus)
+    : outer_attrs (std::move (outer_attribs)), label (std::move (break_label)),
+      break_expr (std::move (expr_in_break)), locus (locus)
+  {}
+
+  // Copy constructor defined to use clone for unique pointer
+  BreakExpr (BreakExpr const &other)
+    : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
+      label (other.label), locus (other.locus),
+      marked_for_strip (other.marked_for_strip)
+  {
+    // guard to protect from null pointer dereference
+    if (other.break_expr != nullptr)
+      break_expr = other.break_expr->clone_expr ();
+  }
+
+  // Overload assignment operator to clone unique pointer
+  BreakExpr &operator= (BreakExpr const &other)
+  {
+    ExprWithoutBlock::operator= (other);
+    label = other.label;
+    locus = other.locus;
+    marked_for_strip = other.marked_for_strip;
+    outer_attrs = other.outer_attrs;
+
+    // guard to protect from null pointer dereference
+    if (other.break_expr != nullptr)
+      break_expr = other.break_expr->clone_expr ();
+    else
+      break_expr = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  BreakExpr (BreakExpr &&other) = default;
+  BreakExpr &operator= (BreakExpr &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Can't think of any invalid invariants, so store boolean.
+  void mark_for_strip () override { marked_for_strip = true; }
+  bool is_marked_for_strip () const override { return marked_for_strip; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_break_expr ()
+  {
+    rust_assert (has_break_expr ());
+    return break_expr;
+  }
+
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+  void set_outer_attrs (std::vector<Attribute> new_attrs) override
+  {
+    outer_attrs = std::move (new_attrs);
+  }
+
+  Lifetime &get_label () { return label; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  BreakExpr *clone_expr_without_block_impl () const override
+  {
+    return new BreakExpr (*this);
+  }
+};
+
+// Base range expression AST node object - abstract
+class RangeExpr : public ExprWithoutBlock
+{
+  Location locus;
+
+protected:
+  // outer attributes not allowed before range expressions
+  RangeExpr (Location locus) : locus (locus) {}
+
+public:
+  Location get_locus () const override final { return locus; }
+
+  // should never be called - error if called
+  void set_outer_attrs (std::vector<Attribute> /* new_attrs */) override
+  {
+    rust_assert (false);
+  }
+};
+
+// Range from (inclusive) and to (exclusive) expression AST node object
+// aka RangeExpr; constructs a std::ops::Range object
+class RangeFromToExpr : public RangeExpr
+{
+  std::unique_ptr<Expr> from;
+  std::unique_ptr<Expr> to;
+
+public:
+  std::string as_string () const override;
+
+  RangeFromToExpr (std::unique_ptr<Expr> range_from,
+		   std::unique_ptr<Expr> range_to, Location locus)
+    : RangeExpr (locus), from (std::move (range_from)),
+      to (std::move (range_to))
+  {}
+
+  // Copy constructor with cloning
+  RangeFromToExpr (RangeFromToExpr const &other) : RangeExpr (other)
+  {
+    // guard to prevent null dereference (only required if error state)
+    if (other.from != nullptr)
+      from = other.from->clone_expr ();
+    if (other.to != nullptr)
+      to = other.to->clone_expr ();
+  }
+
+  // Overload assignment operator to clone unique pointers
+  RangeFromToExpr &operator= (RangeFromToExpr const &other)
+  {
+    RangeExpr::operator= (other);
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.from != nullptr)
+      from = other.from->clone_expr ();
+    else
+      from = nullptr;
+    if (other.to != nullptr)
+      to = other.to->clone_expr ();
+    else
+      to = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  RangeFromToExpr (RangeFromToExpr &&other) = default;
+  RangeFromToExpr &operator= (RangeFromToExpr &&other) = default;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Invalid if either expr is null, so base stripping on that.
+  void mark_for_strip () override
+  {
+    from = nullptr;
+    to = nullptr;
+  }
+  bool is_marked_for_strip () const override
+  {
+    return from == nullptr && to == nullptr;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_from_expr ()
+  {
+    rust_assert (from != nullptr);
+    return from;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_to_expr ()
+  {
+    rust_assert (to != nullptr);
+    return to;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangeFromToExpr *clone_expr_without_block_impl () const override
+  {
+    return new RangeFromToExpr (*this);
+  }
+};
+
+// Range from (inclusive) expression AST node object
+// constructs a std::ops::RangeFrom object
+class RangeFromExpr : public RangeExpr
+{
+  std::unique_ptr<Expr> from;
+
+public:
+  std::string as_string () const override;
+
+  RangeFromExpr (std::unique_ptr<Expr> range_from, Location locus)
+    : RangeExpr (locus), from (std::move (range_from))
+  {}
+
+  // Copy constructor with clone
+  RangeFromExpr (RangeFromExpr const &other) : RangeExpr (other)
+  {
+    // guard to prevent null dereference (only required if error state)
+    if (other.from != nullptr)
+      from = other.from->clone_expr ();
+  }
+
+  // Overload assignment operator to clone unique_ptr
+  RangeFromExpr &operator= (RangeFromExpr const &other)
+  {
+    RangeExpr::operator= (other);
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.from != nullptr)
+      from = other.from->clone_expr ();
+    else
+      from = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  RangeFromExpr (RangeFromExpr &&other) = default;
+  RangeFromExpr &operator= (RangeFromExpr &&other) = default;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Invalid if expr is null, so base stripping on that.
+  void mark_for_strip () override { from = nullptr; }
+  bool is_marked_for_strip () const override { return from == nullptr; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_from_expr ()
+  {
+    rust_assert (from != nullptr);
+    return from;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangeFromExpr *clone_expr_without_block_impl () const override
+  {
+    return new RangeFromExpr (*this);
+  }
+};
+
+// Range to (exclusive) expression AST node object
+// constructs a std::ops::RangeTo object
+class RangeToExpr : public RangeExpr
+{
+  std::unique_ptr<Expr> to;
+
+public:
+  std::string as_string () const override;
+
+  // outer attributes not allowed
+  RangeToExpr (std::unique_ptr<Expr> range_to, Location locus)
+    : RangeExpr (locus), to (std::move (range_to))
+  {}
+
+  // Copy constructor with clone
+  RangeToExpr (RangeToExpr const &other) : RangeExpr (other)
+  {
+    // guard to prevent null dereference (only required if error state)
+    if (other.to != nullptr)
+      to = other.to->clone_expr ();
+  }
+
+  // Overload assignment operator to clone unique_ptr
+  RangeToExpr &operator= (RangeToExpr const &other)
+  {
+    RangeExpr::operator= (other);
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.to != nullptr)
+      to = other.to->clone_expr ();
+    else
+      to = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  RangeToExpr (RangeToExpr &&other) = default;
+  RangeToExpr &operator= (RangeToExpr &&other) = default;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Invalid if expr is null, so base stripping on that.
+  void mark_for_strip () override { to = nullptr; }
+  bool is_marked_for_strip () const override { return to == nullptr; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_to_expr ()
+  {
+    rust_assert (to != nullptr);
+    return to;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangeToExpr *clone_expr_without_block_impl () const override
+  {
+    return new RangeToExpr (*this);
+  }
+};
+
+// Full range expression AST node object
+// constructs a std::ops::RangeFull object
+class RangeFullExpr : public RangeExpr
+{
+  // TODO: find another way to store this to save memory?
+  bool marked_for_strip = false;
+
+public:
+  std::string as_string () const override;
+
+  RangeFullExpr (Location locus) : RangeExpr (locus) {}
+  // outer attributes not allowed
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Can't think of any invalid invariants, so store boolean.
+  void mark_for_strip () override { marked_for_strip = true; }
+  bool is_marked_for_strip () const override { return marked_for_strip; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangeFullExpr *clone_expr_without_block_impl () const override
+  {
+    return new RangeFullExpr (*this);
+  }
+};
+
+// Range from (inclusive) and to (inclusive) expression AST node object
+// aka RangeInclusiveExpr; constructs a std::ops::RangeInclusive object
+class RangeFromToInclExpr : public RangeExpr
+{
+  std::unique_ptr<Expr> from;
+  std::unique_ptr<Expr> to;
+
+public:
+  std::string as_string () const override;
+
+  RangeFromToInclExpr (std::unique_ptr<Expr> range_from,
+		       std::unique_ptr<Expr> range_to, Location locus)
+    : RangeExpr (locus), from (std::move (range_from)),
+      to (std::move (range_to))
+  {}
+  // outer attributes not allowed
+
+  // Copy constructor with clone
+  RangeFromToInclExpr (RangeFromToInclExpr const &other) : RangeExpr (other)
+  {
+    // guard to prevent null dereference (only required if error state)
+    if (other.from != nullptr)
+      from = other.from->clone_expr ();
+    if (other.to != nullptr)
+      to = other.to->clone_expr ();
+  }
+
+  // Overload assignment operator to use clone
+  RangeFromToInclExpr &operator= (RangeFromToInclExpr const &other)
+  {
+    RangeExpr::operator= (other);
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.from != nullptr)
+      from = other.from->clone_expr ();
+    else
+      from = nullptr;
+    if (other.to != nullptr)
+      to = other.to->clone_expr ();
+    else
+      to = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  RangeFromToInclExpr (RangeFromToInclExpr &&other) = default;
+  RangeFromToInclExpr &operator= (RangeFromToInclExpr &&other) = default;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Invalid if either expr is null, so base stripping on that.
+  void mark_for_strip () override
+  {
+    from = nullptr;
+    to = nullptr;
+  }
+  bool is_marked_for_strip () const override
+  {
+    return from == nullptr && to == nullptr;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_from_expr ()
+  {
+    rust_assert (from != nullptr);
+    return from;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_to_expr ()
+  {
+    rust_assert (to != nullptr);
+    return to;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangeFromToInclExpr *clone_expr_without_block_impl () const override
+  {
+    return new RangeFromToInclExpr (*this);
+  }
+};
+
+// Range to (inclusive) expression AST node object
+// aka RangeToInclusiveExpr; constructs a std::ops::RangeToInclusive object
+class RangeToInclExpr : public RangeExpr
+{
+  std::unique_ptr<Expr> to;
+
+public:
+  std::string as_string () const override;
+
+  RangeToInclExpr (std::unique_ptr<Expr> range_to, Location locus)
+    : RangeExpr (locus), to (std::move (range_to))
+  {}
+  // outer attributes not allowed
+
+  // Copy constructor with clone
+  RangeToInclExpr (RangeToInclExpr const &other) : RangeExpr (other)
+  {
+    // guard to prevent null dereference (only required if error state)
+    if (other.to != nullptr)
+      to = other.to->clone_expr ();
+  }
+
+  // Overload assignment operator to clone pointer
+  RangeToInclExpr &operator= (RangeToInclExpr const &other)
+  {
+    RangeExpr::operator= (other);
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.to != nullptr)
+      to = other.to->clone_expr ();
+    else
+      to = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  RangeToInclExpr (RangeToInclExpr &&other) = default;
+  RangeToInclExpr &operator= (RangeToInclExpr &&other) = default;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Invalid if expr is null, so base stripping on that.
+  void mark_for_strip () override { to = nullptr; }
+  bool is_marked_for_strip () const override { return to == nullptr; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_to_expr ()
+  {
+    rust_assert (to != nullptr);
+    return to;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangeToInclExpr *clone_expr_without_block_impl () const override
+  {
+    return new RangeToInclExpr (*this);
+  }
+};
+
+// Return expression AST node representation
+class ReturnExpr : public ExprWithoutBlock
+{
+  std::vector<Attribute> outer_attrs;
+  std::unique_ptr<Expr> return_expr;
+  Location locus;
+
+  // TODO: find another way to store this to save memory?
+  bool marked_for_strip = false;
+
+public:
+  std::string as_string () const override;
+
+  /* Returns whether the object has an expression returned (i.e. not void return
+   * type). */
+  bool has_returned_expr () const { return return_expr != nullptr; }
+
+  // Constructor for ReturnExpr.
+  ReturnExpr (std::unique_ptr<Expr> returned_expr,
+	      std::vector<Attribute> outer_attribs, Location locus)
+    : outer_attrs (std::move (outer_attribs)),
+      return_expr (std::move (returned_expr)), locus (locus)
+  {}
+
+  // Copy constructor with clone
+  ReturnExpr (ReturnExpr const &other)
+    : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
+      locus (other.locus), marked_for_strip (other.marked_for_strip)
+  {
+    // guard to protect from null pointer dereference
+    if (other.return_expr != nullptr)
+      return_expr = other.return_expr->clone_expr ();
+  }
+
+  // Overloaded assignment operator to clone return_expr pointer
+  ReturnExpr &operator= (ReturnExpr const &other)
+  {
+    ExprWithoutBlock::operator= (other);
+    locus = other.locus;
+    marked_for_strip = other.marked_for_strip;
+    outer_attrs = other.outer_attrs;
+
+    // guard to protect from null pointer dereference
+    if (other.return_expr != nullptr)
+      return_expr = other.return_expr->clone_expr ();
+    else
+      return_expr = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  ReturnExpr (ReturnExpr &&other) = default;
+  ReturnExpr &operator= (ReturnExpr &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Can't think of any invalid invariants, so store boolean.
+  void mark_for_strip () override { marked_for_strip = true; }
+  bool is_marked_for_strip () const override { return marked_for_strip; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_returned_expr ()
+  {
+    rust_assert (return_expr != nullptr);
+    return return_expr;
+  }
+
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+  void set_outer_attrs (std::vector<Attribute> new_attrs) override
+  {
+    outer_attrs = std::move (new_attrs);
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ReturnExpr *clone_expr_without_block_impl () const override
+  {
+    return new ReturnExpr (*this);
+  }
+};
+
+// Forward decl - defined in rust-macro.h
+class MacroInvocation;
+
+// An unsafe block AST node
+class UnsafeBlockExpr : public ExprWithBlock
+{
+  std::vector<Attribute> outer_attrs;
+  // Or just have it extend BlockExpr
+  std::unique_ptr<BlockExpr> expr;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  UnsafeBlockExpr (std::unique_ptr<BlockExpr> block_expr,
+		   std::vector<Attribute> outer_attribs, Location locus)
+    : outer_attrs (std::move (outer_attribs)), expr (std::move (block_expr)),
+      locus (locus)
+  {}
+
+  // Copy constructor with clone
+  UnsafeBlockExpr (UnsafeBlockExpr const &other)
+    : ExprWithBlock (other), outer_attrs (other.outer_attrs),
+      locus (other.locus)
+  {
+    // guard to prevent null dereference (only required if error state)
+    if (other.expr != nullptr)
+      expr = other.expr->clone_block_expr ();
+  }
+
+  // Overloaded assignment operator to clone
+  UnsafeBlockExpr &operator= (UnsafeBlockExpr const &other)
+  {
+    ExprWithBlock::operator= (other);
+    locus = other.locus;
+    outer_attrs = other.outer_attrs;
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.expr != nullptr)
+      expr = other.expr->clone_block_expr ();
+    else
+      expr = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  UnsafeBlockExpr (UnsafeBlockExpr &&other) = default;
+  UnsafeBlockExpr &operator= (UnsafeBlockExpr &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Invalid if block is null, so base stripping on that.
+  void mark_for_strip () override { expr = nullptr; }
+  bool is_marked_for_strip () const override { return expr == nullptr; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<BlockExpr> &get_block_expr ()
+  {
+    rust_assert (expr != nullptr);
+    return expr;
+  }
+
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+  void set_outer_attrs (std::vector<Attribute> new_attrs) override
+  {
+    outer_attrs = std::move (new_attrs);
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  UnsafeBlockExpr *clone_expr_with_block_impl () const override
+  {
+    return new UnsafeBlockExpr (*this);
+  }
+};
+
+// Loop label expression AST node used with break and continue expressions
+// TODO: inline?
+class LoopLabel /*: public Node*/
+{
+  Lifetime label; // or type LIFETIME_OR_LABEL
+  Location locus;
+
+  NodeId node_id;
+
+public:
+  std::string as_string () const;
+
+  LoopLabel (Lifetime loop_label, Location locus = Location ())
+    : label (std::move (loop_label)), locus (locus),
+      node_id (Analysis::Mappings::get ()->get_next_node_id ())
+  {}
+
+  // Returns whether the LoopLabel is in an error state.
+  bool is_error () const { return label.is_error (); }
+
+  // Creates an error state LoopLabel.
+  static LoopLabel error () { return LoopLabel (Lifetime::error ()); }
+
+  Location get_locus () const { return locus; }
+
+  Lifetime &get_lifetime () { return label; }
+
+  NodeId get_node_id () const { return node_id; }
+};
+
+// Base loop expression AST node - aka LoopExpr
+class BaseLoopExpr : public ExprWithBlock
+{
+protected:
+  // protected to allow subclasses better use of them
+  std::vector<Attribute> outer_attrs;
+  LoopLabel loop_label;
+  std::unique_ptr<BlockExpr> loop_block;
+
+private:
+  Location locus;
+
+protected:
+  // Constructor for BaseLoopExpr
+  BaseLoopExpr (std::unique_ptr<BlockExpr> loop_block, Location locus,
+		LoopLabel loop_label = LoopLabel::error (),
+		std::vector<Attribute> outer_attribs
+		= std::vector<Attribute> ())
+    : outer_attrs (std::move (outer_attribs)),
+      loop_label (std::move (loop_label)), loop_block (std::move (loop_block)),
+      locus (locus)
+  {}
+
+  // Copy constructor for BaseLoopExpr with clone
+  BaseLoopExpr (BaseLoopExpr const &other)
+    : ExprWithBlock (other), outer_attrs (other.outer_attrs),
+      loop_label (other.loop_label), locus (other.locus)
+  {
+    // guard to prevent null dereference (only required if error state)
+    if (other.loop_block != nullptr)
+      loop_block = other.loop_block->clone_block_expr ();
+  }
+
+  // Overloaded assignment operator to clone
+  BaseLoopExpr &operator= (BaseLoopExpr const &other)
+  {
+    ExprWithBlock::operator= (other);
+    loop_label = other.loop_label;
+    locus = other.locus;
+    outer_attrs = other.outer_attrs;
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.loop_block != nullptr)
+      loop_block = other.loop_block->clone_block_expr ();
+    else
+      loop_block = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  BaseLoopExpr (BaseLoopExpr &&other) = default;
+  BaseLoopExpr &operator= (BaseLoopExpr &&other) = default;
+
+public:
+  bool has_loop_label () const { return !loop_label.is_error (); }
+
+  LoopLabel &get_loop_label () { return loop_label; }
+
+  Location get_locus () const override final { return locus; }
+
+  // Invalid if loop block is null, so base stripping on that.
+  void mark_for_strip () override { loop_block = nullptr; }
+  bool is_marked_for_strip () const override { return loop_block == nullptr; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<BlockExpr> &get_loop_block ()
+  {
+    rust_assert (loop_block != nullptr);
+    return loop_block;
+  }
+
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+  void set_outer_attrs (std::vector<Attribute> new_attrs) override
+  {
+    outer_attrs = std::move (new_attrs);
+  }
+};
+
+// 'Loop' expression (i.e. the infinite loop) AST node
+class LoopExpr : public BaseLoopExpr
+{
+public:
+  std::string as_string () const override;
+
+  // Constructor for LoopExpr
+  LoopExpr (std::unique_ptr<BlockExpr> loop_block, Location locus,
+	    LoopLabel loop_label = LoopLabel::error (),
+	    std::vector<Attribute> outer_attribs = std::vector<Attribute> ())
+    : BaseLoopExpr (std::move (loop_block), locus, std::move (loop_label),
+		    std::move (outer_attribs))
+  {}
+
+  void accept_vis (ASTVisitor &vis) override;
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  LoopExpr *clone_expr_with_block_impl () const override
+  {
+    return new LoopExpr (*this);
+  }
+};
+
+// While loop expression AST node (predicate loop)
+class WhileLoopExpr : public BaseLoopExpr
+{
+  std::unique_ptr<Expr> condition;
+
+public:
+  std::string as_string () const override;
+
+  // Constructor for while loop with loop label
+  WhileLoopExpr (std::unique_ptr<Expr> loop_condition,
+		 std::unique_ptr<BlockExpr> loop_block, Location locus,
+		 LoopLabel loop_label = LoopLabel::error (),
+		 std::vector<Attribute> outer_attribs
+		 = std::vector<Attribute> ())
+    : BaseLoopExpr (std::move (loop_block), locus, std::move (loop_label),
+		    std::move (outer_attribs)),
+      condition (std::move (loop_condition))
+  {}
+
+  // Copy constructor with clone
+  WhileLoopExpr (WhileLoopExpr const &other)
+    : BaseLoopExpr (other), condition (other.condition->clone_expr ())
+  {}
+
+  // Overloaded assignment operator to clone
+  WhileLoopExpr &operator= (WhileLoopExpr const &other)
+  {
+    BaseLoopExpr::operator= (other);
+    condition = other.condition->clone_expr ();
+    // loop_block = other.loop_block->clone_block_expr();
+    // loop_label = other.loop_label;
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  WhileLoopExpr (WhileLoopExpr &&other) = default;
+  WhileLoopExpr &operator= (WhileLoopExpr &&other) = default;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_predicate_expr ()
+  {
+    rust_assert (condition != nullptr);
+    return condition;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  WhileLoopExpr *clone_expr_with_block_impl () const override
+  {
+    return new WhileLoopExpr (*this);
+  }
+};
+
+// While let loop expression AST node (predicate pattern loop)
+class WhileLetLoopExpr : public BaseLoopExpr
+{
+  // MatchArmPatterns patterns;
+  std::vector<std::unique_ptr<Pattern> > match_arm_patterns; // inlined
+  std::unique_ptr<Expr> scrutinee;
+
+public:
+  std::string as_string () const override;
+
+  // Constructor with a loop label
+  WhileLetLoopExpr (std::vector<std::unique_ptr<Pattern> > match_arm_patterns,
+		    std::unique_ptr<Expr> scrutinee,
+		    std::unique_ptr<BlockExpr> loop_block, Location locus,
+		    LoopLabel loop_label = LoopLabel::error (),
+		    std::vector<Attribute> outer_attribs
+		    = std::vector<Attribute> ())
+    : BaseLoopExpr (std::move (loop_block), locus, std::move (loop_label),
+		    std::move (outer_attribs)),
+      match_arm_patterns (std::move (match_arm_patterns)),
+      scrutinee (std::move (scrutinee))
+  {}
+
+  // Copy constructor with clone
+  WhileLetLoopExpr (WhileLetLoopExpr const &other)
+    : BaseLoopExpr (other),
+      /*match_arm_patterns(other.match_arm_patterns),*/ scrutinee (
+	other.scrutinee->clone_expr ())
+  {
+    match_arm_patterns.reserve (other.match_arm_patterns.size ());
+    for (const auto &e : other.match_arm_patterns)
+      match_arm_patterns.push_back (e->clone_pattern ());
+  }
+
+  // Overloaded assignment operator to clone pointers
+  WhileLetLoopExpr &operator= (WhileLetLoopExpr const &other)
+  {
+    BaseLoopExpr::operator= (other);
+    // match_arm_patterns = other.match_arm_patterns;
+    scrutinee = other.scrutinee->clone_expr ();
+    // loop_block = other.loop_block->clone_block_expr();
+    // loop_label = other.loop_label;
+    // outer_attrs = other.outer_attrs;
+
+    match_arm_patterns.reserve (other.match_arm_patterns.size ());
+    for (const auto &e : other.match_arm_patterns)
+      match_arm_patterns.push_back (e->clone_pattern ());
+
+    return *this;
+  }
+
+  // move constructors
+  WhileLetLoopExpr (WhileLetLoopExpr &&other) = default;
+  WhileLetLoopExpr &operator= (WhileLetLoopExpr &&other) = default;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_scrutinee_expr ()
+  {
+    rust_assert (scrutinee != nullptr);
+    return scrutinee;
+  }
+
+  // TODO: this mutable getter seems really dodgy. Think up better way.
+  const std::vector<std::unique_ptr<Pattern> > &get_patterns () const
+  {
+    return match_arm_patterns;
+  }
+  std::vector<std::unique_ptr<Pattern> > &get_patterns ()
+  {
+    return match_arm_patterns;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  WhileLetLoopExpr *clone_expr_with_block_impl () const override
+  {
+    return new WhileLetLoopExpr (*this);
+  }
+};
+
+// For loop expression AST node (iterator loop)
+class ForLoopExpr : public BaseLoopExpr
+{
+  std::unique_ptr<Pattern> pattern;
+  std::unique_ptr<Expr> iterator_expr;
+
+public:
+  std::string as_string () const override;
+
+  // Constructor with loop label
+  ForLoopExpr (std::unique_ptr<Pattern> loop_pattern,
+	       std::unique_ptr<Expr> iterator_expr,
+	       std::unique_ptr<BlockExpr> loop_body, Location locus,
+	       LoopLabel loop_label = LoopLabel::error (),
+	       std::vector<Attribute> outer_attribs = std::vector<Attribute> ())
+    : BaseLoopExpr (std::move (loop_body), locus, std::move (loop_label),
+		    std::move (outer_attribs)),
+      pattern (std::move (loop_pattern)),
+      iterator_expr (std::move (iterator_expr))
+  {}
+
+  // Copy constructor with clone
+  ForLoopExpr (ForLoopExpr const &other)
+    : BaseLoopExpr (other), pattern (other.pattern->clone_pattern ()),
+      iterator_expr (other.iterator_expr->clone_expr ())
+  {}
+
+  // Overloaded assignment operator to clone
+  ForLoopExpr &operator= (ForLoopExpr const &other)
+  {
+    BaseLoopExpr::operator= (other);
+    pattern = other.pattern->clone_pattern ();
+    iterator_expr = other.iterator_expr->clone_expr ();
+    /*loop_block = other.loop_block->clone_block_expr();
+    loop_label = other.loop_label;
+    outer_attrs = other.outer_attrs;*/
+
+    return *this;
+  }
+
+  // move constructors
+  ForLoopExpr (ForLoopExpr &&other) = default;
+  ForLoopExpr &operator= (ForLoopExpr &&other) = default;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_iterator_expr ()
+  {
+    rust_assert (iterator_expr != nullptr);
+    return iterator_expr;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Pattern> &get_pattern ()
+  {
+    rust_assert (pattern != nullptr);
+    return pattern;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ForLoopExpr *clone_expr_with_block_impl () const override
+  {
+    return new ForLoopExpr (*this);
+  }
+};
+
+// forward decl for IfExpr
+class IfLetExpr;
+
+// Base if expression with no "else" or "if let" AST node
+class IfExpr : public ExprWithBlock
+{
+  std::vector<Attribute> outer_attrs;
+  std::unique_ptr<Expr> condition;
+  std::unique_ptr<BlockExpr> if_block;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  IfExpr (std::unique_ptr<Expr> condition, std::unique_ptr<BlockExpr> if_block,
+	  std::vector<Attribute> outer_attrs, Location locus)
+    : outer_attrs (std::move (outer_attrs)), condition (std::move (condition)),
+      if_block (std::move (if_block)), locus (locus)
+  {}
+  // outer attributes are never allowed on IfExprs
+
+  // Copy constructor with clone
+  IfExpr (IfExpr const &other)
+    : ExprWithBlock (other), outer_attrs (other.outer_attrs),
+      locus (other.locus)
+  {
+    // guard to prevent null dereference (only required if error state)
+    if (other.condition != nullptr)
+      condition = other.condition->clone_expr ();
+    if (other.if_block != nullptr)
+      if_block = other.if_block->clone_block_expr ();
+  }
+
+  // Overloaded assignment operator to clone expressions
+  IfExpr &operator= (IfExpr const &other)
+  {
+    ExprWithBlock::operator= (other);
+    outer_attrs = other.outer_attrs;
+    locus = other.locus;
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.condition != nullptr)
+      condition = other.condition->clone_expr ();
+    else
+      condition = nullptr;
+    if (other.if_block != nullptr)
+      if_block = other.if_block->clone_block_expr ();
+    else
+      if_block = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  IfExpr (IfExpr &&other) = default;
+  IfExpr &operator= (IfExpr &&other) = default;
+
+  // Unique pointer custom clone function
+  std::unique_ptr<IfExpr> clone_if_expr () const
+  {
+    return std::unique_ptr<IfExpr> (clone_if_expr_impl ());
+  }
+
+  /* Note that multiple "else if"s are handled via nested ASTs rather than a
+   * vector of else ifs - i.e. not like a switch statement. TODO - is this a
+   * better approach? or does it not parse correctly and have downsides? */
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  void vis_if_condition (ASTVisitor &vis) { condition->accept_vis (vis); }
+  void vis_if_block (ASTVisitor &vis) { if_block->accept_vis (vis); }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_condition_expr ()
+  {
+    rust_assert (condition != nullptr);
+    return condition;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<BlockExpr> &get_if_block ()
+  {
+    rust_assert (if_block != nullptr);
+    return if_block;
+  }
+
+  // Invalid if if block or condition is null, so base stripping on that.
+  void mark_for_strip () override
+  {
+    if_block = nullptr;
+    condition = nullptr;
+  }
+  bool is_marked_for_strip () const override
+  {
+    return if_block == nullptr && condition == nullptr;
+  }
+
+  void set_outer_attrs (std::vector<Attribute> new_attrs) override
+  {
+    outer_attrs = std::move (new_attrs);
+  }
+
+  // TODO: this mutable getter seems really dodgy. Think up better way.
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+protected:
+  // Base clone function but still concrete as concrete base class
+  virtual IfExpr *clone_if_expr_impl () const { return new IfExpr (*this); }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfExpr *clone_expr_with_block_impl () const final override
+  {
+    return clone_if_expr_impl ();
+  }
+};
+
+// If expression with an ending "else" expression AST node (trailing)
+class IfExprConseqElse : public IfExpr
+{
+  std::unique_ptr<BlockExpr> else_block;
+
+public:
+  std::string as_string () const override;
+
+  IfExprConseqElse (std::unique_ptr<Expr> condition,
+		    std::unique_ptr<BlockExpr> if_block,
+		    std::unique_ptr<BlockExpr> else_block,
+		    std::vector<Attribute> outer_attrs, Location locus)
+    : IfExpr (std::move (condition), std::move (if_block),
+	      std::move (outer_attrs), locus),
+      else_block (std::move (else_block))
+  {}
+  // again, outer attributes not allowed
+
+  // Copy constructor with clone
+  IfExprConseqElse (IfExprConseqElse const &other)
+    : IfExpr (other), else_block (other.else_block->clone_block_expr ())
+  {}
+
+  // Overloaded assignment operator with cloning
+  IfExprConseqElse &operator= (IfExprConseqElse const &other)
+  {
+    IfExpr::operator= (other);
+    // condition = other.condition->clone_expr();
+    // if_block = other.if_block->clone_block_expr();
+    else_block = other.else_block->clone_block_expr ();
+
+    return *this;
+  }
+
+  // move constructors
+  IfExprConseqElse (IfExprConseqElse &&other) = default;
+  IfExprConseqElse &operator= (IfExprConseqElse &&other) = default;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  void vis_else_block (ASTVisitor &vis) { else_block->accept_vis (vis); }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<BlockExpr> &get_else_block ()
+  {
+    rust_assert (else_block != nullptr);
+    return else_block;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfExprConseqElse *clone_if_expr_impl () const override
+  {
+    return new IfExprConseqElse (*this);
+  }
+};
+
+// If expression with an ending "else if" expression AST node
+class IfExprConseqIf : public IfExpr
+{
+  std::unique_ptr<IfExpr> conseq_if_expr;
+
+public:
+  std::string as_string () const override;
+
+  IfExprConseqIf (std::unique_ptr<Expr> condition,
+		  std::unique_ptr<BlockExpr> if_block,
+		  std::unique_ptr<IfExpr> conseq_if_expr,
+		  std::vector<Attribute> outer_attrs, Location locus)
+    : IfExpr (std::move (condition), std::move (if_block),
+	      std::move (outer_attrs), locus),
+      conseq_if_expr (std::move (conseq_if_expr))
+  {}
+  // outer attributes not allowed
+
+  // Copy constructor with clone
+  IfExprConseqIf (IfExprConseqIf const &other)
+    : IfExpr (other), conseq_if_expr (other.conseq_if_expr->clone_if_expr ())
+  {}
+
+  // Overloaded assignment operator to use clone
+  IfExprConseqIf &operator= (IfExprConseqIf const &other)
+  {
+    IfExpr::operator= (other);
+    // condition = other.condition->clone_expr();
+    // if_block = other.if_block->clone_block_expr();
+    conseq_if_expr = other.conseq_if_expr->clone_if_expr ();
+
+    return *this;
+  }
+
+  // move constructors
+  IfExprConseqIf (IfExprConseqIf &&other) = default;
+  IfExprConseqIf &operator= (IfExprConseqIf &&other) = default;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  void vis_conseq_if_expr (ASTVisitor &vis)
+  {
+    conseq_if_expr->accept_vis (vis);
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<IfExpr> &get_conseq_if_expr ()
+  {
+    rust_assert (conseq_if_expr != nullptr);
+    return conseq_if_expr;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfExprConseqIf *clone_if_expr_impl () const override
+  {
+    return new IfExprConseqIf (*this);
+  }
+};
+
+// Basic "if let" expression AST node with no else
+class IfLetExpr : public ExprWithBlock
+{
+  std::vector<Attribute> outer_attrs;
+  std::vector<std::unique_ptr<Pattern> > match_arm_patterns; // inlined
+  std::unique_ptr<Expr> value;
+  std::unique_ptr<BlockExpr> if_block;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  IfLetExpr (std::vector<std::unique_ptr<Pattern> > match_arm_patterns,
+	     std::unique_ptr<Expr> value, std::unique_ptr<BlockExpr> if_block,
+	     std::vector<Attribute> outer_attrs, Location locus)
+    : outer_attrs (std::move (outer_attrs)),
+      match_arm_patterns (std::move (match_arm_patterns)),
+      value (std::move (value)), if_block (std::move (if_block)), locus (locus)
+  {}
+
+  // copy constructor with clone
+  IfLetExpr (IfLetExpr const &other)
+    : ExprWithBlock (other), outer_attrs (other.outer_attrs),
+      locus (other.locus)
+  {
+    // guard to prevent null dereference (only required if error state)
+    if (other.value != nullptr)
+      value = other.value->clone_expr ();
+    if (other.if_block != nullptr)
+      if_block = other.if_block->clone_block_expr ();
+
+    match_arm_patterns.reserve (other.match_arm_patterns.size ());
+    for (const auto &e : other.match_arm_patterns)
+      match_arm_patterns.push_back (e->clone_pattern ());
+  }
+
+  // overload assignment operator to clone
+  IfLetExpr &operator= (IfLetExpr const &other)
+  {
+    ExprWithBlock::operator= (other);
+    outer_attrs = other.outer_attrs;
+    locus = other.locus;
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.value != nullptr)
+      value = other.value->clone_expr ();
+    else
+      value = nullptr;
+    if (other.if_block != nullptr)
+      if_block = other.if_block->clone_block_expr ();
+    else
+      if_block = nullptr;
+
+    match_arm_patterns.reserve (other.match_arm_patterns.size ());
+    for (const auto &e : other.match_arm_patterns)
+      match_arm_patterns.push_back (e->clone_pattern ());
+
+    return *this;
+  }
+
+  // move constructors
+  IfLetExpr (IfLetExpr &&other) = default;
+  IfLetExpr &operator= (IfLetExpr &&other) = default;
+
+  // Unique pointer custom clone function
+  std::unique_ptr<IfLetExpr> clone_if_let_expr () const
+  {
+    return std::unique_ptr<IfLetExpr> (clone_if_let_expr_impl ());
+  }
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Invalid if block or value is null, so base stripping on that.
+  void mark_for_strip () override
+  {
+    if_block = nullptr;
+    value = nullptr;
+  }
+  bool is_marked_for_strip () const override
+  {
+    return if_block == nullptr && value == nullptr;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_value_expr ()
+  {
+    rust_assert (value != nullptr);
+    return value;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<BlockExpr> &get_if_block ()
+  {
+    rust_assert (if_block != nullptr);
+    return if_block;
+  }
+
+  // TODO: this mutable getter seems really dodgy. Think up better way.
+  const std::vector<std::unique_ptr<Pattern> > &get_patterns () const
+  {
+    return match_arm_patterns;
+  }
+  std::vector<std::unique_ptr<Pattern> > &get_patterns ()
+  {
+    return match_arm_patterns;
+  }
+
+  void set_outer_attrs (std::vector<Attribute> new_attrs) override
+  {
+    outer_attrs = std::move (new_attrs);
+  }
+
+  // TODO: this mutable getter seems really dodgy. Think up better way.
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base (or rather this or any derived object) */
+  IfLetExpr *clone_expr_with_block_impl () const final override
+  {
+    return clone_if_let_expr_impl ();
+  }
+
+  // Base clone function but still concrete as concrete base class
+  virtual IfLetExpr *clone_if_let_expr_impl () const
+  {
+    return new IfLetExpr (*this);
+  }
+};
+
+// If expression with an ending "else if let" expression AST node
+class IfExprConseqIfLet : public IfExpr
+{
+  std::unique_ptr<IfLetExpr> if_let_expr;
+
+public:
+  std::string as_string () const override;
+
+  IfExprConseqIfLet (std::unique_ptr<Expr> condition,
+		     std::unique_ptr<BlockExpr> if_block,
+		     std::unique_ptr<IfLetExpr> conseq_if_let_expr,
+		     std::vector<Attribute> outer_attrs, Location locus)
+    : IfExpr (std::move (condition), std::move (if_block),
+	      std::move (outer_attrs), locus),
+      if_let_expr (std::move (conseq_if_let_expr))
+  {}
+  // outer attributes not allowed
+
+  // Copy constructor with clone
+  IfExprConseqIfLet (IfExprConseqIfLet const &other)
+    : IfExpr (other), if_let_expr (other.if_let_expr->clone_if_let_expr ())
+  {}
+
+  // Overloaded assignment operator to use clone
+  IfExprConseqIfLet &operator= (IfExprConseqIfLet const &other)
+  {
+    IfExpr::operator= (other);
+    // condition = other.condition->clone_expr();
+    // if_block = other.if_block->clone_block_expr();
+    if_let_expr = other.if_let_expr->clone_if_let_expr ();
+
+    return *this;
+  }
+
+  // move constructors
+  IfExprConseqIfLet (IfExprConseqIfLet &&other) = default;
+  IfExprConseqIfLet &operator= (IfExprConseqIfLet &&other) = default;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<IfLetExpr> &get_conseq_if_let_expr ()
+  {
+    rust_assert (if_let_expr != nullptr);
+    return if_let_expr;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfExprConseqIfLet *clone_if_expr_impl () const override
+  {
+    return new IfExprConseqIfLet (*this);
+  }
+};
+
+/* AST node representing "if let" expression with an "else" expression at the
+ * end */
+class IfLetExprConseqElse : public IfLetExpr
+{
+  std::unique_ptr<BlockExpr> else_block;
+
+public:
+  std::string as_string () const override;
+
+  IfLetExprConseqElse (
+    std::vector<std::unique_ptr<Pattern> > match_arm_patterns,
+    std::unique_ptr<Expr> value, std::unique_ptr<BlockExpr> if_block,
+    std::unique_ptr<BlockExpr> else_block, std::vector<Attribute> outer_attrs,
+    Location locus)
+    : IfLetExpr (std::move (match_arm_patterns), std::move (value),
+		 std::move (if_block), std::move (outer_attrs), locus),
+      else_block (std::move (else_block))
+  {}
+  // outer attributes not allowed
+
+  // copy constructor with clone
+  IfLetExprConseqElse (IfLetExprConseqElse const &other)
+    : IfLetExpr (other), else_block (other.else_block->clone_block_expr ())
+  {}
+
+  // overload assignment operator to clone
+  IfLetExprConseqElse &operator= (IfLetExprConseqElse const &other)
+  {
+    IfLetExpr::operator= (other);
+    // match_arm_patterns = other.match_arm_patterns;
+    // value = other.value->clone_expr();
+    // if_block = other.if_block->clone_block_expr();
+    else_block = other.else_block->clone_block_expr ();
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  IfLetExprConseqElse (IfLetExprConseqElse &&other) = default;
+  IfLetExprConseqElse &operator= (IfLetExprConseqElse &&other) = default;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<BlockExpr> &get_else_block ()
+  {
+    rust_assert (else_block != nullptr);
+    return else_block;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfLetExprConseqElse *clone_if_let_expr_impl () const override
+  {
+    return new IfLetExprConseqElse (*this);
+  }
+};
+
+/* AST node representing "if let" expression with an "else if" expression at the
+ * end */
+class IfLetExprConseqIf : public IfLetExpr
+{
+  std::unique_ptr<IfExpr> if_expr;
+
+public:
+  std::string as_string () const override;
+
+  IfLetExprConseqIf (std::vector<std::unique_ptr<Pattern> > match_arm_patterns,
+		     std::unique_ptr<Expr> value,
+		     std::unique_ptr<BlockExpr> if_block,
+		     std::unique_ptr<IfExpr> if_expr,
+		     std::vector<Attribute> outer_attrs, Location locus)
+    : IfLetExpr (std::move (match_arm_patterns), std::move (value),
+		 std::move (if_block), std::move (outer_attrs), locus),
+      if_expr (std::move (if_expr))
+  {}
+  // again, outer attributes not allowed
+
+  // copy constructor with clone
+  IfLetExprConseqIf (IfLetExprConseqIf const &other)
+    : IfLetExpr (other), if_expr (other.if_expr->clone_if_expr ())
+  {}
+
+  // overload assignment operator to clone
+  IfLetExprConseqIf &operator= (IfLetExprConseqIf const &other)
+  {
+    IfLetExpr::operator= (other);
+    // match_arm_patterns = other.match_arm_patterns;
+    // value = other.value->clone_expr();
+    // if_block = other.if_block->clone_block_expr();
+    if_expr = other.if_expr->clone_if_expr ();
+
+    return *this;
+  }
+
+  // move constructors
+  IfLetExprConseqIf (IfLetExprConseqIf &&other) = default;
+  IfLetExprConseqIf &operator= (IfLetExprConseqIf &&other) = default;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<IfExpr> &get_conseq_if_expr ()
+  {
+    rust_assert (if_expr != nullptr);
+    return if_expr;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfLetExprConseqIf *clone_if_let_expr_impl () const override
+  {
+    return new IfLetExprConseqIf (*this);
+  }
+};
+
+/* AST node representing "if let" expression with an "else if let" expression at
+ * the end */
+class IfLetExprConseqIfLet : public IfLetExpr
+{
+  std::unique_ptr<IfLetExpr> if_let_expr;
+
+public:
+  std::string as_string () const override;
+
+  IfLetExprConseqIfLet (
+    std::vector<std::unique_ptr<Pattern> > match_arm_patterns,
+    std::unique_ptr<Expr> value, std::unique_ptr<BlockExpr> if_block,
+    std::unique_ptr<IfLetExpr> if_let_expr, std::vector<Attribute> outer_attrs,
+    Location locus)
+    : IfLetExpr (std::move (match_arm_patterns), std::move (value),
+		 std::move (if_block), std::move (outer_attrs), locus),
+      if_let_expr (std::move (if_let_expr))
+  {}
+  // outer attributes not allowed
+
+  // copy constructor with clone
+  IfLetExprConseqIfLet (IfLetExprConseqIfLet const &other)
+    : IfLetExpr (other), if_let_expr (other.if_let_expr->clone_if_let_expr ())
+  {}
+
+  // overload assignment operator to clone
+  IfLetExprConseqIfLet &operator= (IfLetExprConseqIfLet const &other)
+  {
+    IfLetExpr::operator= (other);
+    // match_arm_patterns = other.match_arm_patterns;
+    // value = other.value->clone_expr();
+    // if_block = other.if_block->clone_block_expr();
+    if_let_expr = other.if_let_expr->clone_if_let_expr ();
+
+    return *this;
+  }
+
+  // move constructors
+  IfLetExprConseqIfLet (IfLetExprConseqIfLet &&other) = default;
+  IfLetExprConseqIfLet &operator= (IfLetExprConseqIfLet &&other) = default;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<IfLetExpr> &get_conseq_if_let_expr ()
+  {
+    rust_assert (if_let_expr != nullptr);
+    return if_let_expr;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfLetExprConseqIfLet *clone_if_let_expr_impl () const override
+  {
+    return new IfLetExprConseqIfLet (*this);
+  }
+};
+
+// Match arm expression
+struct MatchArm
+{
+private:
+  std::vector<Attribute> outer_attrs;
+  // MatchArmPatterns patterns;
+  std::vector<std::unique_ptr<Pattern> > match_arm_patterns; // inlined
+
+  // bool has_match_arm_guard;
+  // inlined from MatchArmGuard
+  std::unique_ptr<Expr> guard_expr;
+
+  Location locus;
+
+public:
+  // Returns whether the MatchArm has a match arm guard expression
+  bool has_match_arm_guard () const { return guard_expr != nullptr; }
+
+  // Constructor for match arm with a guard expression
+  MatchArm (std::vector<std::unique_ptr<Pattern> > match_arm_patterns,
+	    Location locus, std::unique_ptr<Expr> guard_expr = nullptr,
+	    std::vector<Attribute> outer_attrs = std::vector<Attribute> ())
+    : outer_attrs (std::move (outer_attrs)),
+      match_arm_patterns (std::move (match_arm_patterns)),
+      guard_expr (std::move (guard_expr)), locus (locus)
+  {}
+
+  // Copy constructor with clone
+  MatchArm (MatchArm const &other) : outer_attrs (other.outer_attrs)
+  {
+    // guard to protect from null pointer dereference
+    if (other.guard_expr != nullptr)
+      guard_expr = other.guard_expr->clone_expr ();
+
+    match_arm_patterns.reserve (other.match_arm_patterns.size ());
+    for (const auto &e : other.match_arm_patterns)
+      match_arm_patterns.push_back (e->clone_pattern ());
+
+    locus = other.locus;
+  }
+
+  ~MatchArm () = default;
+
+  // Overload assignment operator to clone
+  MatchArm &operator= (MatchArm const &other)
+  {
+    outer_attrs = other.outer_attrs;
+
+    if (other.guard_expr != nullptr)
+      guard_expr = other.guard_expr->clone_expr ();
+    else
+      guard_expr = nullptr;
+
+    match_arm_patterns.reserve (other.match_arm_patterns.size ());
+    for (const auto &e : other.match_arm_patterns)
+      match_arm_patterns.push_back (e->clone_pattern ());
+
+    return *this;
+  }
+
+  // move constructors
+  MatchArm (MatchArm &&other) = default;
+  MatchArm &operator= (MatchArm &&other) = default;
+
+  // Returns whether match arm is in an error state.
+  bool is_error () const { return match_arm_patterns.empty (); }
+
+  // Creates a match arm in an error state.
+  static MatchArm create_error ()
+  {
+    Location locus = Location ();
+    return MatchArm (std::vector<std::unique_ptr<Pattern> > (), locus);
+  }
+
+  std::string as_string () const;
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_guard_expr ()
+  {
+    rust_assert (has_match_arm_guard ());
+    return guard_expr;
+  }
+
+  // TODO: this mutable getter seems really dodgy. Think up better way.
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+  const std::vector<std::unique_ptr<Pattern> > &get_patterns () const
+  {
+    return match_arm_patterns;
+  }
+  std::vector<std::unique_ptr<Pattern> > &get_patterns ()
+  {
+    return match_arm_patterns;
+  }
+
+  Location get_locus () const { return locus; }
+};
+
+/* A "match case" - a correlated match arm and resulting expression. Not
+ * abstract. */
+struct MatchCase
+{
+private:
+  MatchArm arm;
+  std::unique_ptr<Expr> expr;
+  NodeId node_id;
+
+  /* TODO: does whether trailing comma exists need to be stored? currently
+   * assuming it is only syntactical and has no effect on meaning. */
+
+public:
+  MatchCase (MatchArm arm, std::unique_ptr<Expr> expr)
+    : arm (std::move (arm)), expr (std::move (expr)),
+      node_id (Analysis::Mappings::get ()->get_next_node_id ())
+  {}
+
+  MatchCase (const MatchCase &other)
+    : arm (other.arm), expr (other.expr->clone_expr ()), node_id (other.node_id)
+  {}
+
+  MatchCase &operator= (const MatchCase &other)
+  {
+    arm = other.arm;
+    expr = other.expr->clone_expr ();
+    node_id = other.node_id;
+
+    return *this;
+  }
+
+  MatchCase (MatchCase &&other) = default;
+  MatchCase &operator= (MatchCase &&other) = default;
+
+  ~MatchCase () = default;
+
+  std::string as_string () const;
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_expr ()
+  {
+    rust_assert (expr != nullptr);
+    return expr;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  MatchArm &get_arm ()
+  {
+    rust_assert (!arm.is_error ());
+    return arm;
+  }
+
+  NodeId get_node_id () const { return node_id; }
+};
+
+// Match expression AST node
+class MatchExpr : public ExprWithBlock
+{
+  std::vector<Attribute> outer_attrs;
+  std::unique_ptr<Expr> branch_value;
+  std::vector<Attribute> inner_attrs;
+  std::vector<MatchCase> match_arms;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  // Returns whether the match expression has any match arms.
+  bool has_match_arms () const { return !match_arms.empty (); }
+
+  MatchExpr (std::unique_ptr<Expr> branch_value,
+	     std::vector<MatchCase> match_arms,
+	     std::vector<Attribute> inner_attrs,
+	     std::vector<Attribute> outer_attrs, Location locus)
+    : outer_attrs (std::move (outer_attrs)),
+      branch_value (std::move (branch_value)),
+      inner_attrs (std::move (inner_attrs)),
+      match_arms (std::move (match_arms)), locus (locus)
+  {}
+
+  // Copy constructor requires clone due to unique_ptr
+  MatchExpr (MatchExpr const &other)
+    : ExprWithBlock (other), outer_attrs (other.outer_attrs),
+      inner_attrs (other.inner_attrs), match_arms (other.match_arms),
+      locus (other.locus)
+  {
+    // guard to prevent null dereference (only required if error state)
+    if (other.branch_value != nullptr)
+      branch_value = other.branch_value->clone_expr ();
+  }
+
+  // Overloaded assignment operator to clone due to unique_ptr
+  MatchExpr &operator= (MatchExpr const &other)
+  {
+    ExprWithBlock::operator= (other);
+    inner_attrs = other.inner_attrs;
+    match_arms = other.match_arms;
+    outer_attrs = other.outer_attrs;
+    locus = other.locus;
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.branch_value != nullptr)
+      branch_value = other.branch_value->clone_expr ();
+    else
+      branch_value = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  MatchExpr (MatchExpr &&other) = default;
+  MatchExpr &operator= (MatchExpr &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Invalid if branch value is null, so base stripping on that.
+  void mark_for_strip () override { branch_value = nullptr; }
+  bool is_marked_for_strip () const override { return branch_value == nullptr; }
+
+  // TODO: this mutable getter seems really dodgy. Think up better way.
+  const std::vector<Attribute> &get_inner_attrs () const { return inner_attrs; }
+  std::vector<Attribute> &get_inner_attrs () { return inner_attrs; }
+
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+  void set_outer_attrs (std::vector<Attribute> new_attrs) override
+  {
+    outer_attrs = std::move (new_attrs);
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_scrutinee_expr ()
+  {
+    rust_assert (branch_value != nullptr);
+    return branch_value;
+  }
+
+  const std::vector<MatchCase> &get_match_cases () const { return match_arms; }
+  std::vector<MatchCase> &get_match_cases () { return match_arms; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  MatchExpr *clone_expr_with_block_impl () const override
+  {
+    return new MatchExpr (*this);
+  }
+};
+
+// Await expression AST node (pseudo-member variable access)
+class AwaitExpr : public ExprWithoutBlock
+{
+  std::vector<Attribute> outer_attrs;
+  std::unique_ptr<Expr> awaited_expr;
+  Location locus;
+
+public:
+  // TODO: ensure outer attributes are actually allowed
+  AwaitExpr (std::unique_ptr<Expr> awaited_expr,
+	     std::vector<Attribute> outer_attrs, Location locus)
+    : outer_attrs (std::move (outer_attrs)),
+      awaited_expr (std::move (awaited_expr)), locus (locus)
+  {}
+
+  // copy constructor with clone
+  AwaitExpr (AwaitExpr const &other)
+    : ExprWithoutBlock (other), outer_attrs (other.outer_attrs),
+      locus (other.locus)
+  {
+    // guard to prevent null dereference (only required if error state)
+    if (other.awaited_expr != nullptr)
+      awaited_expr = other.awaited_expr->clone_expr ();
+  }
+
+  // overloaded assignment operator with clone
+  AwaitExpr &operator= (AwaitExpr const &other)
+  {
+    ExprWithoutBlock::operator= (other);
+    outer_attrs = other.outer_attrs;
+    locus = other.locus;
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.awaited_expr != nullptr)
+      awaited_expr = other.awaited_expr->clone_expr ();
+    else
+      awaited_expr = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  AwaitExpr (AwaitExpr &&other) = default;
+  AwaitExpr &operator= (AwaitExpr &&other) = default;
+
+  std::string as_string () const override;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Invalid if awaited expr is null, so base stripping on that.
+  void mark_for_strip () override { awaited_expr = nullptr; }
+  bool is_marked_for_strip () const override { return awaited_expr == nullptr; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_awaited_expr ()
+  {
+    rust_assert (awaited_expr != nullptr);
+    return awaited_expr;
+  }
+
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+  void set_outer_attrs (std::vector<Attribute> new_attrs) override
+  {
+    outer_attrs = std::move (new_attrs);
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  AwaitExpr *clone_expr_without_block_impl () const override
+  {
+    return new AwaitExpr (*this);
+  }
+};
+
+// Async block expression AST node (block expr that evaluates to a future)
+class AsyncBlockExpr : public ExprWithBlock
+{
+  // TODO: should this extend BlockExpr rather than be a composite of it?
+  std::vector<Attribute> outer_attrs;
+  bool has_move;
+  std::unique_ptr<BlockExpr> block_expr;
+  Location locus;
+
+public:
+  AsyncBlockExpr (std::unique_ptr<BlockExpr> block_expr, bool has_move,
+		  std::vector<Attribute> outer_attrs, Location locus)
+    : outer_attrs (std::move (outer_attrs)), has_move (has_move),
+      block_expr (std::move (block_expr)), locus (locus)
+  {}
+
+  // copy constructor with clone
+  AsyncBlockExpr (AsyncBlockExpr const &other)
+    : ExprWithBlock (other), outer_attrs (other.outer_attrs),
+      has_move (other.has_move), locus (other.locus)
+  {
+    // guard to prevent null dereference (only required if error state)
+    if (other.block_expr != nullptr)
+      block_expr = other.block_expr->clone_block_expr ();
+  }
+
+  // overloaded assignment operator to clone
+  AsyncBlockExpr &operator= (AsyncBlockExpr const &other)
+  {
+    ExprWithBlock::operator= (other);
+    outer_attrs = other.outer_attrs;
+    has_move = other.has_move;
+    locus = other.locus;
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.block_expr != nullptr)
+      block_expr = other.block_expr->clone_block_expr ();
+    else
+      block_expr = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  AsyncBlockExpr (AsyncBlockExpr &&other) = default;
+  AsyncBlockExpr &operator= (AsyncBlockExpr &&other) = default;
+
+  std::string as_string () const override;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Invalid if block is null, so base stripping on that.
+  void mark_for_strip () override { block_expr = nullptr; }
+  bool is_marked_for_strip () const override { return block_expr == nullptr; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<BlockExpr> &get_block_expr ()
+  {
+    rust_assert (block_expr != nullptr);
+    return block_expr;
+  }
+
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+  void set_outer_attrs (std::vector<Attribute> new_attrs) override
+  {
+    outer_attrs = std::move (new_attrs);
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  AsyncBlockExpr *clone_expr_with_block_impl () const override
+  {
+    return new AsyncBlockExpr (*this);
+  }
+};
+} // namespace AST
+} // namespace Rust
+
+#endif
diff --git a/gcc/rust/ast/rust-item.h b/gcc/rust/ast/rust-item.h
new file mode 100644
index 00000000000..4987674cba1
--- /dev/null
+++ b/gcc/rust/ast/rust-item.h
@@ -0,0 +1,4382 @@
+// 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_AST_ITEM_H
+#define RUST_AST_ITEM_H
+
+#include "rust-ast.h"
+#include "rust-path.h"
+#include "rust-common.h"
+
+namespace Rust {
+namespace AST {
+// forward decls
+class BlockExpr;
+class TypePath;
+
+// TODO: inline?
+/*struct AbiName {
+    std::string abi_name;
+    // Technically is meant to be STRING_LITERAL
+
+  public:
+    // Returns whether abi name is empty, i.e. doesn't exist.
+    bool is_empty() const {
+	return abi_name.empty();
+    }
+
+    AbiName(std::string name) : abi_name(std::move(name)) {}
+
+    // Empty AbiName constructor
+    AbiName() {}
+};*/
+
+// A type generic parameter (as opposed to a lifetime generic parameter)
+class TypeParam : public GenericParam
+{
+  // bool has_outer_attribute;
+  // std::unique_ptr<Attribute> outer_attr;
+  Attribute outer_attr;
+
+  Identifier type_representation;
+
+  // bool has_type_param_bounds;
+  // TypeParamBounds type_param_bounds;
+  std::vector<std::unique_ptr<TypeParamBound>>
+    type_param_bounds; // inlined form
+
+  // bool has_type;
+  std::unique_ptr<Type> type;
+
+  Location locus;
+
+public:
+  Identifier get_type_representation () const { return type_representation; }
+
+  // Returns whether the type of the type param has been specified.
+  bool has_type () const { return type != nullptr; }
+
+  // Returns whether the type param has type param bounds.
+  bool has_type_param_bounds () const { return !type_param_bounds.empty (); }
+
+  // Returns whether the type param has an outer attribute.
+  bool has_outer_attribute () const { return !outer_attr.is_empty (); }
+
+  TypeParam (Identifier type_representation, Location locus = Location (),
+	     std::vector<std::unique_ptr<TypeParamBound>> type_param_bounds
+	     = std::vector<std::unique_ptr<TypeParamBound>> (),
+	     std::unique_ptr<Type> type = nullptr,
+	     Attribute outer_attr = Attribute::create_empty ())
+    : GenericParam (Analysis::Mappings::get ()->get_next_node_id ()),
+      outer_attr (std::move (outer_attr)),
+      type_representation (std::move (type_representation)),
+      type_param_bounds (std::move (type_param_bounds)),
+      type (std::move (type)), locus (locus)
+  {}
+
+  // Copy constructor uses clone
+  TypeParam (TypeParam const &other)
+    : GenericParam (other.node_id), outer_attr (other.outer_attr),
+      type_representation (other.type_representation), locus (other.locus)
+  {
+    // guard to prevent null pointer dereference
+    if (other.type != nullptr)
+      type = other.type->clone_type ();
+
+    type_param_bounds.reserve (other.type_param_bounds.size ());
+    for (const auto &e : other.type_param_bounds)
+      type_param_bounds.push_back (e->clone_type_param_bound ());
+  }
+
+  // Overloaded assignment operator to clone
+  TypeParam &operator= (TypeParam const &other)
+  {
+    type_representation = other.type_representation;
+    outer_attr = other.outer_attr;
+    locus = other.locus;
+    node_id = other.node_id;
+
+    // guard to prevent null pointer dereference
+    if (other.type != nullptr)
+      type = other.type->clone_type ();
+    else
+      type = nullptr;
+
+    type_param_bounds.reserve (other.type_param_bounds.size ());
+    for (const auto &e : other.type_param_bounds)
+      type_param_bounds.push_back (e->clone_type_param_bound ());
+
+    return *this;
+  }
+
+  // move constructors
+  TypeParam (TypeParam &&other) = default;
+  TypeParam &operator= (TypeParam &&other) = default;
+
+  std::string as_string () const override;
+
+  Location get_locus () const override final { return locus; }
+
+  Kind get_kind () const override final { return Kind::Type; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Type> &get_type ()
+  {
+    rust_assert (type != nullptr);
+    return type;
+  }
+
+  // TODO: mutable getter seems kinda dodgy
+  std::vector<std::unique_ptr<TypeParamBound>> &get_type_param_bounds ()
+  {
+    return type_param_bounds;
+  }
+  const std::vector<std::unique_ptr<TypeParamBound>> &
+  get_type_param_bounds () const
+  {
+    return type_param_bounds;
+  }
+
+protected:
+  // Clone function implementation as virtual method
+  TypeParam *clone_generic_param_impl () const override
+  {
+    return new TypeParam (*this);
+  }
+};
+
+/* "where" clause item base. Abstract - use LifetimeWhereClauseItem,
+ * TypeBoundWhereClauseItem */
+class WhereClauseItem
+{
+public:
+  virtual ~WhereClauseItem () {}
+
+  // Unique pointer custom clone function
+  std::unique_ptr<WhereClauseItem> clone_where_clause_item () const
+  {
+    return std::unique_ptr<WhereClauseItem> (clone_where_clause_item_impl ());
+  }
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (ASTVisitor &vis) = 0;
+
+  virtual NodeId get_node_id () const = 0;
+
+protected:
+  // Clone function implementation as pure virtual method
+  virtual WhereClauseItem *clone_where_clause_item_impl () const = 0;
+};
+
+// A lifetime where clause item
+class LifetimeWhereClauseItem : public WhereClauseItem
+{
+  Lifetime lifetime;
+  std::vector<Lifetime> lifetime_bounds;
+  Location locus;
+  NodeId node_id;
+
+public:
+  LifetimeWhereClauseItem (Lifetime lifetime,
+			   std::vector<Lifetime> lifetime_bounds,
+			   Location locus)
+    : lifetime (std::move (lifetime)),
+      lifetime_bounds (std::move (lifetime_bounds)), locus (locus),
+      node_id (Analysis::Mappings::get ()->get_next_node_id ())
+  {}
+
+  std::string as_string () const override;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  NodeId get_node_id () const override final { return node_id; }
+
+  Lifetime &get_lifetime () { return lifetime; }
+
+  std::vector<Lifetime> &get_lifetime_bounds () { return lifetime_bounds; }
+
+  Location get_locus () const { return locus; }
+
+protected:
+  // Clone function implementation as (not pure) virtual method
+  LifetimeWhereClauseItem *clone_where_clause_item_impl () const override
+  {
+    return new LifetimeWhereClauseItem (*this);
+  }
+};
+
+// A type bound where clause item
+class TypeBoundWhereClauseItem : public WhereClauseItem
+{
+  std::vector<LifetimeParam> for_lifetimes;
+  std::unique_ptr<Type> bound_type;
+  std::vector<std::unique_ptr<TypeParamBound>> type_param_bounds;
+  NodeId node_id;
+  Location locus;
+
+public:
+  // Returns whether the item has ForLifetimes
+  bool has_for_lifetimes () const { return !for_lifetimes.empty (); }
+
+  // Returns whether the item has type param bounds
+  bool has_type_param_bounds () const { return !type_param_bounds.empty (); }
+
+  TypeBoundWhereClauseItem (
+    std::vector<LifetimeParam> for_lifetimes, std::unique_ptr<Type> bound_type,
+    std::vector<std::unique_ptr<TypeParamBound>> type_param_bounds,
+    Location locus)
+    : for_lifetimes (std::move (for_lifetimes)),
+      bound_type (std::move (bound_type)),
+      type_param_bounds (std::move (type_param_bounds)),
+      node_id (Analysis::Mappings::get ()->get_next_node_id ()), locus (locus)
+  {}
+
+  // Copy constructor requires clone
+  TypeBoundWhereClauseItem (TypeBoundWhereClauseItem const &other)
+    : for_lifetimes (other.for_lifetimes),
+      bound_type (other.bound_type->clone_type ())
+  {
+    node_id = other.node_id;
+    type_param_bounds.reserve (other.type_param_bounds.size ());
+    for (const auto &e : other.type_param_bounds)
+      type_param_bounds.push_back (e->clone_type_param_bound ());
+  }
+
+  // Overload assignment operator to clone
+  TypeBoundWhereClauseItem &operator= (TypeBoundWhereClauseItem const &other)
+  {
+    node_id = other.node_id;
+    for_lifetimes = other.for_lifetimes;
+    bound_type = other.bound_type->clone_type ();
+    type_param_bounds.reserve (other.type_param_bounds.size ());
+    for (const auto &e : other.type_param_bounds)
+      type_param_bounds.push_back (e->clone_type_param_bound ());
+
+    return *this;
+  }
+
+  // move constructors
+  TypeBoundWhereClauseItem (TypeBoundWhereClauseItem &&other) = default;
+  TypeBoundWhereClauseItem &operator= (TypeBoundWhereClauseItem &&other)
+    = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  std::unique_ptr<Type> &get_type ()
+  {
+    rust_assert (bound_type != nullptr);
+    return bound_type;
+  }
+
+  // TODO: this mutable getter seems really dodgy. Think up better way.
+  std::vector<std::unique_ptr<TypeParamBound>> &get_type_param_bounds ()
+  {
+    return type_param_bounds;
+  }
+
+  const std::vector<std::unique_ptr<TypeParamBound>> &
+  get_type_param_bounds () const
+  {
+    return type_param_bounds;
+  }
+
+  NodeId get_node_id () const override final { return node_id; }
+
+  Location get_locus () const { return locus; }
+
+protected:
+  // Clone function implementation as (not pure) virtual method
+  TypeBoundWhereClauseItem *clone_where_clause_item_impl () const override
+  {
+    return new TypeBoundWhereClauseItem (*this);
+  }
+};
+
+// A where clause
+struct WhereClause
+{
+private:
+  std::vector<std::unique_ptr<WhereClauseItem>> where_clause_items;
+  NodeId node_id;
+
+public:
+  WhereClause (std::vector<std::unique_ptr<WhereClauseItem>> where_clause_items)
+    : where_clause_items (std::move (where_clause_items)),
+      node_id (Analysis::Mappings::get ()->get_next_node_id ())
+  {}
+
+  // copy constructor with vector clone
+  WhereClause (WhereClause const &other)
+  {
+    node_id = other.node_id;
+    where_clause_items.reserve (other.where_clause_items.size ());
+    for (const auto &e : other.where_clause_items)
+      where_clause_items.push_back (e->clone_where_clause_item ());
+  }
+
+  // overloaded assignment operator with vector clone
+  WhereClause &operator= (WhereClause const &other)
+  {
+    node_id = other.node_id;
+    where_clause_items.reserve (other.where_clause_items.size ());
+    for (const auto &e : other.where_clause_items)
+      where_clause_items.push_back (e->clone_where_clause_item ());
+
+    return *this;
+  }
+
+  // move constructors
+  WhereClause (WhereClause &&other) = default;
+  WhereClause &operator= (WhereClause &&other) = default;
+
+  // Creates a WhereClause with no items.
+  static WhereClause create_empty ()
+  {
+    return WhereClause (std::vector<std::unique_ptr<WhereClauseItem>> ());
+  }
+
+  // Returns whether the WhereClause has no items.
+  bool is_empty () const { return where_clause_items.empty (); }
+
+  std::string as_string () const;
+
+  NodeId get_node_id () const { return node_id; }
+
+  // TODO: this mutable getter seems kinda dodgy
+  std::vector<std::unique_ptr<WhereClauseItem>> &get_items ()
+  {
+    return where_clause_items;
+  }
+  const std::vector<std::unique_ptr<WhereClauseItem>> &get_items () const
+  {
+    return where_clause_items;
+  }
+};
+
+// A self parameter in a method
+struct SelfParam
+{
+private:
+  bool has_ref;
+  bool is_mut;
+  // bool has_lifetime; // only possible if also ref
+  Lifetime lifetime;
+
+  // bool has_type; // only possible if not ref
+  std::unique_ptr<Type> type;
+
+  NodeId node_id;
+
+  Location locus;
+
+  // Unrestricted constructor used for error state
+  SelfParam (Lifetime lifetime, bool has_ref, bool is_mut, Type *type)
+    : has_ref (has_ref), is_mut (is_mut), lifetime (std::move (lifetime)),
+      type (type), node_id (Analysis::Mappings::get ()->get_next_node_id ())
+  {}
+  // this is ok as no outside classes can ever call this
+
+  // TODO: self param can have outer attributes
+
+public:
+  // Returns whether the self-param has a type field.
+  bool has_type () const { return type != nullptr; }
+
+  // Returns whether the self-param has a valid lifetime.
+  bool has_lifetime () const { return !lifetime.is_error (); }
+
+  // Returns whether the self-param is in an error state.
+  bool is_error () const
+  {
+    return (has_type () && has_lifetime ()) || (has_lifetime () && !has_ref);
+    // not having either is not an error
+  }
+
+  // Creates an error state self-param.
+  static SelfParam create_error ()
+  {
+    // cannot have no ref but have a lifetime at the same time
+    return SelfParam (Lifetime (Lifetime::STATIC), false, false, nullptr);
+  }
+
+  // Type-based self parameter (not ref, no lifetime)
+  SelfParam (std::unique_ptr<Type> type, bool is_mut, Location locus)
+    : has_ref (false), is_mut (is_mut), lifetime (Lifetime::error ()),
+      type (std::move (type)),
+      node_id (Analysis::Mappings::get ()->get_next_node_id ()), locus (locus)
+  {}
+
+  // Lifetime-based self parameter (is ref, no type)
+  SelfParam (Lifetime lifetime, bool is_mut, Location locus)
+    : has_ref (true), is_mut (is_mut), lifetime (std::move (lifetime)),
+      node_id (Analysis::Mappings::get ()->get_next_node_id ()), locus (locus)
+  {}
+
+  // Copy constructor requires clone
+  SelfParam (SelfParam const &other)
+    : has_ref (other.has_ref), is_mut (other.is_mut), lifetime (other.lifetime),
+      node_id (Analysis::Mappings::get ()->get_next_node_id ()),
+      locus (other.locus)
+  {
+    node_id = other.node_id;
+    if (other.type != nullptr)
+      type = other.type->clone_type ();
+  }
+
+  // Overload assignment operator to use clone
+  SelfParam &operator= (SelfParam const &other)
+  {
+    is_mut = other.is_mut;
+    has_ref = other.has_ref;
+    lifetime = other.lifetime;
+    locus = other.locus;
+    node_id = other.node_id;
+
+    if (other.type != nullptr)
+      type = other.type->clone_type ();
+    else
+      type = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  SelfParam (SelfParam &&other) = default;
+  SelfParam &operator= (SelfParam &&other) = default;
+
+  std::string as_string () const;
+
+  Location get_locus () const { return locus; }
+
+  bool get_has_ref () const { return has_ref; };
+  bool get_is_mut () const { return is_mut; }
+
+  Lifetime get_lifetime () const { return lifetime; }
+
+  NodeId get_node_id () const { return node_id; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Type> &get_type ()
+  {
+    rust_assert (has_type ());
+    return type;
+  }
+};
+
+// Qualifiers for function, i.e. const, unsafe, extern etc.
+struct FunctionQualifiers
+{
+private:
+  AsyncConstStatus const_status;
+  bool has_unsafe;
+  bool has_extern;
+  std::string extern_abi;
+  Location locus;
+
+public:
+  FunctionQualifiers (Location locus, AsyncConstStatus const_status,
+		      bool has_unsafe, bool has_extern = false,
+		      std::string extern_abi = std::string ())
+    : const_status (const_status), has_unsafe (has_unsafe),
+      has_extern (has_extern), extern_abi (std::move (extern_abi)),
+      locus (locus)
+  {
+    if (!this->extern_abi.empty ())
+      {
+	// having extern is required; not having it is an implementation error
+	rust_assert (has_extern);
+      }
+  }
+
+  std::string as_string () const;
+
+  AsyncConstStatus get_const_status () const { return const_status; }
+  bool is_unsafe () const { return has_unsafe; }
+  bool is_extern () const { return has_extern; }
+  std::string get_extern_abi () const { return extern_abi; }
+  bool has_abi () const { return !extern_abi.empty (); }
+
+  Location get_locus () const { return locus; }
+};
+
+// A function parameter
+struct FunctionParam
+{
+private:
+  std::vector<Attribute> outer_attrs;
+  Location locus;
+  std::unique_ptr<Pattern> param_name;
+  std::unique_ptr<Type> type;
+
+public:
+  FunctionParam (std::unique_ptr<Pattern> param_name,
+		 std::unique_ptr<Type> param_type,
+		 std::vector<Attribute> outer_attrs, Location locus)
+    : outer_attrs (std::move (outer_attrs)), locus (locus),
+      param_name (std::move (param_name)), type (std::move (param_type)),
+      node_id (Analysis::Mappings::get ()->get_next_node_id ())
+  {}
+
+  // Copy constructor uses clone
+  FunctionParam (FunctionParam const &other)
+    : locus (other.locus), node_id (other.node_id)
+  {
+    // guard to prevent nullptr dereference
+    if (other.param_name != nullptr)
+      param_name = other.param_name->clone_pattern ();
+    if (other.type != nullptr)
+      type = other.type->clone_type ();
+  }
+
+  // Overload assignment operator to use clone
+  FunctionParam &operator= (FunctionParam const &other)
+  {
+    locus = other.locus;
+    node_id = other.node_id;
+
+    // guard to prevent nullptr dereference
+    if (other.param_name != nullptr)
+      param_name = other.param_name->clone_pattern ();
+    else
+      param_name = nullptr;
+    if (other.type != nullptr)
+      type = other.type->clone_type ();
+    else
+      type = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  FunctionParam (FunctionParam &&other) = default;
+  FunctionParam &operator= (FunctionParam &&other) = default;
+
+  // Returns whether FunctionParam is in an invalid state.
+  bool is_error () const { return param_name == nullptr || type == nullptr; }
+
+  // Creates an error FunctionParam.
+  static FunctionParam create_error ()
+  {
+    return FunctionParam (nullptr, nullptr, {}, Location ());
+  }
+
+  std::string as_string () const;
+
+  Location get_locus () const { return locus; }
+
+  // TODO: seems kinda dodgy. Think of better way.
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Pattern> &get_pattern ()
+  {
+    rust_assert (param_name != nullptr);
+    return param_name;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Type> &get_type ()
+  {
+    rust_assert (type != nullptr);
+    return type;
+  }
+  NodeId get_node_id () const { return node_id; }
+
+protected:
+  NodeId node_id;
+};
+
+// Visibility of item - if the item has it, then it is some form of public
+struct Visibility
+{
+public:
+  enum VisType
+  {
+    PRIV,
+    PUB,
+    PUB_CRATE,
+    PUB_SELF,
+    PUB_SUPER,
+    PUB_IN_PATH
+  };
+
+private:
+  VisType vis_type;
+  // Only assigned if vis_type is IN_PATH
+  SimplePath in_path;
+
+  // should this store location info?
+
+public:
+  // Creates a Visibility - TODO make constructor protected or private?
+  Visibility (VisType vis_type, SimplePath in_path)
+    : vis_type (vis_type), in_path (std::move (in_path))
+  {}
+
+  VisType get_public_vis_type () const { return vis_type; }
+
+  // Returns whether visibility is in an error state.
+  bool is_error () const
+  {
+    return vis_type == PUB_IN_PATH && in_path.is_empty ();
+  }
+
+  // Returns whether a visibility has a path
+  bool has_path () const { return !(is_error ()) && vis_type == PUB_IN_PATH; }
+
+  // Returns whether visibility is public or not.
+  bool is_public () const { return vis_type != PRIV && !is_error (); }
+
+  // Creates an error visibility.
+  static Visibility create_error ()
+  {
+    return Visibility (PUB_IN_PATH, SimplePath::create_empty ());
+  }
+
+  // Unique pointer custom clone function
+  /*std::unique_ptr<Visibility> clone_visibility() const {
+      return std::unique_ptr<Visibility>(clone_visibility_impl());
+  }*/
+
+  /* TODO: think of a way to only allow valid Visibility states - polymorphism
+   * is one idea but may be too resource-intensive. */
+
+  // Creates a public visibility with no further features/arguments.
+  static Visibility create_public ()
+  {
+    return Visibility (PUB, SimplePath::create_empty ());
+  }
+
+  // Creates a public visibility with crate-relative paths
+  static Visibility create_crate (Location crate_tok_location)
+  {
+    return Visibility (PUB_CRATE,
+		       SimplePath::from_str ("crate", crate_tok_location));
+  }
+
+  // Creates a public visibility with self-relative paths
+  static Visibility create_self (Location self_tok_location)
+  {
+    return Visibility (PUB_SELF,
+		       SimplePath::from_str ("self", self_tok_location));
+  }
+
+  // Creates a public visibility with parent module-relative paths
+  static Visibility create_super (Location super_tok_location)
+  {
+    return Visibility (PUB_SUPER,
+		       SimplePath::from_str ("super", super_tok_location));
+  }
+
+  // Creates a private visibility
+  static Visibility create_private ()
+  {
+    return Visibility (PRIV, SimplePath::create_empty ());
+  }
+
+  // Creates a public visibility with a given path or whatever.
+  static Visibility create_in_path (SimplePath in_path)
+  {
+    return Visibility (PUB_IN_PATH, std::move (in_path));
+  }
+
+  std::string as_string () const;
+  const SimplePath &get_path () const { return in_path; }
+  SimplePath &get_path () { return in_path; }
+
+protected:
+  // Clone function implementation - not currently virtual but may be if
+  // polymorphism used
+  /*virtual*/ Visibility *clone_visibility_impl () const
+  {
+    return new Visibility (*this);
+  }
+};
+
+// A method (function belonging to a type)
+class Method : public InherentImplItem, public TraitImplItem
+{
+  std::vector<Attribute> outer_attrs;
+  Visibility vis;
+  FunctionQualifiers qualifiers;
+  Identifier method_name;
+  std::vector<std::unique_ptr<GenericParam>> generic_params;
+  SelfParam self_param;
+  std::vector<FunctionParam> function_params;
+  std::unique_ptr<Type> return_type;
+  WhereClause where_clause;
+  std::unique_ptr<BlockExpr> function_body;
+  Location locus;
+  NodeId node_id;
+
+public:
+  // Returns whether the method is in an error state.
+  bool is_error () const
+  {
+    return function_body == nullptr || method_name.empty ()
+	   || self_param.is_error ();
+  }
+
+  // Creates an error state method.
+  static Method create_error ()
+  {
+    return Method ("", FunctionQualifiers (Location (), NONE, true),
+		   std::vector<std::unique_ptr<GenericParam>> (),
+		   SelfParam::create_error (), std::vector<FunctionParam> (),
+		   nullptr, WhereClause::create_empty (), nullptr,
+		   Visibility::create_error (), std::vector<Attribute> (), {});
+  }
+
+  // Returns whether the method has generic parameters.
+  bool has_generics () const { return !generic_params.empty (); }
+
+  // Returns whether the method has parameters.
+  bool has_params () const { return !function_params.empty (); }
+
+  // Returns whether the method has a return type (void otherwise).
+  bool has_return_type () const { return return_type != nullptr; }
+
+  // Returns whether the where clause exists (i.e. has items)
+  bool has_where_clause () const { return !where_clause.is_empty (); }
+
+  // Returns whether method has a non-default visibility.
+  bool has_visibility () const { return !vis.is_error (); }
+
+  // Mega-constructor with all possible fields
+  Method (Identifier method_name, FunctionQualifiers qualifiers,
+	  std::vector<std::unique_ptr<GenericParam>> generic_params,
+	  SelfParam self_param, std::vector<FunctionParam> function_params,
+	  std::unique_ptr<Type> return_type, WhereClause where_clause,
+	  std::unique_ptr<BlockExpr> function_body, Visibility vis,
+	  std::vector<Attribute> outer_attrs, Location locus)
+    : outer_attrs (std::move (outer_attrs)), vis (std::move (vis)),
+      qualifiers (std::move (qualifiers)),
+      method_name (std::move (method_name)),
+      generic_params (std::move (generic_params)),
+      self_param (std::move (self_param)),
+      function_params (std::move (function_params)),
+      return_type (std::move (return_type)),
+      where_clause (std::move (where_clause)),
+      function_body (std::move (function_body)), locus (locus),
+      node_id (Analysis::Mappings::get ()->get_next_node_id ())
+  {}
+
+  // TODO: add constructor with less fields
+
+  // Copy constructor with clone
+  Method (Method const &other)
+    : outer_attrs (other.outer_attrs), vis (other.vis),
+      qualifiers (other.qualifiers), method_name (other.method_name),
+      self_param (other.self_param), function_params (other.function_params),
+      where_clause (other.where_clause), locus (other.locus)
+  {
+    // guard to prevent null dereference (always required)
+    if (other.return_type != nullptr)
+      return_type = other.return_type->clone_type ();
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.function_body != nullptr)
+      function_body = other.function_body->clone_block_expr ();
+
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+
+    node_id = other.node_id;
+  }
+
+  // Overloaded assignment operator to clone
+  Method &operator= (Method const &other)
+  {
+    method_name = other.method_name;
+    outer_attrs = other.outer_attrs;
+    vis = other.vis;
+    qualifiers = other.qualifiers;
+    self_param = other.self_param;
+    function_params = other.function_params;
+    where_clause = other.where_clause;
+    locus = other.locus;
+
+    // guard to prevent null dereference (always required)
+    if (other.return_type != nullptr)
+      return_type = other.return_type->clone_type ();
+    else
+      return_type = nullptr;
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.function_body != nullptr)
+      function_body = other.function_body->clone_block_expr ();
+    else
+      function_body = nullptr;
+
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+
+    node_id = other.node_id;
+
+    return *this;
+  }
+
+  // move constructors
+  Method (Method &&other) = default;
+  Method &operator= (Method &&other) = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Invalid if block is null, so base stripping on that.
+  void mark_for_strip () override { function_body = nullptr; }
+  bool is_marked_for_strip () const override
+  {
+    return function_body == nullptr;
+  }
+
+  // TODO: this mutable getter seems really dodgy. Think up better way.
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+
+  std::vector<FunctionParam> &get_function_params () { return function_params; }
+  const std::vector<FunctionParam> &get_function_params () const
+  {
+    return function_params;
+  }
+
+  std::vector<std::unique_ptr<GenericParam>> &get_generic_params ()
+  {
+    return generic_params;
+  }
+  const std::vector<std::unique_ptr<GenericParam>> &get_generic_params () const
+  {
+    return generic_params;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<BlockExpr> &get_definition ()
+  {
+    rust_assert (function_body != nullptr);
+    return function_body;
+  }
+
+  SelfParam &get_self_param () { return self_param; }
+  const SelfParam &get_self_param () const { return self_param; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Type> &get_return_type ()
+  {
+    rust_assert (has_return_type ());
+    return return_type;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  WhereClause &get_where_clause () { return where_clause; }
+
+  Identifier get_method_name () const { return method_name; }
+
+  NodeId get_node_id () const { return node_id; }
+
+  Location get_locus () const override final { return locus; }
+
+  FunctionQualifiers get_qualifiers () { return qualifiers; }
+
+  const Visibility &get_visibility () const { return vis; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  Method *clone_inherent_impl_item_impl () const final override
+  {
+    return clone_method_impl ();
+  }
+
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  Method *clone_trait_impl_item_impl () const final override
+  {
+    return clone_method_impl ();
+  }
+
+  /*virtual*/ Method *clone_method_impl () const { return new Method (*this); }
+};
+
+// Item that supports visibility - abstract base class
+class VisItem : public Item
+{
+  Visibility visibility;
+  std::vector<Attribute> outer_attrs;
+
+protected:
+  // Visibility constructor
+  VisItem (Visibility visibility,
+	   std::vector<Attribute> outer_attrs = std::vector<Attribute> ())
+    : visibility (std::move (visibility)), outer_attrs (std::move (outer_attrs))
+  {}
+
+  // Visibility copy constructor
+  VisItem (VisItem const &other)
+    : visibility (other.visibility), outer_attrs (other.outer_attrs)
+  {}
+
+  // Overload assignment operator to clone
+  VisItem &operator= (VisItem const &other)
+  {
+    visibility = other.visibility;
+    outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  VisItem (VisItem &&other) = default;
+  VisItem &operator= (VisItem &&other) = default;
+
+public:
+  /* Does the item have some kind of public visibility (non-default
+   * visibility)? */
+  bool has_visibility () const { return visibility.is_public (); }
+
+  std::string as_string () const override;
+
+  // TODO: this mutable getter seems really dodgy. Think up better way.
+  Visibility &get_visibility () { return visibility; }
+  const Visibility &get_visibility () const { return visibility; }
+
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+};
+
+// Rust module item - abstract base class
+class Module : public VisItem
+{
+public:
+  // Type of the current module. A module can be either loaded or unloaded,
+  // meaning that the items of the module can already be present or not. For
+  // example, the following module would be loaded: `mod foo { fn bar() {} }`.
+  // However, the module would be unloaded if it refers to an external file (i.e
+  // `mod foo;`) and then become loaded upon expansion.
+  enum ModuleKind
+  {
+    LOADED,
+    UNLOADED,
+  };
+
+  Identifier get_name () const { return module_name; }
+
+private:
+  Identifier module_name;
+  Location locus;
+  ModuleKind kind;
+
+  // Name of the file including the module
+  std::string outer_filename;
+  // bool has_inner_attrs;
+  std::vector<Attribute> inner_attrs;
+  // bool has_items;
+  std::vector<std::unique_ptr<Item>> items;
+  // Names of including inline modules (immediate parent is last in the list)
+  std::vector<std::string> module_scope;
+
+  // Filename the module refers to. Empty string on LOADED modules or if an
+  // error occured when dealing with UNLOADED modules
+  std::string module_file;
+
+  void clone_items (const std::vector<std::unique_ptr<Item>> &other_items)
+  {
+    items.reserve (other_items.size ());
+    for (const auto &e : other_items)
+      items.push_back (e->clone_item ());
+  }
+
+public:
+  // Returns whether the module has items in its body.
+  bool has_items () const { return !items.empty (); }
+
+  // Returns whether the module has any inner attributes.
+  bool has_inner_attrs () const { return !inner_attrs.empty (); }
+
+  // Unloaded module constructor
+  Module (Identifier module_name, Visibility visibility,
+	  std::vector<Attribute> outer_attrs, Location locus,
+	  std::string outer_filename, std::vector<std::string> module_scope)
+    : VisItem (std::move (visibility), std::move (outer_attrs)),
+      module_name (module_name), locus (locus), kind (ModuleKind::UNLOADED),
+      outer_filename (outer_filename), inner_attrs (std::vector<Attribute> ()),
+      items (std::vector<std::unique_ptr<Item>> ()),
+      module_scope (std::move (module_scope))
+  {}
+
+  // Loaded module constructor, with items
+  Module (Identifier name, Location locus,
+	  std::vector<std::unique_ptr<Item>> items,
+	  Visibility visibility = Visibility::create_error (),
+	  std::vector<Attribute> inner_attrs = std::vector<Attribute> (),
+	  std::vector<Attribute> outer_attrs = std::vector<Attribute> ())
+    : VisItem (std::move (visibility), std::move (outer_attrs)),
+      module_name (name), locus (locus), kind (ModuleKind::LOADED),
+      outer_filename (std::string ()), inner_attrs (std::move (inner_attrs)),
+      items (std::move (items))
+  {}
+
+  // Copy constructor with vector clone
+  Module (Module const &other)
+    : VisItem (other), module_name (other.module_name), locus (other.locus),
+      kind (other.kind), inner_attrs (other.inner_attrs),
+      module_scope (other.module_scope)
+  {
+    // We need to check whether we are copying a loaded module or an unloaded
+    // one. In the second case, clear the `items` vector.
+    if (other.kind == LOADED)
+      clone_items (other.items);
+    else
+      items.clear ();
+  }
+
+  // Overloaded assignment operator with vector clone
+  Module &operator= (Module const &other)
+  {
+    VisItem::operator= (other);
+
+    module_name = other.module_name;
+    locus = other.locus;
+    kind = other.kind;
+    inner_attrs = other.inner_attrs;
+    module_scope = other.module_scope;
+
+    // Likewise, we need to clear the `items` vector in case the other module is
+    // unloaded
+    if (kind == LOADED)
+      clone_items (other.items);
+    else
+      items.clear ();
+
+    return *this;
+  }
+
+  // Search for the filename associated with an external module, storing it in
+  // module_file
+  void process_file_path ();
+  // Load the items contained in an external module
+  void load_items ();
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  /* Override that runs the function recursively on all items contained within
+   * the module. */
+  void add_crate_name (std::vector<std::string> &names) const override;
+
+  // Returns the kind of the module
+  enum ModuleKind get_kind () const { return kind; }
+
+  // TODO: think of better way to do this - mutable getter seems dodgy
+  const std::vector<Attribute> &get_inner_attrs () const { return inner_attrs; }
+  std::vector<Attribute> &get_inner_attrs () { return inner_attrs; }
+
+  const std::vector<std::unique_ptr<Item>> &get_items () const { return items; }
+  std::vector<std::unique_ptr<Item>> &get_items () { return items; }
+
+  // move constructors
+  Module (Module &&other) = default;
+  Module &operator= (Module &&other) = default;
+
+  std::string as_string () const override;
+
+  Location get_locus () const override final { return locus; }
+
+  // Invalid if name is empty, so base stripping on that.
+  void mark_for_strip () override { module_name = ""; }
+  bool is_marked_for_strip () const override { return module_name.empty (); }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  Module *clone_item_impl () const override { return new Module (*this); }
+};
+
+// Rust extern crate declaration AST node
+class ExternCrate : public VisItem
+{
+  // this is either an identifier or "self", with self parsed to string
+  std::string referenced_crate;
+  // bool has_as_clause;
+  // AsClause as_clause;
+  // this is either an identifier or "_", with _ parsed to string
+  std::string as_clause_name;
+
+  Location locus;
+
+  /* e.g.
+      "extern crate foo as _"
+      "extern crate foo"
+      "extern crate std as cool_std"  */
+public:
+  std::string as_string () const override;
+
+  // Returns whether extern crate declaration has an as clause.
+  bool has_as_clause () const { return !as_clause_name.empty (); }
+
+  /* Returns whether extern crate declaration references the current crate
+   * (i.e. self). */
+  bool references_self () const { return referenced_crate == "self"; }
+
+  // Constructor
+  ExternCrate (std::string referenced_crate, Visibility visibility,
+	       std::vector<Attribute> outer_attrs, Location locus,
+	       std::string as_clause_name = std::string ())
+    : VisItem (std::move (visibility), std::move (outer_attrs)),
+      referenced_crate (std::move (referenced_crate)),
+      as_clause_name (std::move (as_clause_name)), locus (locus)
+  {}
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  const std::string &get_referenced_crate () const { return referenced_crate; }
+  const std::string &get_as_clause () const { return as_clause_name; }
+
+  // Override that adds extern crate name in decl to passed list of names.
+  void add_crate_name (std::vector<std::string> &names) const override
+  {
+    names.push_back (referenced_crate);
+  }
+
+  // Invalid if crate name is empty, so base stripping on that.
+  void mark_for_strip () override { referenced_crate = ""; }
+  bool is_marked_for_strip () const override
+  {
+    return referenced_crate.empty ();
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  ExternCrate *clone_item_impl () const override
+  {
+    return new ExternCrate (*this);
+  }
+};
+
+// The path-ish thing referred to in a use declaration - abstract base class
+class UseTree
+{
+  Location locus;
+
+public:
+  enum Kind
+  {
+    Glob,
+    Rebind,
+    List,
+  };
+
+  virtual ~UseTree () {}
+
+  // Overload assignment operator to clone
+  UseTree &operator= (UseTree const &other)
+  {
+    locus = other.locus;
+
+    return *this;
+  }
+
+  UseTree (const UseTree &other) = default;
+
+  // move constructors
+  UseTree (UseTree &&other) = default;
+  UseTree &operator= (UseTree &&other) = default;
+
+  // Unique pointer custom clone function
+  std::unique_ptr<UseTree> clone_use_tree () const
+  {
+    return std::unique_ptr<UseTree> (clone_use_tree_impl ());
+  }
+
+  virtual std::string as_string () const = 0;
+  virtual Kind get_kind () const = 0;
+
+  Location get_locus () const { return locus; }
+
+  virtual void accept_vis (ASTVisitor &vis) = 0;
+
+protected:
+  // Clone function implementation as pure virtual method
+  virtual UseTree *clone_use_tree_impl () const = 0;
+
+  UseTree (Location locus) : locus (locus) {}
+};
+
+// Use tree with a glob (wildcard) operator
+class UseTreeGlob : public UseTree
+{
+public:
+  enum PathType
+  {
+    NO_PATH,
+    GLOBAL,
+    PATH_PREFIXED
+  };
+
+private:
+  PathType glob_type;
+  SimplePath path;
+
+public:
+  UseTreeGlob (PathType glob_type, SimplePath path, Location locus)
+    : UseTree (locus), glob_type (glob_type), path (std::move (path))
+  {
+    if (this->glob_type != PATH_PREFIXED)
+      {
+	// compiler implementation error if there is a path with a
+	// non-path-prefixed use tree glob
+	rust_assert (!has_path ());
+      }
+    // TODO: do path-prefixed paths also have to have a path? If so, have an
+    // assert for that too.
+  }
+
+  /* Returns whether has path. Should be made redundant by PathType
+   * PATH_PREFIXED. */
+  bool has_path () const { return !path.is_empty (); }
+
+  std::string as_string () const override;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  Kind get_kind () const override { return Glob; }
+
+  SimplePath get_path () const
+  {
+    rust_assert (has_path ());
+    return path;
+  }
+
+  /* TODO: find way to ensure only PATH_PREFIXED glob_type has path - factory
+   * methods? */
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  UseTreeGlob *clone_use_tree_impl () const override
+  {
+    return new UseTreeGlob (*this);
+  }
+};
+
+// Use tree with a list of paths with a common prefix
+class UseTreeList : public UseTree
+{
+public:
+  enum PathType
+  {
+    NO_PATH,
+    GLOBAL,
+    PATH_PREFIXED
+  };
+
+private:
+  PathType path_type;
+  SimplePath path;
+
+  std::vector<std::unique_ptr<UseTree>> trees;
+
+public:
+  UseTreeList (PathType path_type, SimplePath path,
+	       std::vector<std::unique_ptr<UseTree>> trees, Location locus)
+    : UseTree (locus), path_type (path_type), path (std::move (path)),
+      trees (std::move (trees))
+  {
+    if (this->path_type != PATH_PREFIXED)
+      {
+	// compiler implementation error if there is a path with a
+	// non-path-prefixed use tree glob
+	rust_assert (!has_path ());
+      }
+    // TODO: do path-prefixed paths also have to have a path? If so, have an
+    // assert for that too.
+  }
+
+  // copy constructor with vector clone
+  UseTreeList (UseTreeList const &other)
+    : UseTree (other), path_type (other.path_type), path (other.path)
+  {
+    trees.reserve (other.trees.size ());
+    for (const auto &e : other.trees)
+      trees.push_back (e->clone_use_tree ());
+  }
+
+  // overloaded assignment operator with vector clone
+  UseTreeList &operator= (UseTreeList const &other)
+  {
+    UseTree::operator= (other);
+    path_type = other.path_type;
+    path = other.path;
+
+    trees.reserve (other.trees.size ());
+    for (const auto &e : other.trees)
+      trees.push_back (e->clone_use_tree ());
+
+    return *this;
+  }
+
+  // move constructors
+  UseTreeList (UseTreeList &&other) = default;
+  UseTreeList &operator= (UseTreeList &&other) = default;
+
+  // Returns whether has path. Should be made redundant by path_type.
+  bool has_path () const { return !path.is_empty (); }
+
+  // Returns whether has inner tree elements.
+  bool has_trees () const { return !trees.empty (); }
+
+  std::string as_string () const override;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  Kind get_kind () const override { return List; }
+  SimplePath get_path () const
+  {
+    rust_assert (has_path ());
+    return path;
+  }
+
+  const std::vector<std::unique_ptr<UseTree>> &get_trees () const
+  {
+    return trees;
+  }
+
+  // TODO: find way to ensure only PATH_PREFIXED path_type has path - factory
+  // methods?
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  UseTreeList *clone_use_tree_impl () const override
+  {
+    return new UseTreeList (*this);
+  }
+};
+
+// Use tree where it rebinds the module name as something else
+class UseTreeRebind : public UseTree
+{
+public:
+  enum NewBindType
+  {
+    NONE,
+    IDENTIFIER,
+    WILDCARD
+  };
+
+private:
+  SimplePath path;
+
+  NewBindType bind_type;
+  Identifier identifier; // only if NewBindType is IDENTIFIER
+
+public:
+  UseTreeRebind (NewBindType bind_type, SimplePath path, Location locus,
+		 Identifier identifier = std::string ())
+    : UseTree (locus), path (std::move (path)), bind_type (bind_type),
+      identifier (std::move (identifier))
+  {}
+
+  // Returns whether has path (this should always be true).
+  bool has_path () const { return !path.is_empty (); }
+
+  // Returns whether has identifier (or, rather, is allowed to).
+  bool has_identifier () const { return bind_type == IDENTIFIER; }
+
+  std::string as_string () const override;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  Kind get_kind () const override { return Rebind; }
+
+  SimplePath get_path () const
+  {
+    rust_assert (has_path ());
+    return path;
+  }
+
+  const Identifier &get_identifier () const
+  {
+    rust_assert (has_identifier ());
+    return identifier;
+  }
+
+  // TODO: find way to ensure only PATH_PREFIXED path_type has path - factory
+  // methods?
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  virtual UseTreeRebind *clone_use_tree_impl () const override
+  {
+    return new UseTreeRebind (*this);
+  }
+};
+
+// Rust use declaration (i.e. for modules) AST node
+class UseDeclaration : public VisItem
+{
+  std::unique_ptr<UseTree> use_tree;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  UseDeclaration (std::unique_ptr<UseTree> use_tree, Visibility visibility,
+		  std::vector<Attribute> outer_attrs, Location locus)
+    : VisItem (std::move (visibility), std::move (outer_attrs)),
+      use_tree (std::move (use_tree)), locus (locus)
+  {}
+
+  // Copy constructor with clone
+  UseDeclaration (UseDeclaration const &other)
+    : VisItem (other), locus (other.locus)
+  {
+    // guard to prevent null dereference (only required if error state)
+    if (other.use_tree != nullptr)
+      use_tree = other.use_tree->clone_use_tree ();
+  }
+
+  // Overloaded assignment operator to clone
+  UseDeclaration &operator= (UseDeclaration const &other)
+  {
+    VisItem::operator= (other);
+    // visibility = other.visibility->clone_visibility();
+    // outer_attrs = other.outer_attrs;
+    locus = other.locus;
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.use_tree != nullptr)
+      use_tree = other.use_tree->clone_use_tree ();
+    else
+      use_tree = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  UseDeclaration (UseDeclaration &&other) = default;
+  UseDeclaration &operator= (UseDeclaration &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+  const std::unique_ptr<UseTree> &get_tree () const { return use_tree; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Invalid if use tree is null, so base stripping on that.
+  void mark_for_strip () override { use_tree = nullptr; }
+  bool is_marked_for_strip () const override { return use_tree == nullptr; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  UseDeclaration *clone_item_impl () const override
+  {
+    return new UseDeclaration (*this);
+  }
+};
+
+class LetStmt;
+
+// Rust function declaration AST node
+class Function : public VisItem, public InherentImplItem, public TraitImplItem
+{
+  FunctionQualifiers qualifiers;
+  Identifier function_name;
+  std::vector<std::unique_ptr<GenericParam>> generic_params;
+  std::vector<FunctionParam> function_params;
+  std::unique_ptr<Type> return_type;
+  WhereClause where_clause;
+  std::unique_ptr<BlockExpr> function_body;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  // Returns whether function has generic parameters.
+  bool has_generics () const { return !generic_params.empty (); }
+
+  // Returns whether function has regular parameters.
+  bool has_function_params () const { return !function_params.empty (); }
+
+  // Returns whether function has return type - if not, it is void.
+  bool has_return_type () const { return return_type != nullptr; }
+
+  // Returns whether function has a where clause.
+  bool has_where_clause () const { return !where_clause.is_empty (); }
+
+  // Mega-constructor with all possible fields
+  Function (Identifier function_name, FunctionQualifiers qualifiers,
+	    std::vector<std::unique_ptr<GenericParam>> generic_params,
+	    std::vector<FunctionParam> function_params,
+	    std::unique_ptr<Type> return_type, WhereClause where_clause,
+	    std::unique_ptr<BlockExpr> function_body, Visibility vis,
+	    std::vector<Attribute> outer_attrs, Location locus)
+    : VisItem (std::move (vis), std::move (outer_attrs)),
+      qualifiers (std::move (qualifiers)),
+      function_name (std::move (function_name)),
+      generic_params (std::move (generic_params)),
+      function_params (std::move (function_params)),
+      return_type (std::move (return_type)),
+      where_clause (std::move (where_clause)),
+      function_body (std::move (function_body)), locus (locus)
+  {}
+
+  // TODO: add constructor with less fields
+
+  // Copy constructor with clone
+  Function (Function const &other)
+    : VisItem (other), qualifiers (other.qualifiers),
+      function_name (other.function_name),
+      function_params (other.function_params),
+      where_clause (other.where_clause), locus (other.locus)
+  {
+    // guard to prevent null dereference (always required)
+    if (other.return_type != nullptr)
+      return_type = other.return_type->clone_type ();
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.function_body != nullptr)
+      function_body = other.function_body->clone_block_expr ();
+
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+  }
+
+  // Overloaded assignment operator to clone
+  Function &operator= (Function const &other)
+  {
+    VisItem::operator= (other);
+    function_name = other.function_name;
+    qualifiers = other.qualifiers;
+    function_params = other.function_params;
+    where_clause = other.where_clause;
+    // visibility = other.visibility->clone_visibility();
+    // outer_attrs = other.outer_attrs;
+    locus = other.locus;
+
+    // guard to prevent null dereference (always required)
+    if (other.return_type != nullptr)
+      return_type = other.return_type->clone_type ();
+    else
+      return_type = nullptr;
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.function_body != nullptr)
+      function_body = other.function_body->clone_block_expr ();
+    else
+      function_body = nullptr;
+
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+
+    return *this;
+  }
+
+  // move constructors
+  Function (Function &&other) = default;
+  Function &operator= (Function &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Invalid if block is null, so base stripping on that.
+  void mark_for_strip () override { function_body = nullptr; }
+  bool is_marked_for_strip () const override
+  {
+    return function_body == nullptr;
+  }
+
+  std::vector<FunctionParam> &get_function_params () { return function_params; }
+  const std::vector<FunctionParam> &get_function_params () const
+  {
+    return function_params;
+  }
+
+  std::vector<std::unique_ptr<GenericParam>> &get_generic_params ()
+  {
+    return generic_params;
+  }
+  const std::vector<std::unique_ptr<GenericParam>> &get_generic_params () const
+  {
+    return generic_params;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<BlockExpr> &get_definition ()
+  {
+    rust_assert (function_body != nullptr);
+    return function_body;
+  }
+
+  const FunctionQualifiers &get_qualifiers () const { return qualifiers; }
+
+  Identifier get_function_name () const { return function_name; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  WhereClause &get_where_clause () { return where_clause; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Type> &get_return_type ()
+  {
+    rust_assert (has_return_type ());
+    return return_type;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  Function *clone_item_impl () const override { return new Function (*this); }
+
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  Function *clone_inherent_impl_item_impl () const override
+  {
+    return new Function (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  Function *clone_trait_impl_item_impl () const override
+  {
+    return new Function (*this);
+  }
+};
+
+// Rust type alias (i.e. typedef) AST node
+class TypeAlias : public VisItem, public TraitImplItem
+{
+  Identifier new_type_name;
+
+  // bool has_generics;
+  // Generics generic_params;
+  std::vector<std::unique_ptr<GenericParam>> generic_params; // inlined
+
+  // bool has_where_clause;
+  WhereClause where_clause;
+
+  std::unique_ptr<Type> existing_type;
+
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  // Returns whether type alias has generic parameters.
+  bool has_generics () const { return !generic_params.empty (); }
+
+  // Returns whether type alias has a where clause.
+  bool has_where_clause () const { return !where_clause.is_empty (); }
+
+  // Mega-constructor with all possible fields
+  TypeAlias (Identifier new_type_name,
+	     std::vector<std::unique_ptr<GenericParam>> generic_params,
+	     WhereClause where_clause, std::unique_ptr<Type> existing_type,
+	     Visibility vis, std::vector<Attribute> outer_attrs, Location locus)
+    : VisItem (std::move (vis), std::move (outer_attrs)),
+      new_type_name (std::move (new_type_name)),
+      generic_params (std::move (generic_params)),
+      where_clause (std::move (where_clause)),
+      existing_type (std::move (existing_type)), locus (locus)
+  {}
+
+  // Copy constructor
+  TypeAlias (TypeAlias const &other)
+    : VisItem (other), new_type_name (other.new_type_name),
+      where_clause (other.where_clause), locus (other.locus)
+  {
+    // guard to prevent null dereference (only required if error state)
+    if (other.existing_type != nullptr)
+      existing_type = other.existing_type->clone_type ();
+
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+  }
+
+  // Overloaded assignment operator to clone
+  TypeAlias &operator= (TypeAlias const &other)
+  {
+    VisItem::operator= (other);
+    new_type_name = other.new_type_name;
+    where_clause = other.where_clause;
+    // visibility = other.visibility->clone_visibility();
+    // outer_attrs = other.outer_attrs;
+    locus = other.locus;
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.existing_type != nullptr)
+      existing_type = other.existing_type->clone_type ();
+    else
+      existing_type = nullptr;
+
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+
+    return *this;
+  }
+
+  // move constructors
+  TypeAlias (TypeAlias &&other) = default;
+  TypeAlias &operator= (TypeAlias &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Invalid if existing type is null, so base stripping on that.
+  void mark_for_strip () override { existing_type = nullptr; }
+  bool is_marked_for_strip () const override
+  {
+    return existing_type == nullptr;
+  }
+
+  std::vector<std::unique_ptr<GenericParam>> &get_generic_params ()
+  {
+    return generic_params;
+  }
+  const std::vector<std::unique_ptr<GenericParam>> &get_generic_params () const
+  {
+    return generic_params;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  WhereClause &get_where_clause () { return where_clause; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Type> &get_type_aliased ()
+  {
+    rust_assert (existing_type != nullptr);
+    return existing_type;
+  }
+
+  Identifier get_new_type_name () const { return new_type_name; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  TypeAlias *clone_item_impl () const override { return new TypeAlias (*this); }
+
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  TypeAlias *clone_trait_impl_item_impl () const override
+  {
+    return new TypeAlias (*this);
+  }
+};
+
+// Rust base struct declaration AST node - abstract base class
+class Struct : public VisItem
+{
+protected:
+  // protected to enable access by derived classes - allows better as_string
+  Identifier struct_name;
+
+  // bool has_generics;
+  // Generics generic_params;
+  std::vector<std::unique_ptr<GenericParam>> generic_params; // inlined
+
+  // bool has_where_clause;
+  WhereClause where_clause;
+
+private:
+  Location locus;
+
+public:
+  // Returns whether struct has generic parameters.
+  bool has_generics () const { return !generic_params.empty (); }
+
+  // Returns whether struct has a where clause.
+  bool has_where_clause () const { return !where_clause.is_empty (); }
+
+  Location get_locus () const override final { return locus; }
+
+  // Invalid if name is empty, so base stripping on that.
+  void mark_for_strip () override { struct_name = ""; }
+  bool is_marked_for_strip () const override { return struct_name.empty (); }
+
+  Identifier get_struct_name () const { return struct_name; }
+
+  std::vector<std::unique_ptr<GenericParam>> &get_generic_params ()
+  {
+    return generic_params;
+  }
+  const std::vector<std::unique_ptr<GenericParam>> &get_generic_params () const
+  {
+    return generic_params;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  WhereClause &get_where_clause () { return where_clause; }
+
+  Identifier get_identifier () const { return struct_name; }
+
+protected:
+  Struct (Identifier struct_name,
+	  std::vector<std::unique_ptr<GenericParam>> generic_params,
+	  WhereClause where_clause, Visibility vis, Location locus,
+	  std::vector<Attribute> outer_attrs = std::vector<Attribute> ())
+    : VisItem (std::move (vis), std::move (outer_attrs)),
+      struct_name (std::move (struct_name)),
+      generic_params (std::move (generic_params)),
+      where_clause (std::move (where_clause)), locus (locus)
+  {}
+
+  // Copy constructor with vector clone
+  Struct (Struct const &other)
+    : VisItem (other), struct_name (other.struct_name),
+      where_clause (other.where_clause), locus (other.locus)
+  {
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+  }
+
+  // Overloaded assignment operator with vector clone
+  Struct &operator= (Struct const &other)
+  {
+    VisItem::operator= (other);
+    struct_name = other.struct_name;
+    where_clause = other.where_clause;
+    locus = other.locus;
+
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+
+    return *this;
+  }
+
+  // move constructors
+  Struct (Struct &&other) = default;
+  Struct &operator= (Struct &&other) = default;
+};
+
+// A single field in a struct
+struct StructField
+{
+private:
+  // bool has_outer_attributes;
+  std::vector<Attribute> outer_attrs;
+
+  // bool has_visibility;
+  Visibility visibility;
+
+  Identifier field_name;
+  std::unique_ptr<Type> field_type;
+
+  NodeId node_id;
+
+  Location locus;
+
+public:
+  // Returns whether struct field has any outer attributes.
+  bool has_outer_attributes () const { return !outer_attrs.empty (); }
+
+  // Returns whether struct field has a non-private (non-default) visibility.
+  bool has_visibility () const { return !visibility.is_error (); }
+
+  StructField (Identifier field_name, std::unique_ptr<Type> field_type,
+	       Visibility vis, Location locus,
+	       std::vector<Attribute> outer_attrs = std::vector<Attribute> ())
+    : outer_attrs (std::move (outer_attrs)), visibility (std::move (vis)),
+      field_name (std::move (field_name)), field_type (std::move (field_type)),
+      node_id (Analysis::Mappings::get ()->get_next_node_id ()), locus (locus)
+  {}
+
+  // Copy constructor
+  StructField (StructField const &other)
+    : outer_attrs (other.outer_attrs), visibility (other.visibility),
+      field_name (other.field_name), node_id (other.node_id),
+      locus (other.locus)
+  {
+    // guard to prevent null dereference
+    if (other.field_type != nullptr)
+      field_type = other.field_type->clone_type ();
+  }
+
+  ~StructField () = default;
+
+  // Overloaded assignment operator to clone
+  StructField &operator= (StructField const &other)
+  {
+    field_name = other.field_name;
+    visibility = other.visibility;
+    outer_attrs = other.outer_attrs;
+    node_id = other.node_id;
+
+    // guard to prevent null dereference
+    if (other.field_type != nullptr)
+      field_type = other.field_type->clone_type ();
+    else
+      field_type = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  StructField (StructField &&other) = default;
+  StructField &operator= (StructField &&other) = default;
+
+  // Returns whether struct field is in an error state.
+  bool is_error () const
+  {
+    return field_name.empty () && field_type == nullptr;
+    // this should really be an or since neither are allowed
+  }
+
+  // Creates an error state struct field.
+  static StructField create_error ()
+  {
+    return StructField (std::string (""), nullptr, Visibility::create_error (),
+			Location ());
+  }
+
+  std::string as_string () const;
+
+  // TODO: this mutable getter seems really dodgy. Think up better way.
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+
+  Identifier get_field_name () const { return field_name; }
+
+  Location get_locus () const { return locus; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Type> &get_field_type ()
+  {
+    rust_assert (field_type != nullptr);
+    return field_type;
+  }
+
+  const Visibility &get_visibility () const { return visibility; }
+
+  NodeId get_node_id () const { return node_id; }
+};
+
+// Rust struct declaration with true struct type AST node
+class StructStruct : public Struct
+{
+  std::vector<StructField> fields;
+  bool is_unit;
+
+public:
+  std::string as_string () const override;
+
+  // Mega-constructor with all possible fields
+  StructStruct (std::vector<StructField> fields, Identifier struct_name,
+		std::vector<std::unique_ptr<GenericParam>> generic_params,
+		WhereClause where_clause, bool is_unit, Visibility vis,
+		std::vector<Attribute> outer_attrs, Location locus)
+    : Struct (std::move (struct_name), std::move (generic_params),
+	      std::move (where_clause), std::move (vis), locus,
+	      std::move (outer_attrs)),
+      fields (std::move (fields)), is_unit (is_unit)
+  {}
+
+  // Unit struct constructor
+  StructStruct (Identifier struct_name,
+		std::vector<std::unique_ptr<GenericParam>> generic_params,
+		WhereClause where_clause, Visibility vis,
+		std::vector<Attribute> outer_attrs, Location locus)
+    : Struct (std::move (struct_name), std::move (generic_params),
+	      std::move (where_clause), std::move (vis), locus,
+	      std::move (outer_attrs)),
+      is_unit (true)
+  {}
+
+  /* Returns whether the struct is a unit struct - struct defined without
+   * fields. This is important because it also means an implicit constant of its
+   * type is defined. */
+  bool is_unit_struct () const { return is_unit; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: this mutable getter seems really dodgy. Think up better way.
+  std::vector<StructField> &get_fields () { return fields; }
+  const std::vector<StructField> &get_fields () const { return fields; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  StructStruct *clone_item_impl () const override
+  {
+    return new StructStruct (*this);
+  }
+};
+
+// A single field in a tuple
+struct TupleField
+{
+private:
+  // bool has_outer_attributes;
+  std::vector<Attribute> outer_attrs;
+
+  // bool has_visibility;
+  Visibility visibility;
+
+  std::unique_ptr<Type> field_type;
+
+  NodeId node_id;
+
+  Location locus;
+
+public:
+  // Returns whether tuple field has outer attributes.
+  bool has_outer_attributes () const { return !outer_attrs.empty (); }
+
+  /* Returns whether tuple field has a non-default visibility (i.e. a public
+   * one) */
+  bool has_visibility () const { return !visibility.is_error (); }
+
+  // Complete constructor
+  TupleField (std::unique_ptr<Type> field_type, Visibility vis, Location locus,
+	      std::vector<Attribute> outer_attrs = std::vector<Attribute> ())
+    : outer_attrs (std::move (outer_attrs)), visibility (std::move (vis)),
+      field_type (std::move (field_type)),
+      node_id (Analysis::Mappings::get ()->get_next_node_id ()), locus (locus)
+  {}
+
+  // Copy constructor with clone
+  TupleField (TupleField const &other)
+    : outer_attrs (other.outer_attrs), visibility (other.visibility),
+      node_id (other.node_id), locus (other.locus)
+  {
+    // guard to prevent null dereference (only required if error)
+    if (other.field_type != nullptr)
+      field_type = other.field_type->clone_type ();
+  }
+
+  ~TupleField () = default;
+
+  // Overloaded assignment operator to clone
+  TupleField &operator= (TupleField const &other)
+  {
+    visibility = other.visibility;
+    outer_attrs = other.outer_attrs;
+    node_id = other.node_id;
+    locus = other.locus;
+
+    // guard to prevent null dereference (only required if error)
+    if (other.field_type != nullptr)
+      field_type = other.field_type->clone_type ();
+    else
+      field_type = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  TupleField (TupleField &&other) = default;
+  TupleField &operator= (TupleField &&other) = default;
+
+  // Returns whether tuple field is in an error state.
+  bool is_error () const { return field_type == nullptr; }
+
+  // Creates an error state tuple field.
+  static TupleField create_error ()
+  {
+    return TupleField (nullptr, Visibility::create_error (), Location ());
+  }
+
+  std::string as_string () const;
+
+  NodeId get_node_id () const { return node_id; }
+
+  const Visibility &get_visibility () const { return visibility; }
+
+  Location get_locus () const { return locus; }
+
+  // TODO: this mutable getter seems really dodgy. Think up better way.
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Type> &get_field_type ()
+  {
+    rust_assert (field_type != nullptr);
+    return field_type;
+  }
+};
+
+// Rust tuple declared using struct keyword AST node
+class TupleStruct : public Struct
+{
+  std::vector<TupleField> fields;
+
+public:
+  std::string as_string () const override;
+
+  // Mega-constructor with all possible fields
+  TupleStruct (std::vector<TupleField> fields, Identifier struct_name,
+	       std::vector<std::unique_ptr<GenericParam>> generic_params,
+	       WhereClause where_clause, Visibility vis,
+	       std::vector<Attribute> outer_attrs, Location locus)
+    : Struct (std::move (struct_name), std::move (generic_params),
+	      std::move (where_clause), std::move (vis), locus,
+	      std::move (outer_attrs)),
+      fields (std::move (fields))
+  {}
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: this mutable getter seems really dodgy. Think up better way.
+  std::vector<TupleField> &get_fields () { return fields; }
+  const std::vector<TupleField> &get_fields () const { return fields; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  TupleStruct *clone_item_impl () const override
+  {
+    return new TupleStruct (*this);
+  }
+};
+
+/* An item used in an "enum" tagged union - not abstract: base represents a
+ * name-only enum. EnumItems (variants) syntactically allow a Visibility
+ * annotation. */
+class EnumItem : public VisItem
+{
+  Identifier variant_name;
+
+  Location locus;
+
+public:
+  virtual ~EnumItem () {}
+
+  EnumItem (Identifier variant_name, Visibility vis,
+	    std::vector<Attribute> outer_attrs, Location locus)
+    : VisItem (std::move (vis), std::move (outer_attrs)),
+      variant_name (std::move (variant_name)), locus (locus)
+  {}
+
+  // Unique pointer custom clone function
+  std::unique_ptr<EnumItem> clone_enum_item () const
+  {
+    return std::unique_ptr<EnumItem> (clone_item_impl ());
+  }
+
+  virtual std::string as_string () const;
+
+  // not pure virtual as not abstract
+  virtual void accept_vis (ASTVisitor &vis);
+
+  Location get_locus () const { return locus; }
+
+  Identifier get_identifier () const { return variant_name; }
+
+  // Based on idea that name is never empty.
+  void mark_for_strip () { variant_name = ""; }
+  bool is_marked_for_strip () const { return variant_name.empty (); }
+
+protected:
+  EnumItem *clone_item_impl () const override { return new EnumItem (*this); }
+};
+
+// A tuple item used in an "enum" tagged union
+class EnumItemTuple : public EnumItem
+{
+  // bool has_tuple_fields;
+  std::vector<TupleField> tuple_fields;
+
+public:
+  // Returns whether tuple enum item has tuple fields.
+  bool has_tuple_fields () const { return !tuple_fields.empty (); }
+
+  EnumItemTuple (Identifier variant_name, Visibility vis,
+		 std::vector<TupleField> tuple_fields,
+		 std::vector<Attribute> outer_attrs, Location locus)
+    : EnumItem (std::move (variant_name), std::move (vis),
+		std::move (outer_attrs), locus),
+      tuple_fields (std::move (tuple_fields))
+  {}
+
+  std::string as_string () const override;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: this mutable getter seems really dodgy. Think up better way.
+  std::vector<TupleField> &get_tuple_fields () { return tuple_fields; }
+  const std::vector<TupleField> &get_tuple_fields () const
+  {
+    return tuple_fields;
+  }
+
+protected:
+  // Clone function implementation as (not pure) virtual method
+  EnumItemTuple *clone_item_impl () const override
+  {
+    return new EnumItemTuple (*this);
+  }
+};
+
+// A struct item used in an "enum" tagged union
+class EnumItemStruct : public EnumItem
+{
+  // bool has_struct_fields;
+  std::vector<StructField> struct_fields;
+
+public:
+  // Returns whether struct enum item has struct fields.
+  bool has_struct_fields () const { return !struct_fields.empty (); }
+
+  EnumItemStruct (Identifier variant_name, Visibility vis,
+		  std::vector<StructField> struct_fields,
+		  std::vector<Attribute> outer_attrs, Location locus)
+    : EnumItem (std::move (variant_name), std::move (vis),
+		std::move (outer_attrs), locus),
+      struct_fields (std::move (struct_fields))
+  {}
+
+  std::string as_string () const override;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: this mutable getter seems really dodgy. Think up better way.
+  std::vector<StructField> &get_struct_fields () { return struct_fields; }
+  const std::vector<StructField> &get_struct_fields () const
+  {
+    return struct_fields;
+  }
+
+protected:
+  // Clone function implementation as (not pure) virtual method
+  EnumItemStruct *clone_item_impl () const override
+  {
+    return new EnumItemStruct (*this);
+  }
+};
+
+// A discriminant (numbered enum) item used in an "enum" tagged union
+class EnumItemDiscriminant : public EnumItem
+{
+  std::unique_ptr<Expr> expression;
+
+public:
+  EnumItemDiscriminant (Identifier variant_name, Visibility vis,
+			std::unique_ptr<Expr> expr,
+			std::vector<Attribute> outer_attrs, Location locus)
+    : EnumItem (std::move (variant_name), std::move (vis),
+		std::move (outer_attrs), locus),
+      expression (std::move (expr))
+  {}
+
+  // Copy constructor with clone
+  EnumItemDiscriminant (EnumItemDiscriminant const &other)
+    : EnumItem (other), expression (other.expression->clone_expr ())
+  {}
+
+  // Overloaded assignment operator to clone
+  EnumItemDiscriminant &operator= (EnumItemDiscriminant const &other)
+  {
+    EnumItem::operator= (other);
+    expression = other.expression->clone_expr ();
+    // variant_name = other.variant_name;
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  EnumItemDiscriminant (EnumItemDiscriminant &&other) = default;
+  EnumItemDiscriminant &operator= (EnumItemDiscriminant &&other) = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_expr ()
+  {
+    rust_assert (expression != nullptr);
+    return expression;
+  }
+
+protected:
+  // Clone function implementation as (not pure) virtual method
+  EnumItemDiscriminant *clone_item_impl () const override
+  {
+    return new EnumItemDiscriminant (*this);
+  }
+};
+
+// AST node for Rust "enum" - tagged union
+class Enum : public VisItem
+{
+  Identifier enum_name;
+
+  // bool has_generics;
+  // Generics generic_params;
+  std::vector<std::unique_ptr<GenericParam>> generic_params; // inlined
+
+  // bool has_where_clause;
+  WhereClause where_clause;
+
+  std::vector<std::unique_ptr<EnumItem>> items;
+
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  // Returns whether "enum" has generic parameters.
+  bool has_generics () const { return !generic_params.empty (); }
+
+  // Returns whether "enum" has a where clause.
+  bool has_where_clause () const { return !where_clause.is_empty (); }
+
+  /* Returns whether enum is a "zero-variant" (no possible variant) enum,
+   * which cannot be instantiated. */
+  bool is_zero_variant () const { return items.empty (); }
+
+  // Mega-constructor
+  Enum (Identifier enum_name, Visibility vis,
+	std::vector<std::unique_ptr<GenericParam>> generic_params,
+	WhereClause where_clause, std::vector<std::unique_ptr<EnumItem>> items,
+	std::vector<Attribute> outer_attrs, Location locus)
+    : VisItem (std::move (vis), std::move (outer_attrs)),
+      enum_name (std::move (enum_name)),
+      generic_params (std::move (generic_params)),
+      where_clause (std::move (where_clause)), items (std::move (items)),
+      locus (locus)
+  {}
+
+  // TODO: constructor with less arguments
+
+  // Copy constructor with vector clone
+  Enum (Enum const &other)
+    : VisItem (other), enum_name (other.enum_name),
+      where_clause (other.where_clause), locus (other.locus)
+  {
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+
+    items.reserve (other.items.size ());
+    for (const auto &e : other.items)
+      items.push_back (e->clone_enum_item ());
+  }
+
+  // Overloaded assignment operator with vector clone
+  Enum &operator= (Enum const &other)
+  {
+    VisItem::operator= (other);
+    enum_name = other.enum_name;
+    where_clause = other.where_clause;
+    locus = other.locus;
+
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+
+    items.reserve (other.items.size ());
+    for (const auto &e : other.items)
+      items.push_back (e->clone_enum_item ());
+
+    return *this;
+  }
+
+  // Move constructors
+  Enum (Enum &&other) = default;
+  Enum &operator= (Enum &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  Identifier get_identifier () const { return enum_name; }
+
+  // Invalid if name is empty, so base stripping on that.
+  void mark_for_strip () override { enum_name = ""; }
+  bool is_marked_for_strip () const override { return enum_name.empty (); }
+
+  // TODO: this mutable getter seems really dodgy. Think up better way.
+  std::vector<std::unique_ptr<EnumItem>> &get_variants () { return items; }
+  const std::vector<std::unique_ptr<EnumItem>> &get_variants () const
+  {
+    return items;
+  }
+
+  std::vector<std::unique_ptr<GenericParam>> &get_generic_params ()
+  {
+    return generic_params;
+  }
+  const std::vector<std::unique_ptr<GenericParam>> &get_generic_params () const
+  {
+    return generic_params;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  WhereClause &get_where_clause () { return where_clause; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  Enum *clone_item_impl () const override { return new Enum (*this); }
+};
+
+// Rust untagged union used for C compat AST node
+class Union : public VisItem
+{
+  Identifier union_name;
+
+  // bool has_generics;
+  // Generics generic_params;
+  std::vector<std::unique_ptr<GenericParam>> generic_params; // inlined
+
+  // bool has_where_clause;
+  WhereClause where_clause;
+
+  std::vector<StructField> variants;
+
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  // Returns whether union has generic params.
+  bool has_generics () const { return !generic_params.empty (); }
+
+  // Returns whether union has where clause.
+  bool has_where_clause () const { return !where_clause.is_empty (); }
+
+  Union (Identifier union_name, Visibility vis,
+	 std::vector<std::unique_ptr<GenericParam>> generic_params,
+	 WhereClause where_clause, std::vector<StructField> variants,
+	 std::vector<Attribute> outer_attrs, Location locus)
+    : VisItem (std::move (vis), std::move (outer_attrs)),
+      union_name (std::move (union_name)),
+      generic_params (std::move (generic_params)),
+      where_clause (std::move (where_clause)), variants (std::move (variants)),
+      locus (locus)
+  {}
+
+  // copy constructor with vector clone
+  Union (Union const &other)
+    : VisItem (other), union_name (other.union_name),
+      where_clause (other.where_clause), variants (other.variants),
+      locus (other.locus)
+  {
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+  }
+
+  // overloaded assignment operator with vector clone
+  Union &operator= (Union const &other)
+  {
+    VisItem::operator= (other);
+    union_name = other.union_name;
+    where_clause = other.where_clause;
+    variants = other.variants;
+    locus = other.locus;
+
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+
+    return *this;
+  }
+
+  // move constructors
+  Union (Union &&other) = default;
+  Union &operator= (Union &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Invalid if name is empty, so base stripping on that.
+  void mark_for_strip () override { union_name = ""; }
+  bool is_marked_for_strip () const override { return union_name.empty (); }
+
+  // TODO: this mutable getter seems really dodgy. Think up better way.
+  std::vector<StructField> &get_variants () { return variants; }
+  const std::vector<StructField> &get_variants () const { return variants; }
+
+  std::vector<std::unique_ptr<GenericParam>> &get_generic_params ()
+  {
+    return generic_params;
+  }
+  const std::vector<std::unique_ptr<GenericParam>> &get_generic_params () const
+  {
+    return generic_params;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  WhereClause &get_where_clause () { return where_clause; }
+
+  Identifier get_identifier () const { return union_name; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  Union *clone_item_impl () const override { return new Union (*this); }
+};
+
+/* "Constant item" AST node - used for constant, compile-time expressions
+ * within module scope (like constexpr) */
+class ConstantItem : public VisItem,
+		     public InherentImplItem,
+		     public TraitImplItem
+{
+  // either has an identifier or "_" - maybe handle in identifier?
+  // bool identifier_is_underscore;
+  // if no identifier declared, identifier will be "_"
+  std::string identifier;
+
+  std::unique_ptr<Type> type;
+  std::unique_ptr<Expr> const_expr;
+
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  ConstantItem (std::string ident, Visibility vis, std::unique_ptr<Type> type,
+		std::unique_ptr<Expr> const_expr,
+		std::vector<Attribute> outer_attrs, Location locus)
+    : VisItem (std::move (vis), std::move (outer_attrs)),
+      identifier (std::move (ident)), type (std::move (type)),
+      const_expr (std::move (const_expr)), locus (locus)
+  {}
+
+  ConstantItem (ConstantItem const &other)
+    : VisItem (other), identifier (other.identifier), locus (other.locus)
+  {
+    // guard to prevent null dereference (only required if error state)
+    if (other.type != nullptr)
+      type = other.type->clone_type ();
+    if (other.const_expr != nullptr)
+      const_expr = other.const_expr->clone_expr ();
+  }
+
+  // Overload assignment operator to clone
+  ConstantItem &operator= (ConstantItem const &other)
+  {
+    VisItem::operator= (other);
+    identifier = other.identifier;
+    locus = other.locus;
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.type != nullptr)
+      type = other.type->clone_type ();
+    else
+      type = nullptr;
+    if (other.const_expr != nullptr)
+      const_expr = other.const_expr->clone_expr ();
+    else
+      const_expr = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  ConstantItem (ConstantItem &&other) = default;
+  ConstantItem &operator= (ConstantItem &&other) = default;
+
+  /* Returns whether constant item is an "unnamed" (wildcard underscore used
+   * as identifier) constant. */
+  bool is_unnamed () const { return identifier == "_"; }
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Invalid if type or expression are null, so base stripping on that.
+  void mark_for_strip () override
+  {
+    type = nullptr;
+    const_expr = nullptr;
+  }
+  bool is_marked_for_strip () const override
+  {
+    return type == nullptr && const_expr == nullptr;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_expr ()
+  {
+    rust_assert (const_expr != nullptr);
+    return const_expr;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Type> &get_type ()
+  {
+    rust_assert (type != nullptr);
+    return type;
+  }
+
+  std::string get_identifier () const { return identifier; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  ConstantItem *clone_item_impl () const override
+  {
+    return new ConstantItem (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  ConstantItem *clone_inherent_impl_item_impl () const override
+  {
+    return new ConstantItem (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  ConstantItem *clone_trait_impl_item_impl () const override
+  {
+    return new ConstantItem (*this);
+  }
+};
+
+/* Static item AST node - items within module scope with fixed storage
+ * duration? */
+class StaticItem : public VisItem
+{
+  bool has_mut;
+  Identifier name;
+  std::unique_ptr<Type> type;
+  std::unique_ptr<Expr> expr;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  StaticItem (Identifier name, bool is_mut, std::unique_ptr<Type> type,
+	      std::unique_ptr<Expr> expr, Visibility vis,
+	      std::vector<Attribute> outer_attrs, Location locus)
+    : VisItem (std::move (vis), std::move (outer_attrs)), has_mut (is_mut),
+      name (std::move (name)), type (std::move (type)), expr (std::move (expr)),
+      locus (locus)
+  {}
+
+  // Copy constructor with clone
+  StaticItem (StaticItem const &other)
+    : VisItem (other), has_mut (other.has_mut), name (other.name),
+      locus (other.locus)
+  {
+    // guard to prevent null dereference (only required if error state)
+    if (other.type != nullptr)
+      type = other.type->clone_type ();
+    if (other.expr != nullptr)
+      expr = other.expr->clone_expr ();
+  }
+
+  // Overloaded assignment operator to clone
+  StaticItem &operator= (StaticItem const &other)
+  {
+    VisItem::operator= (other);
+    name = other.name;
+    has_mut = other.has_mut;
+    locus = other.locus;
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.type != nullptr)
+      type = other.type->clone_type ();
+    else
+      type = nullptr;
+    if (other.expr != nullptr)
+      expr = other.expr->clone_expr ();
+    else
+      expr = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  StaticItem (StaticItem &&other) = default;
+  StaticItem &operator= (StaticItem &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Invalid if type or expression are null, so base stripping on that.
+  void mark_for_strip () override
+  {
+    type = nullptr;
+    expr = nullptr;
+  }
+  bool is_marked_for_strip () const override
+  {
+    return type == nullptr && expr == nullptr;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_expr ()
+  {
+    rust_assert (expr != nullptr);
+    return expr;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Type> &get_type ()
+  {
+    rust_assert (type != nullptr);
+    return type;
+  }
+
+  bool is_mutable () const { return has_mut; }
+
+  Identifier get_identifier () const { return name; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  StaticItem *clone_item_impl () const override
+  {
+    return new StaticItem (*this);
+  }
+};
+
+// Function declaration in traits
+struct TraitFunctionDecl
+{
+private:
+  // TODO: delete and replace with Function decl item? no as no body in this.
+  FunctionQualifiers qualifiers;
+  Identifier function_name;
+
+  // bool has_generics;
+  // Generics generic_params;
+  std::vector<std::unique_ptr<GenericParam>> generic_params; // inlined
+
+  // bool has_params;
+  // FunctionParams function_params;
+  std::vector<FunctionParam> function_params; // inlined
+
+  // bool has_return_type;
+  std::unique_ptr<Type> return_type;
+
+  // bool has_where_clause;
+  WhereClause where_clause;
+
+  // should this store location info?
+
+public:
+  // Returns whether function decl has generic parameters.
+  bool has_generics () const { return !generic_params.empty (); }
+
+  // Returns whether function decl has regular parameters.
+  bool has_params () const { return !function_params.empty (); }
+
+  // Returns whether function has return type (otherwise is void).
+  bool has_return_type () const { return return_type != nullptr; }
+
+  // Returns whether function has a where clause.
+  bool has_where_clause () const { return !where_clause.is_empty (); }
+
+  Identifier get_identifier () const { return function_name; }
+
+  // Mega-constructor
+  TraitFunctionDecl (Identifier function_name, FunctionQualifiers qualifiers,
+		     std::vector<std::unique_ptr<GenericParam>> generic_params,
+		     std::vector<FunctionParam> function_params,
+		     std::unique_ptr<Type> return_type,
+		     WhereClause where_clause)
+    : qualifiers (std::move (qualifiers)),
+      function_name (std::move (function_name)),
+      generic_params (std::move (generic_params)),
+      function_params (std::move (function_params)),
+      return_type (std::move (return_type)),
+      where_clause (std::move (where_clause))
+  {}
+
+  // Copy constructor with clone
+  TraitFunctionDecl (TraitFunctionDecl const &other)
+    : qualifiers (other.qualifiers), function_name (other.function_name),
+      function_params (other.function_params), where_clause (other.where_clause)
+  {
+    // guard to prevent nullptr dereference
+    if (other.return_type != nullptr)
+      return_type = other.return_type->clone_type ();
+
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+  }
+
+  ~TraitFunctionDecl () = default;
+
+  // Overloaded assignment operator with clone
+  TraitFunctionDecl &operator= (TraitFunctionDecl const &other)
+  {
+    function_name = other.function_name;
+    qualifiers = other.qualifiers;
+    function_params = other.function_params;
+    where_clause = other.where_clause;
+
+    // guard to prevent nullptr dereference
+    if (other.return_type != nullptr)
+      return_type = other.return_type->clone_type ();
+    else
+      return_type = nullptr;
+
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+
+    return *this;
+  }
+
+  // move constructors
+  TraitFunctionDecl (TraitFunctionDecl &&other) = default;
+  TraitFunctionDecl &operator= (TraitFunctionDecl &&other) = default;
+
+  std::string as_string () const;
+
+  // Invalid if function name is empty, so base stripping on that.
+  void mark_for_strip () { function_name = ""; }
+  bool is_marked_for_strip () const { return function_name.empty (); }
+
+  // TODO: this mutable getter seems really dodgy. Think up better way.
+  std::vector<FunctionParam> &get_function_params () { return function_params; }
+  const std::vector<FunctionParam> &get_function_params () const
+  {
+    return function_params;
+  }
+
+  std::vector<std::unique_ptr<GenericParam>> &get_generic_params ()
+  {
+    return generic_params;
+  }
+  const std::vector<std::unique_ptr<GenericParam>> &get_generic_params () const
+  {
+    return generic_params;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Type> &get_return_type () { return return_type; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  WhereClause &get_where_clause () { return where_clause; }
+
+  FunctionQualifiers get_qualifiers () { return qualifiers; }
+};
+
+// Actual trait item function declaration within traits
+class TraitItemFunc : public TraitItem
+{
+  std::vector<Attribute> outer_attrs;
+  TraitFunctionDecl decl;
+  std::unique_ptr<BlockExpr> block_expr;
+  Location locus;
+
+public:
+  // Returns whether function has a definition or is just a declaration.
+  bool has_definition () const { return block_expr != nullptr; }
+
+  TraitItemFunc (TraitFunctionDecl decl, std::unique_ptr<BlockExpr> block_expr,
+		 std::vector<Attribute> outer_attrs, Location locus)
+    : TraitItem (), outer_attrs (std::move (outer_attrs)),
+      decl (std::move (decl)), block_expr (std::move (block_expr)),
+      locus (locus)
+  {}
+
+  // Copy constructor with clone
+  TraitItemFunc (TraitItemFunc const &other)
+    : outer_attrs (other.outer_attrs), decl (other.decl), locus (other.locus)
+  {
+    node_id = other.node_id;
+
+    // guard to prevent null dereference
+    if (other.block_expr != nullptr)
+      block_expr = other.block_expr->clone_block_expr ();
+  }
+
+  // Overloaded assignment operator to clone
+  TraitItemFunc &operator= (TraitItemFunc const &other)
+  {
+    TraitItem::operator= (other);
+    outer_attrs = other.outer_attrs;
+    decl = other.decl;
+    locus = other.locus;
+    node_id = other.node_id;
+
+    // guard to prevent null dereference
+    if (other.block_expr != nullptr)
+      block_expr = other.block_expr->clone_block_expr ();
+    else
+      block_expr = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  TraitItemFunc (TraitItemFunc &&other) = default;
+  TraitItemFunc &operator= (TraitItemFunc &&other) = default;
+
+  std::string as_string () const override;
+
+  Location get_locus () const { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Invalid if trait decl is empty, so base stripping on that.
+  void mark_for_strip () override { decl.mark_for_strip (); }
+  bool is_marked_for_strip () const override
+  {
+    return decl.is_marked_for_strip ();
+  }
+
+  // TODO: this mutable getter seems really dodgy. Think up better way.
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<BlockExpr> &get_definition () { return block_expr; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  TraitFunctionDecl &get_trait_function_decl ()
+  {
+    // TODO: maybe only allow access if not marked for strip?
+    return decl;
+  }
+
+protected:
+  // Clone function implementation as (not pure) virtual method
+  TraitItemFunc *clone_trait_item_impl () const override
+  {
+    return new TraitItemFunc (*this);
+  }
+};
+
+// Method declaration within traits
+struct TraitMethodDecl
+{
+private:
+  // TODO: delete and replace with Function decl item? no as no body.
+  FunctionQualifiers qualifiers;
+  Identifier function_name;
+
+  // bool has_generics;
+  // Generics generic_params;
+  std::vector<std::unique_ptr<GenericParam>> generic_params; // inlined
+
+  SelfParam self_param;
+
+  // bool has_params;
+  // FunctionParams function_params;
+  std::vector<FunctionParam> function_params; // inlined
+
+  // bool has_return_type;
+  std::unique_ptr<Type> return_type;
+
+  // bool has_where_clause;
+  WhereClause where_clause;
+
+  // should this store location info?
+
+public:
+  // Returns whether method decl has generic parameters.
+  bool has_generics () const { return !generic_params.empty (); }
+
+  // Returns whether method decl has regular parameters.
+  bool has_params () const { return !function_params.empty (); }
+
+  // Returns whether method has return type (otherwise is void).
+  bool has_return_type () const { return return_type != nullptr; }
+
+  // Returns whether method has a where clause.
+  bool has_where_clause () const { return !where_clause.is_empty (); }
+
+  Identifier get_identifier () const { return function_name; }
+
+  // Mega-constructor
+  TraitMethodDecl (Identifier function_name, FunctionQualifiers qualifiers,
+		   std::vector<std::unique_ptr<GenericParam>> generic_params,
+		   SelfParam self_param,
+		   std::vector<FunctionParam> function_params,
+		   std::unique_ptr<Type> return_type, WhereClause where_clause)
+    : qualifiers (std::move (qualifiers)),
+      function_name (std::move (function_name)),
+      generic_params (std::move (generic_params)),
+      self_param (std::move (self_param)),
+      function_params (std::move (function_params)),
+      return_type (std::move (return_type)),
+      where_clause (std::move (where_clause))
+  {}
+
+  // Copy constructor with clone
+  TraitMethodDecl (TraitMethodDecl const &other)
+    : qualifiers (other.qualifiers), function_name (other.function_name),
+      self_param (other.self_param), function_params (other.function_params),
+      where_clause (other.where_clause)
+  {
+    // guard to prevent nullptr dereference
+    if (other.return_type != nullptr)
+      return_type = other.return_type->clone_type ();
+
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+  }
+
+  ~TraitMethodDecl () = default;
+
+  // Overloaded assignment operator with clone
+  TraitMethodDecl &operator= (TraitMethodDecl const &other)
+  {
+    function_name = other.function_name;
+    qualifiers = other.qualifiers;
+    self_param = other.self_param;
+    function_params = other.function_params;
+    where_clause = other.where_clause;
+
+    // guard to prevent nullptr dereference
+    if (other.return_type != nullptr)
+      return_type = other.return_type->clone_type ();
+    else
+      return_type = nullptr;
+
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+
+    return *this;
+  }
+
+  // move constructors
+  TraitMethodDecl (TraitMethodDecl &&other) = default;
+  TraitMethodDecl &operator= (TraitMethodDecl &&other) = default;
+
+  std::string as_string () const;
+
+  // Invalid if method name is empty, so base stripping on that.
+  void mark_for_strip () { function_name = ""; }
+  bool is_marked_for_strip () const { return function_name.empty (); }
+
+  // TODO: this mutable getter seems really dodgy. Think up better way.
+  std::vector<FunctionParam> &get_function_params () { return function_params; }
+  const std::vector<FunctionParam> &get_function_params () const
+  {
+    return function_params;
+  }
+
+  std::vector<std::unique_ptr<GenericParam>> &get_generic_params ()
+  {
+    return generic_params;
+  }
+  const std::vector<std::unique_ptr<GenericParam>> &get_generic_params () const
+  {
+    return generic_params;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Type> &get_return_type () { return return_type; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  WhereClause &get_where_clause () { return where_clause; }
+
+  SelfParam &get_self_param () { return self_param; }
+  const SelfParam &get_self_param () const { return self_param; }
+
+  FunctionQualifiers get_qualifiers () { return qualifiers; }
+};
+
+// Actual trait item method declaration within traits
+class TraitItemMethod : public TraitItem
+{
+  std::vector<Attribute> outer_attrs;
+  TraitMethodDecl decl;
+  std::unique_ptr<BlockExpr> block_expr;
+  Location locus;
+
+public:
+  // Returns whether method has a definition or is just a declaration.
+  bool has_definition () const { return block_expr != nullptr; }
+
+  TraitItemMethod (TraitMethodDecl decl, std::unique_ptr<BlockExpr> block_expr,
+		   std::vector<Attribute> outer_attrs, Location locus)
+    : TraitItem (), outer_attrs (std::move (outer_attrs)),
+      decl (std::move (decl)), block_expr (std::move (block_expr)),
+      locus (locus)
+  {}
+
+  // Copy constructor with clone
+  TraitItemMethod (TraitItemMethod const &other)
+    : outer_attrs (other.outer_attrs), decl (other.decl), locus (other.locus)
+  {
+    node_id = other.node_id;
+
+    // guard to prevent null dereference
+    if (other.block_expr != nullptr)
+      block_expr = other.block_expr->clone_block_expr ();
+  }
+
+  // Overloaded assignment operator to clone
+  TraitItemMethod &operator= (TraitItemMethod const &other)
+  {
+    TraitItem::operator= (other);
+    outer_attrs = other.outer_attrs;
+    decl = other.decl;
+    locus = other.locus;
+    node_id = other.node_id;
+
+    // guard to prevent null dereference
+    if (other.block_expr != nullptr)
+      block_expr = other.block_expr->clone_block_expr ();
+    else
+      block_expr = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  TraitItemMethod (TraitItemMethod &&other) = default;
+  TraitItemMethod &operator= (TraitItemMethod &&other) = default;
+
+  std::string as_string () const override;
+
+  Location get_locus () const { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Invalid if trait decl is empty, so base stripping on that.
+  void mark_for_strip () override { decl.mark_for_strip (); }
+  bool is_marked_for_strip () const override
+  {
+    return decl.is_marked_for_strip ();
+  }
+
+  // TODO: this mutable getter seems really dodgy. Think up better way.
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  TraitMethodDecl &get_trait_method_decl ()
+  {
+    // TODO: maybe only allow access if not marked for strip?
+    return decl;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<BlockExpr> &get_definition () { return block_expr; }
+
+protected:
+  // Clone function implementation as (not pure) virtual method
+  TraitItemMethod *clone_trait_item_impl () const override
+  {
+    return new TraitItemMethod (*this);
+  }
+};
+
+// Constant item within traits
+class TraitItemConst : public TraitItem
+{
+  std::vector<Attribute> outer_attrs;
+  Identifier name;
+  std::unique_ptr<Type> type;
+
+  // bool has_expression;
+  std::unique_ptr<Expr> expr;
+
+  Location locus;
+
+public:
+  // Whether the constant item has an associated expression.
+  bool has_expression () const { return expr != nullptr; }
+
+  TraitItemConst (Identifier name, std::unique_ptr<Type> type,
+		  std::unique_ptr<Expr> expr,
+		  std::vector<Attribute> outer_attrs, Location locus)
+    : TraitItem (), outer_attrs (std::move (outer_attrs)),
+      name (std::move (name)), type (std::move (type)), expr (std::move (expr)),
+      locus (locus)
+  {}
+
+  // Copy constructor with clones
+  TraitItemConst (TraitItemConst const &other)
+    : outer_attrs (other.outer_attrs), name (other.name), locus (other.locus)
+  {
+    node_id = other.node_id;
+
+    // guard to prevent null dereference
+    if (other.expr != nullptr)
+      expr = other.expr->clone_expr ();
+
+    // guard to prevent null dereference (only for error state)
+    if (other.type != nullptr)
+      type = other.type->clone_type ();
+  }
+
+  // Overloaded assignment operator to clone
+  TraitItemConst &operator= (TraitItemConst const &other)
+  {
+    TraitItem::operator= (other);
+    outer_attrs = other.outer_attrs;
+    name = other.name;
+    locus = other.locus;
+    node_id = other.node_id;
+
+    // guard to prevent null dereference
+    if (other.expr != nullptr)
+      expr = other.expr->clone_expr ();
+    else
+      expr = nullptr;
+
+    // guard to prevent null dereference (only for error state)
+    if (other.type != nullptr)
+      type = other.type->clone_type ();
+    else
+      type = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  TraitItemConst (TraitItemConst &&other) = default;
+  TraitItemConst &operator= (TraitItemConst &&other) = default;
+
+  std::string as_string () const override;
+
+  Location get_locus () const { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Invalid if type is null, so base stripping on that.
+  void mark_for_strip () override { type = nullptr; }
+  bool is_marked_for_strip () const override { return type == nullptr; }
+
+  // TODO: this mutable getter seems really dodgy. Think up better way.
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+
+  bool has_expr () const { return expr != nullptr; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_expr ()
+  {
+    rust_assert (has_expr ());
+    return expr;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Type> &get_type ()
+  {
+    rust_assert (type != nullptr);
+    return type;
+  }
+
+  Identifier get_identifier () const { return name; }
+
+protected:
+  // Clone function implementation as (not pure) virtual method
+  TraitItemConst *clone_trait_item_impl () const override
+  {
+    return new TraitItemConst (*this);
+  }
+};
+
+// Type items within traits
+class TraitItemType : public TraitItem
+{
+  std::vector<Attribute> outer_attrs;
+
+  Identifier name;
+
+  // bool has_type_param_bounds;
+  // TypeParamBounds type_param_bounds;
+  std::vector<std::unique_ptr<TypeParamBound>>
+    type_param_bounds; // inlined form
+
+  Location locus;
+
+public:
+  // Returns whether trait item type has type param bounds.
+  bool has_type_param_bounds () const { return !type_param_bounds.empty (); }
+
+  TraitItemType (Identifier name,
+		 std::vector<std::unique_ptr<TypeParamBound>> type_param_bounds,
+		 std::vector<Attribute> outer_attrs, Location locus)
+    : TraitItem (), outer_attrs (std::move (outer_attrs)),
+      name (std::move (name)),
+      type_param_bounds (std::move (type_param_bounds)), locus (locus)
+  {}
+
+  // Copy constructor with vector clone
+  TraitItemType (TraitItemType const &other)
+    : outer_attrs (other.outer_attrs), name (other.name), locus (other.locus)
+  {
+    node_id = other.node_id;
+    type_param_bounds.reserve (other.type_param_bounds.size ());
+    for (const auto &e : other.type_param_bounds)
+      type_param_bounds.push_back (e->clone_type_param_bound ());
+  }
+
+  // Overloaded assignment operator with vector clone
+  TraitItemType &operator= (TraitItemType const &other)
+  {
+    TraitItem::operator= (other);
+    outer_attrs = other.outer_attrs;
+    name = other.name;
+    locus = other.locus;
+    node_id = other.node_id;
+
+    type_param_bounds.reserve (other.type_param_bounds.size ());
+    for (const auto &e : other.type_param_bounds)
+      type_param_bounds.push_back (e->clone_type_param_bound ());
+
+    return *this;
+  }
+
+  // default move constructors
+  TraitItemType (TraitItemType &&other) = default;
+  TraitItemType &operator= (TraitItemType &&other) = default;
+
+  std::string as_string () const override;
+
+  Location get_locus () const { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Invalid if name is empty, so base stripping on that.
+  void mark_for_strip () override { name = ""; }
+  bool is_marked_for_strip () const override { return name.empty (); }
+
+  // TODO: this mutable getter seems really dodgy. Think up better way.
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+
+  // TODO: mutable getter seems kinda dodgy
+  std::vector<std::unique_ptr<TypeParamBound>> &get_type_param_bounds ()
+  {
+    return type_param_bounds;
+  }
+  const std::vector<std::unique_ptr<TypeParamBound>> &
+  get_type_param_bounds () const
+  {
+    return type_param_bounds;
+  }
+
+  Identifier get_identifier () const { return name; }
+
+protected:
+  // Clone function implementation as (not pure) virtual method
+  TraitItemType *clone_trait_item_impl () const override
+  {
+    return new TraitItemType (*this);
+  }
+};
+
+// Rust trait item declaration AST node
+class Trait : public VisItem
+{
+  bool has_unsafe;
+  Identifier name;
+  std::vector<std::unique_ptr<GenericParam>> generic_params;
+  std::vector<std::unique_ptr<TypeParamBound>> type_param_bounds;
+  WhereClause where_clause;
+  std::vector<Attribute> inner_attrs;
+  std::vector<std::unique_ptr<TraitItem>> trait_items;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  // Returns whether trait has generic parameters.
+  bool has_generics () const { return !generic_params.empty (); }
+
+  // Returns whether trait has type parameter bounds.
+  bool has_type_param_bounds () const { return !type_param_bounds.empty (); }
+
+  // Returns whether trait has where clause.
+  bool has_where_clause () const { return !where_clause.is_empty (); }
+
+  // Returns whether trait has trait items.
+  bool has_trait_items () const { return !trait_items.empty (); }
+
+  // Returns whether trait has inner attributes.
+  bool has_inner_attrs () const { return !inner_attrs.empty (); }
+
+  Identifier get_identifier () const { return name; }
+
+  bool is_unsafe () const { return has_unsafe; }
+
+  // Mega-constructor
+  Trait (Identifier name, bool is_unsafe,
+	 std::vector<std::unique_ptr<GenericParam>> generic_params,
+	 std::vector<std::unique_ptr<TypeParamBound>> type_param_bounds,
+	 WhereClause where_clause,
+	 std::vector<std::unique_ptr<TraitItem>> trait_items, Visibility vis,
+	 std::vector<Attribute> outer_attrs, std::vector<Attribute> inner_attrs,
+	 Location locus)
+    : VisItem (std::move (vis), std::move (outer_attrs)),
+      has_unsafe (is_unsafe), name (std::move (name)),
+      generic_params (std::move (generic_params)),
+      type_param_bounds (std::move (type_param_bounds)),
+      where_clause (std::move (where_clause)),
+      inner_attrs (std::move (inner_attrs)),
+      trait_items (std::move (trait_items)), locus (locus)
+  {}
+
+  // Copy constructor with vector clone
+  Trait (Trait const &other)
+    : VisItem (other), has_unsafe (other.has_unsafe), name (other.name),
+      where_clause (other.where_clause), inner_attrs (other.inner_attrs),
+      locus (other.locus)
+  {
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+
+    type_param_bounds.reserve (other.type_param_bounds.size ());
+    for (const auto &e : other.type_param_bounds)
+      type_param_bounds.push_back (e->clone_type_param_bound ());
+
+    trait_items.reserve (other.trait_items.size ());
+    for (const auto &e : other.trait_items)
+      trait_items.push_back (e->clone_trait_item ());
+  }
+
+  // Overloaded assignment operator with vector clone
+  Trait &operator= (Trait const &other)
+  {
+    VisItem::operator= (other);
+    name = other.name;
+    has_unsafe = other.has_unsafe;
+    where_clause = other.where_clause;
+    inner_attrs = other.inner_attrs;
+    locus = other.locus;
+
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+
+    type_param_bounds.reserve (other.type_param_bounds.size ());
+    for (const auto &e : other.type_param_bounds)
+      type_param_bounds.push_back (e->clone_type_param_bound ());
+
+    trait_items.reserve (other.trait_items.size ());
+    for (const auto &e : other.trait_items)
+      trait_items.push_back (e->clone_trait_item ());
+
+    return *this;
+  }
+
+  // default move constructors
+  Trait (Trait &&other) = default;
+  Trait &operator= (Trait &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Invalid if trait name is empty, so base stripping on that.
+  void mark_for_strip () override { name = ""; }
+  bool is_marked_for_strip () const override { return name.empty (); }
+
+  // TODO: think of better way to do this
+  const std::vector<Attribute> &get_inner_attrs () const { return inner_attrs; }
+  std::vector<Attribute> &get_inner_attrs () { return inner_attrs; }
+
+  const std::vector<std::unique_ptr<TraitItem>> &get_trait_items () const
+  {
+    return trait_items;
+  }
+  std::vector<std::unique_ptr<TraitItem>> &get_trait_items ()
+  {
+    return trait_items;
+  }
+
+  std::vector<std::unique_ptr<GenericParam>> &get_generic_params ()
+  {
+    return generic_params;
+  }
+  const std::vector<std::unique_ptr<GenericParam>> &get_generic_params () const
+  {
+    return generic_params;
+  }
+
+  std::vector<std::unique_ptr<TypeParamBound>> &get_type_param_bounds ()
+  {
+    return type_param_bounds;
+  }
+  const std::vector<std::unique_ptr<TypeParamBound>> &
+  get_type_param_bounds () const
+  {
+    return type_param_bounds;
+  }
+
+  WhereClause &get_where_clause () { return where_clause; }
+
+  void insert_implict_self (std::unique_ptr<AST::GenericParam> &&param)
+  {
+    std::vector<std::unique_ptr<GenericParam>> new_list;
+    new_list.reserve (generic_params.size () + 1);
+
+    new_list.push_back (std::move (param));
+    for (auto &p : generic_params)
+      {
+	new_list.push_back (std::move (p));
+      }
+
+    generic_params = std::move (new_list);
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  Trait *clone_item_impl () const override { return new Trait (*this); }
+};
+
+// Implementation item declaration AST node - abstract base class
+class Impl : public VisItem
+{
+  // must be protected to allow subclasses to access them properly
+protected:
+  // bool has_generics;
+  // Generics generic_params;
+  std::vector<std::unique_ptr<GenericParam>> generic_params; // inlined
+
+  std::unique_ptr<Type> trait_type;
+
+  // bool has_where_clause;
+  WhereClause where_clause;
+
+  // bool has_inner_attrs;
+  std::vector<Attribute> inner_attrs;
+
+private:
+  // doesn't really need to be protected as write access probably not needed
+  Location locus;
+
+public:
+  // Returns whether impl has generic parameters.
+  bool has_generics () const { return !generic_params.empty (); }
+
+  // Returns whether impl has where clause.
+  bool has_where_clause () const { return !where_clause.is_empty (); }
+
+  // Returns whether impl has inner attributes.
+  bool has_inner_attrs () const { return !inner_attrs.empty (); }
+
+  Location get_locus () const override final { return locus; }
+
+  // Invalid if trait type is null, so base stripping on that.
+  void mark_for_strip () override { trait_type = nullptr; }
+  bool is_marked_for_strip () const override { return trait_type == nullptr; }
+
+  // TODO: think of better way to do this
+  const std::vector<Attribute> &get_inner_attrs () const { return inner_attrs; }
+  std::vector<Attribute> &get_inner_attrs () { return inner_attrs; }
+
+  std::vector<std::unique_ptr<GenericParam>> &get_generic_params ()
+  {
+    return generic_params;
+  }
+  const std::vector<std::unique_ptr<GenericParam>> &get_generic_params () const
+  {
+    return generic_params;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  WhereClause &get_where_clause () { return where_clause; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Type> &get_type ()
+  {
+    rust_assert (trait_type != nullptr);
+    return trait_type;
+  }
+
+protected:
+  // Mega-constructor
+  Impl (std::vector<std::unique_ptr<GenericParam>> generic_params,
+	std::unique_ptr<Type> trait_type, WhereClause where_clause,
+	Visibility vis, std::vector<Attribute> inner_attrs,
+	std::vector<Attribute> outer_attrs, Location locus)
+    : VisItem (std::move (vis), std::move (outer_attrs)),
+      generic_params (std::move (generic_params)),
+      trait_type (std::move (trait_type)),
+      where_clause (std::move (where_clause)),
+      inner_attrs (std::move (inner_attrs)), locus (locus)
+  {}
+
+  // Copy constructor
+  Impl (Impl const &other)
+    : VisItem (other), where_clause (other.where_clause),
+      inner_attrs (other.inner_attrs), locus (other.locus)
+  {
+    // guard to prevent null dereference (only required if error state)
+    if (other.trait_type != nullptr)
+      trait_type = other.trait_type->clone_type ();
+
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+  }
+
+  // Assignment operator overload with cloning
+  Impl &operator= (Impl const &other)
+  {
+    VisItem::operator= (other);
+    where_clause = other.where_clause;
+    inner_attrs = other.inner_attrs;
+    locus = other.locus;
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.trait_type != nullptr)
+      trait_type = other.trait_type->clone_type ();
+    else
+      trait_type = nullptr;
+
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+
+    return *this;
+  }
+
+  // move constructors
+  Impl (Impl &&other) = default;
+  Impl &operator= (Impl &&other) = default;
+};
+
+// Regular "impl foo" impl block declaration AST node
+class InherentImpl : public Impl
+{
+  // bool has_impl_items;
+  std::vector<std::unique_ptr<InherentImplItem>> impl_items;
+
+public:
+  std::string as_string () const override;
+
+  // Returns whether inherent impl block has inherent impl items.
+  bool has_impl_items () const { return !impl_items.empty (); }
+
+  // Mega-constructor
+  InherentImpl (std::vector<std::unique_ptr<InherentImplItem>> impl_items,
+		std::vector<std::unique_ptr<GenericParam>> generic_params,
+		std::unique_ptr<Type> trait_type, WhereClause where_clause,
+		Visibility vis, std::vector<Attribute> inner_attrs,
+		std::vector<Attribute> outer_attrs, Location locus)
+    : Impl (std::move (generic_params), std::move (trait_type),
+	    std::move (where_clause), std::move (vis), std::move (inner_attrs),
+	    std::move (outer_attrs), locus),
+      impl_items (std::move (impl_items))
+  {}
+
+  // Copy constructor with vector clone
+  InherentImpl (InherentImpl const &other) : Impl (other)
+  {
+    impl_items.reserve (other.impl_items.size ());
+    for (const auto &e : other.impl_items)
+      impl_items.push_back (e->clone_inherent_impl_item ());
+  }
+
+  // Overloaded assignment operator with vector clone
+  InherentImpl &operator= (InherentImpl const &other)
+  {
+    Impl::operator= (other);
+
+    impl_items.reserve (other.impl_items.size ());
+    for (const auto &e : other.impl_items)
+      impl_items.push_back (e->clone_inherent_impl_item ());
+
+    return *this;
+  }
+
+  // default move constructors
+  InherentImpl (InherentImpl &&other) = default;
+  InherentImpl &operator= (InherentImpl &&other) = default;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: think of better way to do this
+  const std::vector<std::unique_ptr<InherentImplItem>> &get_impl_items () const
+  {
+    return impl_items;
+  }
+  std::vector<std::unique_ptr<InherentImplItem>> &get_impl_items ()
+  {
+    return impl_items;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  InherentImpl *clone_item_impl () const override
+  {
+    return new InherentImpl (*this);
+  }
+};
+
+// The "impl footrait for foo" impl block declaration AST node
+class TraitImpl : public Impl
+{
+  bool has_unsafe;
+  bool has_exclam;
+  TypePath trait_path;
+
+  // bool has_impl_items;
+  std::vector<std::unique_ptr<TraitImplItem>> impl_items;
+
+public:
+  std::string as_string () const override;
+
+  // Returns whether trait impl has impl items.
+  bool has_impl_items () const { return !impl_items.empty (); }
+
+  // Mega-constructor
+  TraitImpl (TypePath trait_path, bool is_unsafe, bool has_exclam,
+	     std::vector<std::unique_ptr<TraitImplItem>> impl_items,
+	     std::vector<std::unique_ptr<GenericParam>> generic_params,
+	     std::unique_ptr<Type> trait_type, WhereClause where_clause,
+	     Visibility vis, std::vector<Attribute> inner_attrs,
+	     std::vector<Attribute> outer_attrs, Location locus)
+    : Impl (std::move (generic_params), std::move (trait_type),
+	    std::move (where_clause), std::move (vis), std::move (inner_attrs),
+	    std::move (outer_attrs), locus),
+      has_unsafe (is_unsafe), has_exclam (has_exclam),
+      trait_path (std::move (trait_path)), impl_items (std::move (impl_items))
+  {}
+
+  // Copy constructor with vector clone
+  TraitImpl (TraitImpl const &other)
+    : Impl (other), has_unsafe (other.has_unsafe),
+      has_exclam (other.has_exclam), trait_path (other.trait_path)
+  {
+    impl_items.reserve (other.impl_items.size ());
+    for (const auto &e : other.impl_items)
+      impl_items.push_back (e->clone_trait_impl_item ());
+  }
+
+  // Overloaded assignment operator with vector clone
+  TraitImpl &operator= (TraitImpl const &other)
+  {
+    Impl::operator= (other);
+    trait_path = other.trait_path;
+    has_unsafe = other.has_unsafe;
+    has_exclam = other.has_exclam;
+
+    impl_items.reserve (other.impl_items.size ());
+    for (const auto &e : other.impl_items)
+      impl_items.push_back (e->clone_trait_impl_item ());
+
+    return *this;
+  }
+
+  // move constructors
+  TraitImpl (TraitImpl &&other) = default;
+  TraitImpl &operator= (TraitImpl &&other) = default;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  bool is_unsafe () const { return has_unsafe; };
+  bool is_exclam () const { return has_exclam; }
+
+  // TODO: think of better way to do this
+  const std::vector<std::unique_ptr<TraitImplItem>> &get_impl_items () const
+  {
+    return impl_items;
+  }
+  std::vector<std::unique_ptr<TraitImplItem>> &get_impl_items ()
+  {
+    return impl_items;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  TypePath &get_trait_path ()
+  {
+    // TODO: assert that trait path is not empty?
+    return trait_path;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  TraitImpl *clone_item_impl () const override { return new TraitImpl (*this); }
+};
+
+#if 0
+// Abstract base class for an item used inside an extern block
+class ExternalItem
+{
+  // bool has_outer_attrs;
+  std::vector<Attribute> outer_attrs;
+
+  // bool has_visibility;
+  Visibility visibility;
+
+  Identifier item_name;
+  Location locus;
+
+public:
+  virtual ~ExternalItem () {}
+
+  /* TODO: spec syntax rules state that "MacroInvocationSemi" can be used as 
+   * ExternalItem, but text body isn't so clear. Adding MacroInvocationSemi 
+   * support would require a lot of refactoring. */
+
+  // Returns whether item has outer attributes.
+  bool has_outer_attrs () const { return !outer_attrs.empty (); }
+
+  // Returns whether item has non-default visibility.
+  bool has_visibility () const { return !visibility.is_error (); }
+
+  // Unique pointer custom clone function
+  std::unique_ptr<ExternalItem> clone_external_item () const
+  {
+    return std::unique_ptr<ExternalItem> (clone_external_item_impl ());
+  }
+
+  virtual std::string as_string () const;
+
+  Location get_locus () const override final { return locus; }
+
+  virtual void accept_vis (ASTVisitor &vis) = 0;
+
+  // TODO: make virtual? Would be more flexible.
+  // Based on idea that name should never be empty.
+  void mark_for_strip () { item_name = ""; };
+  bool is_marked_for_strip () const { return item_name.empty (); };
+
+protected:
+  ExternalItem (Identifier item_name, Visibility vis,
+		std::vector<Attribute> outer_attrs, Location locus)
+    : outer_attrs (std::move (outer_attrs)), visibility (std::move (vis)),
+      item_name (std::move (item_name)), locus (locus)
+  {}
+
+  // Copy constructor
+  ExternalItem (ExternalItem const &other)
+    : outer_attrs (other.outer_attrs), visibility (other.visibility),
+      item_name (other.item_name), locus (other.locus)
+  {}
+
+  // Overloaded assignment operator to clone
+  ExternalItem &operator= (ExternalItem const &other)
+  {
+    item_name = other.item_name;
+    visibility = other.visibility;
+    outer_attrs = other.outer_attrs;
+    locus = other.locus;
+
+    return *this;
+  }
+
+  // move constructors
+  ExternalItem (ExternalItem &&other) = default;
+  ExternalItem &operator= (ExternalItem &&other) = default;
+
+  // Clone function implementation as pure virtual method
+  virtual ExternalItem *clone_external_item_impl () const = 0;
+
+  // possibly make this public if required
+  std::string get_item_name () const { return item_name; }
+};
+#endif
+
+// A static item used in an extern block
+class ExternalStaticItem : public ExternalItem
+{
+  // bool has_outer_attrs;
+  std::vector<Attribute> outer_attrs;
+
+  // bool has_visibility;
+  Visibility visibility;
+
+  Identifier item_name;
+  Location locus;
+
+  bool has_mut;
+  std::unique_ptr<Type> item_type;
+
+public:
+  ExternalStaticItem (Identifier item_name, std::unique_ptr<Type> item_type,
+		      bool is_mut, Visibility vis,
+		      std::vector<Attribute> outer_attrs, Location locus)
+    : ExternalItem (), outer_attrs (std::move (outer_attrs)),
+      visibility (std::move (vis)), item_name (std::move (item_name)),
+      locus (locus), has_mut (is_mut), item_type (std::move (item_type))
+  {}
+
+  // Copy constructor
+  ExternalStaticItem (ExternalStaticItem const &other)
+    : outer_attrs (other.outer_attrs), visibility (other.visibility),
+      item_name (other.item_name), locus (other.locus), has_mut (other.has_mut)
+  {
+    node_id = other.node_id;
+    // guard to prevent null dereference (only required if error state)
+    if (other.item_type != nullptr)
+      item_type = other.item_type->clone_type ();
+  }
+
+  // Overloaded assignment operator to clone
+  ExternalStaticItem &operator= (ExternalStaticItem const &other)
+  {
+    node_id = other.node_id;
+    outer_attrs = other.outer_attrs;
+    visibility = other.visibility;
+    item_name = other.item_name;
+    locus = other.locus;
+    has_mut = other.has_mut;
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.item_type != nullptr)
+      item_type = other.item_type->clone_type ();
+    else
+      item_type = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  ExternalStaticItem (ExternalStaticItem &&other) = default;
+  ExternalStaticItem &operator= (ExternalStaticItem &&other) = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Returns whether item has outer attributes.
+  bool has_outer_attrs () const { return !outer_attrs.empty (); }
+
+  // Returns whether item has non-default visibility.
+  bool has_visibility () const { return !visibility.is_error (); }
+
+  Location get_locus () const { return locus; }
+
+  // Based on idea that type should never be null.
+  void mark_for_strip () override { item_type = nullptr; };
+  bool is_marked_for_strip () const override { return item_type == nullptr; };
+
+  // TODO: this mutable getter seems really dodgy. Think up better way.
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Type> &get_type ()
+  {
+    rust_assert (item_type != nullptr);
+    return item_type;
+  }
+
+  Identifier get_identifier () const { return item_name; }
+
+  const Visibility &get_visibility () const { return visibility; }
+
+  bool is_mut () const { return has_mut; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  ExternalStaticItem *clone_external_item_impl () const override
+  {
+    return new ExternalStaticItem (*this);
+  }
+};
+
+// A named function parameter used in external functions
+struct NamedFunctionParam
+{
+private:
+  // bool has_name;   // otherwise is _
+  std::string name;
+
+  std::unique_ptr<Type> param_type;
+
+  // seemingly new since writing this node
+  std::vector<Attribute> outer_attrs;
+
+  NodeId node_id;
+  Location locus;
+
+public:
+  /* Returns whether the named function parameter has a name (i.e. name is not
+   * '_'). */
+  bool has_name () const { return name != "_"; }
+
+  bool has_outer_attrs () const { return !outer_attrs.empty (); }
+
+  // Returns whether the named function parameter is in an error state.
+  bool is_error () const
+  {
+    // also if identifier is "" but that is probably more costly to compute
+    return param_type == nullptr;
+  }
+
+  std::string get_name () const { return name; }
+
+  // Creates an error state named function parameter.
+  static NamedFunctionParam create_error ()
+  {
+    return NamedFunctionParam ("", nullptr, {}, Location ());
+  }
+
+  NamedFunctionParam (std::string name, std::unique_ptr<Type> param_type,
+		      std::vector<Attribute> outer_attrs, Location locus)
+    : name (std::move (name)), param_type (std::move (param_type)),
+      outer_attrs (std::move (outer_attrs)),
+      node_id (Analysis::Mappings::get ()->get_next_node_id ()), locus (locus)
+  {}
+
+  // Copy constructor
+  NamedFunctionParam (NamedFunctionParam const &other)
+    : name (other.name), outer_attrs (other.outer_attrs)
+  {
+    node_id = other.node_id;
+    // guard to prevent null dereference (only required if error state)
+    if (other.param_type != nullptr)
+      param_type = other.param_type->clone_type ();
+  }
+
+  ~NamedFunctionParam () = default;
+
+  // Overloaded assignment operator to clone
+  NamedFunctionParam &operator= (NamedFunctionParam const &other)
+  {
+    node_id = other.node_id;
+    name = other.name;
+    // has_name = other.has_name;
+    outer_attrs = other.outer_attrs;
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.param_type != nullptr)
+      param_type = other.param_type->clone_type ();
+    else
+      param_type = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  NamedFunctionParam (NamedFunctionParam &&other) = default;
+  NamedFunctionParam &operator= (NamedFunctionParam &&other) = default;
+
+  std::string as_string () const;
+
+  // Based on idea that nane should never be empty.
+  void mark_for_strip () { param_type = nullptr; };
+  bool is_marked_for_strip () const { return is_error (); };
+
+  // TODO: this mutable getter seems really dodgy. Think up better way.
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Type> &get_type ()
+  {
+    rust_assert (param_type != nullptr);
+    return param_type;
+  }
+
+  NodeId get_node_id () const { return node_id; }
+};
+
+// A function item used in an extern block
+class ExternalFunctionItem : public ExternalItem
+{
+  // bool has_outer_attrs;
+  std::vector<Attribute> outer_attrs;
+
+  // bool has_visibility;
+  Visibility visibility;
+
+  Identifier item_name;
+  Location locus;
+
+  // bool has_generics;
+  // Generics generic_params;
+  std::vector<std::unique_ptr<GenericParam>> generic_params; // inlined
+
+  // bool has_return_type;
+  // FunctionReturnType return_type;
+  std::unique_ptr<Type> return_type; // inlined
+
+  // bool has_where_clause;
+  WhereClause where_clause;
+
+  std::vector<NamedFunctionParam> function_params;
+  bool has_variadics;
+  std::vector<Attribute> variadic_outer_attrs;
+
+public:
+  // Returns whether item has generic parameters.
+  bool has_generics () const { return !generic_params.empty (); }
+
+  // Returns whether item has a return type (otherwise void).
+  bool has_return_type () const { return return_type != nullptr; }
+
+  // Returns whether item has a where clause.
+  bool has_where_clause () const { return !where_clause.is_empty (); }
+
+  // Returns whether item has outer attributes.
+  bool has_outer_attrs () const { return !outer_attrs.empty (); }
+
+  // Returns whether item has non-default visibility.
+  bool has_visibility () const { return !visibility.is_error (); }
+
+  // Returns whether item has variadic parameters.
+  bool is_variadic () const { return has_variadics; }
+
+  // Returns whether item has outer attributes on its variadic parameters.
+  bool has_variadic_outer_attrs () const
+  {
+    return !variadic_outer_attrs.empty ();
+  }
+
+  Location get_locus () const { return locus; }
+
+  const Visibility &get_visibility () const { return visibility; }
+
+  ExternalFunctionItem (
+    Identifier item_name,
+    std::vector<std::unique_ptr<GenericParam>> generic_params,
+    std::unique_ptr<Type> return_type, WhereClause where_clause,
+    std::vector<NamedFunctionParam> function_params, bool has_variadics,
+    std::vector<Attribute> variadic_outer_attrs, Visibility vis,
+    std::vector<Attribute> outer_attrs, Location locus)
+    : ExternalItem (), outer_attrs (std::move (outer_attrs)),
+      visibility (std::move (vis)), item_name (std::move (item_name)),
+      locus (locus), generic_params (std::move (generic_params)),
+      return_type (std::move (return_type)),
+      where_clause (std::move (where_clause)),
+      function_params (std::move (function_params)),
+      has_variadics (has_variadics),
+      variadic_outer_attrs (std::move (variadic_outer_attrs))
+  {
+    // TODO: assert that if has variadic outer attrs, then has_variadics is
+    // true?
+  }
+
+  // Copy constructor with clone
+  ExternalFunctionItem (ExternalFunctionItem const &other)
+    : outer_attrs (other.outer_attrs), visibility (other.visibility),
+      item_name (other.item_name), locus (other.locus),
+      where_clause (other.where_clause),
+      function_params (other.function_params),
+      has_variadics (other.has_variadics),
+      variadic_outer_attrs (other.variadic_outer_attrs)
+  {
+    node_id = other.node_id;
+    // guard to prevent null pointer dereference
+    if (other.return_type != nullptr)
+      return_type = other.return_type->clone_type ();
+
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+  }
+
+  // Overloaded assignment operator with clone
+  ExternalFunctionItem &operator= (ExternalFunctionItem const &other)
+  {
+    outer_attrs = other.outer_attrs;
+    visibility = other.visibility;
+    item_name = other.item_name;
+    locus = other.locus;
+    where_clause = other.where_clause;
+    function_params = other.function_params;
+    has_variadics = other.has_variadics;
+    variadic_outer_attrs = other.variadic_outer_attrs;
+    node_id = other.node_id;
+
+    // guard to prevent null pointer dereference
+    if (other.return_type != nullptr)
+      return_type = other.return_type->clone_type ();
+    else
+      return_type = nullptr;
+
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+
+    return *this;
+  }
+
+  // move constructors
+  ExternalFunctionItem (ExternalFunctionItem &&other) = default;
+  ExternalFunctionItem &operator= (ExternalFunctionItem &&other) = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Based on idea that nane should never be empty.
+  void mark_for_strip () override { item_name = ""; };
+  bool is_marked_for_strip () const override { return item_name.empty (); };
+
+  // TODO: this mutable getter seems really dodgy. Think up better way.
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+
+  std::vector<NamedFunctionParam> &get_function_params ()
+  {
+    return function_params;
+  }
+  const std::vector<NamedFunctionParam> &get_function_params () const
+  {
+    return function_params;
+  }
+
+  std::vector<std::unique_ptr<GenericParam>> &get_generic_params ()
+  {
+    return generic_params;
+  }
+  const std::vector<std::unique_ptr<GenericParam>> &get_generic_params () const
+  {
+    return generic_params;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  WhereClause &get_where_clause () { return where_clause; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Type> &get_return_type ()
+  {
+    rust_assert (has_return_type ());
+    return return_type;
+  }
+
+  Identifier get_identifier () const { return item_name; };
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  ExternalFunctionItem *clone_external_item_impl () const override
+  {
+    return new ExternalFunctionItem (*this);
+  }
+};
+
+// An extern block AST node
+class ExternBlock : public VisItem
+{
+  // bool has_abi;
+  std::string abi;
+
+  // bool has_inner_attrs;
+  std::vector<Attribute> inner_attrs;
+
+  // bool has_extern_items;
+  std::vector<std::unique_ptr<ExternalItem>> extern_items;
+
+  Location locus;
+
+  // TODO: find another way to store this to save memory?
+  bool marked_for_strip = false;
+
+public:
+  std::string as_string () const override;
+
+  // Returns whether extern block has inner attributes.
+  bool has_inner_attrs () const { return !inner_attrs.empty (); }
+
+  // Returns whether extern block has extern items.
+  bool has_extern_items () const { return !extern_items.empty (); }
+
+  // Returns whether extern block has ABI name.
+  bool has_abi () const { return !abi.empty (); }
+
+  std::string get_abi () const { return abi; }
+
+  ExternBlock (std::string abi,
+	       std::vector<std::unique_ptr<ExternalItem>> extern_items,
+	       Visibility vis, std::vector<Attribute> inner_attrs,
+	       std::vector<Attribute> outer_attrs, Location locus)
+    : VisItem (std::move (vis), std::move (outer_attrs)), abi (std::move (abi)),
+      inner_attrs (std::move (inner_attrs)),
+      extern_items (std::move (extern_items)), locus (locus)
+  {}
+
+  // Copy constructor with vector clone
+  ExternBlock (ExternBlock const &other)
+    : VisItem (other), abi (other.abi), inner_attrs (other.inner_attrs),
+      locus (other.locus), marked_for_strip (other.marked_for_strip)
+  {
+    extern_items.reserve (other.extern_items.size ());
+    for (const auto &e : other.extern_items)
+      extern_items.push_back (e->clone_external_item ());
+  }
+
+  // Overloaded assignment operator with vector clone
+  ExternBlock &operator= (ExternBlock const &other)
+  {
+    VisItem::operator= (other);
+    abi = other.abi;
+    inner_attrs = other.inner_attrs;
+    locus = other.locus;
+    marked_for_strip = other.marked_for_strip;
+
+    extern_items.reserve (other.extern_items.size ());
+    for (const auto &e : other.extern_items)
+      extern_items.push_back (e->clone_external_item ());
+
+    return *this;
+  }
+
+  // move constructors
+  ExternBlock (ExternBlock &&other) = default;
+  ExternBlock &operator= (ExternBlock &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Can't think of any invalid invariants, so store boolean.
+  void mark_for_strip () override { marked_for_strip = true; }
+  bool is_marked_for_strip () const override { return marked_for_strip; }
+
+  // TODO: think of better way to do this
+  const std::vector<std::unique_ptr<ExternalItem>> &get_extern_items () const
+  {
+    return extern_items;
+  }
+  std::vector<std::unique_ptr<ExternalItem>> &get_extern_items ()
+  {
+    return extern_items;
+  }
+
+  // TODO: think of better way to do this
+  const std::vector<Attribute> &get_inner_attrs () const { return inner_attrs; }
+  std::vector<Attribute> &get_inner_attrs () { return inner_attrs; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  ExternBlock *clone_item_impl () const override
+  {
+    return new ExternBlock (*this);
+  }
+};
+
+// Replaced with forward decls - defined in "rust-macro.h"
+class MacroItem;
+class MacroRulesDefinition;
+} // namespace AST
+} // namespace Rust
+
+#endif
diff --git a/gcc/rust/ast/rust-macro.h b/gcc/rust/ast/rust-macro.h
new file mode 100644
index 00000000000..ce515db0aad
--- /dev/null
+++ b/gcc/rust/ast/rust-macro.h
@@ -0,0 +1,958 @@
+// 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_AST_MACRO_H
+#define RUST_AST_MACRO_H
+
+#include "rust-ast.h"
+#include "rust-location.h"
+#include <string>
+
+namespace Rust {
+namespace AST {
+
+// Decls as definitions moved to rust-ast.h
+class MacroItem;
+
+class MacroFragSpec
+{
+public:
+  enum Kind
+  {
+    BLOCK,
+    EXPR,
+    IDENT,
+    ITEM,
+    LIFETIME,
+    LITERAL,
+    META,
+    PAT,
+    PATH,
+    STMT,
+    TT,
+    TY,
+    VIS,
+    INVALID // not really a specifier, but used to mark invalid one passed in
+  };
+
+  MacroFragSpec (Kind kind) : kind (kind) {}
+
+  static MacroFragSpec get_frag_spec_from_str (const std::string &str)
+  {
+    if (str == "block")
+      return MacroFragSpec (BLOCK);
+    else if (str == "expr")
+      return MacroFragSpec (EXPR);
+    else if (str == "ident")
+      return MacroFragSpec (IDENT);
+    else if (str == "item")
+      return MacroFragSpec (ITEM);
+    else if (str == "lifetime")
+      return MacroFragSpec (LIFETIME);
+    else if (str == "literal")
+      return MacroFragSpec (LITERAL);
+    else if (str == "meta")
+      return MacroFragSpec (META);
+    else if (str == "pat" || str == "pat_param")
+      return MacroFragSpec (PAT);
+    else if (str == "path")
+      return MacroFragSpec (PATH);
+    else if (str == "stmt")
+      return MacroFragSpec (STMT);
+    else if (str == "tt")
+      return MacroFragSpec (TT);
+    else if (str == "ty")
+      return MacroFragSpec (TY);
+    else if (str == "vis")
+      return MacroFragSpec (VIS);
+    else
+      {
+	// error_at("invalid string '%s' used as fragment specifier",
+	// str->c_str()));
+	return MacroFragSpec (INVALID);
+      }
+  }
+
+  Kind get_kind () const { return kind; }
+  bool is_error () const { return kind == Kind::INVALID; }
+
+  // Converts a frag spec enum item to a string form.
+  std::string as_string () const
+  {
+    switch (kind)
+      {
+      case BLOCK:
+	return "block";
+      case EXPR:
+	return "expr";
+      case IDENT:
+	return "ident";
+      case ITEM:
+	return "item";
+      case LIFETIME:
+	return "lifetime";
+      case LITERAL:
+	return "literal";
+      case META:
+	return "meta";
+      case PAT:
+	return "pat";
+      case PATH:
+	return "path";
+      case STMT:
+	return "stmt";
+      case TT:
+	return "tt";
+      case TY:
+	return "ty";
+      case VIS:
+	return "vis";
+      case INVALID:
+	return "INVALID_FRAG_SPEC";
+      default:
+	return "ERROR_MARK_STRING - unknown frag spec";
+      }
+  }
+
+  bool has_follow_set_restrictions () const
+  {
+    switch (kind)
+      {
+      case EXPR:
+      case STMT:
+      case PAT:
+      case PATH:
+      case TY:
+      case VIS:
+	return true;
+      default:
+	return false;
+      }
+  }
+
+  bool has_follow_set_fragment_restrictions () const
+  {
+    switch (kind)
+      {
+      case PAT:
+      case TY:
+      case VIS:
+	return true;
+      default:
+	return false;
+      }
+  }
+
+private:
+  Kind kind;
+};
+
+// A macro match that has an identifier and fragment spec
+class MacroMatchFragment : public MacroMatch
+{
+  Identifier ident;
+  MacroFragSpec frag_spec;
+  Location locus;
+
+public:
+  MacroMatchFragment (Identifier ident, MacroFragSpec frag_spec, Location locus)
+    : ident (std::move (ident)), frag_spec (frag_spec), locus (locus)
+  {}
+
+  // Returns whether macro match fragment is in an error state.
+  bool is_error () const
+  {
+    return frag_spec.get_kind () == MacroFragSpec::INVALID;
+  }
+
+  // Creates an error state macro match fragment.
+  static MacroMatchFragment create_error (Location locus)
+  {
+    return MacroMatchFragment (std::string (""),
+			       MacroFragSpec (MacroFragSpec::Kind::INVALID),
+			       locus);
+  }
+
+  std::string as_string () const override;
+  Location get_match_locus () const override { return locus; };
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  MacroMatchType get_macro_match_type () const override
+  {
+    return MacroMatchType::Fragment;
+  }
+
+  Identifier get_ident () const { return ident; }
+  const MacroFragSpec &get_frag_spec () const { return frag_spec; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  MacroMatchFragment *clone_macro_match_impl () const override
+  {
+    return new MacroMatchFragment (*this);
+  }
+};
+
+// A repetition macro match
+class MacroMatchRepetition : public MacroMatch
+{
+public:
+  enum MacroRepOp
+  {
+    NONE,
+    ANY,
+    ONE_OR_MORE,
+    ZERO_OR_ONE,
+  };
+
+private:
+  std::vector<std::unique_ptr<MacroMatch> > matches;
+  MacroRepOp op;
+
+  // bool has_sep;
+  typedef Token MacroRepSep;
+  // any token except delimiters and repetition operators
+  std::unique_ptr<MacroRepSep> sep;
+  Location locus;
+
+public:
+  // Returns whether macro match repetition has separator token.
+  bool has_sep () const { return sep != nullptr; }
+
+  MacroMatchRepetition (std::vector<std::unique_ptr<MacroMatch> > matches,
+			MacroRepOp op, std::unique_ptr<MacroRepSep> sep,
+			Location locus)
+    : matches (std::move (matches)), op (op), sep (std::move (sep)),
+      locus (locus)
+  {}
+
+  // Copy constructor with clone
+  MacroMatchRepetition (MacroMatchRepetition const &other)
+    : op (other.op), locus (other.locus)
+  {
+    // guard to protect from null pointer dereference
+    if (other.sep != nullptr)
+      sep = other.sep->clone_token ();
+
+    matches.reserve (other.matches.size ());
+    for (const auto &e : other.matches)
+      matches.push_back (e->clone_macro_match ());
+  }
+
+  // Overloaded assignment operator to clone
+  MacroMatchRepetition &operator= (MacroMatchRepetition const &other)
+  {
+    op = other.op;
+    locus = other.locus;
+
+    // guard to protect from null pointer dereference
+    if (other.sep != nullptr)
+      sep = other.sep->clone_token ();
+    else
+      sep = nullptr;
+
+    matches.reserve (other.matches.size ());
+    for (const auto &e : other.matches)
+      matches.push_back (e->clone_macro_match ());
+
+    return *this;
+  }
+
+  // move constructors
+  MacroMatchRepetition (MacroMatchRepetition &&other) = default;
+  MacroMatchRepetition &operator= (MacroMatchRepetition &&other) = default;
+
+  std::string as_string () const override;
+  Location get_match_locus () const override { return locus; };
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  MacroMatchType get_macro_match_type () const override
+  {
+    return MacroMatchType::Repetition;
+  }
+
+  MacroRepOp get_op () const { return op; }
+  const std::unique_ptr<MacroRepSep> &get_sep () const { return sep; }
+  std::vector<std::unique_ptr<MacroMatch> > &get_matches () { return matches; }
+  const std::vector<std::unique_ptr<MacroMatch> > &get_matches () const
+  {
+    return matches;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  MacroMatchRepetition *clone_macro_match_impl () const override
+  {
+    return new MacroMatchRepetition (*this);
+  }
+};
+
+// can't inline due to polymorphism
+class MacroMatcher : public MacroMatch
+{
+  DelimType delim_type;
+  std::vector<std::unique_ptr<MacroMatch> > matches;
+  Location locus;
+
+  // TODO: think of way to mark invalid that doesn't take up more space
+  bool is_invalid;
+
+public:
+  MacroMatcher (DelimType delim_type,
+		std::vector<std::unique_ptr<MacroMatch> > matches,
+		Location locus)
+    : delim_type (delim_type), matches (std::move (matches)), locus (locus),
+      is_invalid (false)
+  {}
+
+  // copy constructor with vector clone
+  MacroMatcher (MacroMatcher const &other)
+    : delim_type (other.delim_type), locus (other.locus)
+  {
+    matches.reserve (other.matches.size ());
+    for (const auto &e : other.matches)
+      matches.push_back (e->clone_macro_match ());
+  }
+
+  // overloaded assignment operator with vector clone
+  MacroMatcher &operator= (MacroMatcher const &other)
+  {
+    delim_type = other.delim_type;
+    locus = other.locus;
+
+    matches.reserve (other.matches.size ());
+    for (const auto &e : other.matches)
+      matches.push_back (e->clone_macro_match ());
+
+    return *this;
+  }
+
+  // move constructors
+  MacroMatcher (MacroMatcher &&other) = default;
+  MacroMatcher &operator= (MacroMatcher &&other) = default;
+
+  // Creates an error state macro matcher.
+  static MacroMatcher create_error (Location locus)
+  {
+    return MacroMatcher (true, locus);
+  }
+
+  // Returns whether MacroMatcher is in an error state.
+  bool is_error () const { return is_invalid; }
+  Location get_match_locus () const override { return locus; }
+
+  std::string as_string () const override;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  MacroMatchType get_macro_match_type () const override
+  {
+    return MacroMatchType::Matcher;
+  }
+
+  DelimType get_delim_type () const { return delim_type; }
+  std::vector<std::unique_ptr<MacroMatch> > &get_matches () { return matches; }
+  const std::vector<std::unique_ptr<MacroMatch> > &get_matches () const
+  {
+    return matches;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  MacroMatcher *clone_macro_match_impl () const override
+  {
+    return new MacroMatcher (*this);
+  }
+
+  // constructor only used to create error matcher
+  MacroMatcher (bool is_invalid, Location locus)
+    : delim_type (PARENS), locus (locus), is_invalid (is_invalid)
+  {}
+};
+
+// TODO: inline?
+struct MacroTranscriber
+{
+private:
+  DelimTokenTree token_tree;
+  Location locus;
+
+public:
+  MacroTranscriber (DelimTokenTree token_tree, Location locus)
+    : token_tree (std::move (token_tree)), locus (locus)
+  {}
+
+  std::string as_string () const { return token_tree.as_string (); }
+
+  Location get_locus () const { return locus; }
+
+  DelimTokenTree &get_token_tree () { return token_tree; }
+};
+
+// A macro rule? Matcher and transcriber pair?
+struct MacroRule
+{
+private:
+  MacroMatcher matcher;
+  MacroTranscriber transcriber;
+  Location locus;
+
+public:
+  MacroRule (MacroMatcher matcher, MacroTranscriber transcriber, Location locus)
+    : matcher (std::move (matcher)), transcriber (std::move (transcriber)),
+      locus (locus)
+  {}
+
+  // Returns whether macro rule is in error state.
+  bool is_error () const { return matcher.is_error (); }
+
+  // Creates an error state macro rule.
+  static MacroRule create_error (Location locus)
+  {
+    return MacroRule (MacroMatcher::create_error (locus),
+		      MacroTranscriber (DelimTokenTree::create_empty (),
+					Location ()),
+		      locus);
+  }
+
+  Location get_locus () const { return locus; }
+
+  std::string as_string () const;
+
+  MacroMatcher &get_matcher () { return matcher; }
+  MacroTranscriber &get_transcriber () { return transcriber; }
+};
+
+// A macro rules definition item AST node
+class MacroRulesDefinition : public MacroItem
+{
+  std::vector<Attribute> outer_attrs;
+  Identifier rule_name;
+  // MacroRulesDef rules_def;
+  // only curly without required semicolon at end
+  DelimType delim_type;
+  // MacroRules rules;
+  std::vector<MacroRule> rules; // inlined form
+  Location locus;
+
+  std::function<ASTFragment (Location, MacroInvocData &)>
+    associated_transcriber;
+  // Since we can't compare std::functions, we need to use an extra boolean
+  bool is_builtin_rule;
+
+  /**
+   * Default function to use as an associated transcriber. This function should
+   * never be called, hence the gcc_unreachable().
+   * If this function is used, then the macro is not builtin and the compiler
+   * should make use of the actual rules. If the macro is builtin, then another
+   * associated transcriber should be used
+   */
+  static ASTFragment dummy_builtin (Location, MacroInvocData &)
+  {
+    gcc_unreachable ();
+    return ASTFragment::create_error ();
+  }
+
+  /* NOTE: in rustc, macro definitions are considered (and parsed as) a type
+   * of macro, whereas here they are considered part of the language itself.
+   * I am not aware of the implications of this decision. The rustc spec does
+   * mention that using the same parser for macro definitions and invocations
+   * is "extremely self-referential and non-intuitive". */
+
+public:
+  std::string as_string () const override;
+
+  MacroRulesDefinition (Identifier rule_name, DelimType delim_type,
+			std::vector<MacroRule> rules,
+			std::vector<Attribute> outer_attrs, Location locus)
+    : outer_attrs (std::move (outer_attrs)), rule_name (std::move (rule_name)),
+      delim_type (delim_type), rules (std::move (rules)), locus (locus),
+      associated_transcriber (dummy_builtin), is_builtin_rule (false)
+  {}
+
+  MacroRulesDefinition (Identifier builtin_name, DelimType delim_type,
+			std::function<ASTFragment (Location, MacroInvocData &)>
+			  associated_transcriber)
+    : outer_attrs (std::vector<Attribute> ()), rule_name (builtin_name),
+      delim_type (delim_type), rules (std::vector<MacroRule> ()),
+      locus (Location ()), associated_transcriber (associated_transcriber),
+      is_builtin_rule (true)
+  {}
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Invalid if rule name is empty, so base stripping on that.
+  void mark_for_strip () override { rule_name = ""; }
+  bool is_marked_for_strip () const override { return rule_name.empty (); }
+
+  // TODO: this mutable getter seems really dodgy. Think up better way.
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+
+  std::vector<MacroRule> &get_macro_rules () { return rules; }
+  const std::vector<MacroRule> &get_macro_rules () const { return rules; }
+
+  Location get_locus () const override final { return locus; }
+
+  Identifier get_rule_name () const { return rule_name; }
+
+  std::vector<MacroRule> &get_rules () { return rules; }
+  const std::vector<MacroRule> &get_rules () const { return rules; }
+
+  bool is_builtin () const { return is_builtin_rule; }
+  const std::function<ASTFragment (Location, MacroInvocData &)> &
+  get_builtin_transcriber () const
+  {
+    rust_assert (is_builtin ());
+    return associated_transcriber;
+  }
+  void set_builtin_transcriber (
+    std::function<ASTFragment (Location, MacroInvocData &)> transcriber)
+  {
+    associated_transcriber = transcriber;
+    is_builtin_rule = true;
+  }
+
+  Kind get_ast_kind () const override { return Kind::MACRO_RULES_DEFINITION; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  MacroRulesDefinition *clone_item_impl () const override
+  {
+    return new MacroRulesDefinition (*this);
+  }
+};
+
+/* AST node of a macro invocation, which is replaced by the macro result at
+ * compile time */
+class MacroInvocation : public TypeNoBounds,
+			public Pattern,
+			public MacroItem,
+			public TraitItem,
+			public TraitImplItem,
+			public InherentImplItem,
+			public ExternalItem,
+			public ExprWithoutBlock
+{
+  std::vector<Attribute> outer_attrs;
+  MacroInvocData invoc_data;
+  Location locus;
+
+  // Important for when we actually expand the macro
+  bool is_semi_coloned;
+
+  NodeId node_id;
+
+public:
+  std::string as_string () const override;
+
+  MacroInvocation (MacroInvocData invoc_data,
+		   std::vector<Attribute> outer_attrs, Location locus,
+		   bool is_semi_coloned = false)
+    : outer_attrs (std::move (outer_attrs)),
+      invoc_data (std::move (invoc_data)), locus (locus),
+      is_semi_coloned (is_semi_coloned),
+      node_id (Analysis::Mappings::get ()->get_next_node_id ())
+  {}
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Invalid if path is empty, so base stripping on that.
+  void mark_for_strip () override { invoc_data.mark_for_strip (); }
+  bool is_marked_for_strip () const override
+  {
+    return invoc_data.is_marked_for_strip ();
+  }
+
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+  void set_outer_attrs (std::vector<Attribute> new_attrs) override
+  {
+    outer_attrs = std::move (new_attrs);
+  }
+
+  NodeId get_pattern_node_id () const override final
+  {
+    return ExprWithoutBlock::get_node_id ();
+  }
+
+  Kind get_ast_kind () const override { return Kind::MACRO_INVOCATION; }
+
+  NodeId get_macro_node_id () const { return node_id; }
+
+  MacroInvocData &get_invoc_data () { return invoc_data; }
+
+  bool has_semicolon () const { return is_semi_coloned; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  MacroInvocation *clone_pattern_impl () const final override
+  {
+    return clone_macro_invocation_impl ();
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  MacroInvocation *clone_expr_without_block_impl () const final override
+  {
+    return clone_macro_invocation_impl ();
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  MacroInvocation *clone_type_no_bounds_impl () const final override
+  {
+    return clone_macro_invocation_impl ();
+  }
+
+  MacroInvocation *clone_external_item_impl () const final override
+  {
+    return clone_macro_invocation_impl ();
+  }
+
+  /*virtual*/ MacroInvocation *clone_macro_invocation_impl () const
+  {
+    return new MacroInvocation (*this);
+  }
+
+  Item *clone_item_impl () const override
+  {
+    return clone_macro_invocation_impl ();
+  }
+
+  bool is_item () const override { return !has_semicolon (); }
+
+  TraitItem *clone_trait_item_impl () const override
+  {
+    return clone_macro_invocation_impl ();
+  };
+
+  TraitImplItem *clone_trait_impl_item_impl () const override
+  {
+    return clone_macro_invocation_impl ();
+  };
+
+  InherentImplItem *clone_inherent_impl_item_impl () const override
+  {
+    return clone_macro_invocation_impl ();
+  }
+
+  ExprWithoutBlock *to_stmt () const override
+
+  {
+    auto new_impl = clone_macro_invocation_impl ();
+    new_impl->is_semi_coloned = true;
+
+    return new_impl;
+  }
+};
+
+// more generic meta item path-only form
+class MetaItemPath : public MetaItem
+{
+  SimplePath path;
+
+public:
+  MetaItemPath (SimplePath path) : path (std::move (path)) {}
+
+  std::string as_string () const override { return path.as_string (); }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // HACK: used to simplify parsing - returns non-empty only in this case
+  SimplePath to_path_item () const override
+  {
+    // this should copy construct - TODO ensure it does
+    return path;
+  }
+
+  bool check_cfg_predicate (const Session &session) const override;
+
+  Attribute to_attribute () const override;
+
+protected:
+  // Use covariance to implement clone function as returning this type
+  MetaItemPath *clone_meta_item_inner_impl () const override
+  {
+    return new MetaItemPath (*this);
+  }
+};
+
+// more generic meta item sequence form
+class MetaItemSeq : public MetaItem
+{
+  SimplePath path;
+  std::vector<std::unique_ptr<MetaItemInner> > seq;
+
+public:
+  MetaItemSeq (SimplePath path,
+	       std::vector<std::unique_ptr<MetaItemInner> > seq)
+    : path (std::move (path)), seq (std::move (seq))
+  {}
+
+  // copy constructor with vector clone
+  MetaItemSeq (const MetaItemSeq &other) : path (other.path)
+  {
+    seq.reserve (other.seq.size ());
+    for (const auto &e : other.seq)
+      seq.push_back (e->clone_meta_item_inner ());
+  }
+
+  // overloaded assignment operator with vector clone
+  MetaItemSeq &operator= (const MetaItemSeq &other)
+  {
+    MetaItem::operator= (other);
+    path = other.path;
+
+    seq.reserve (other.seq.size ());
+    for (const auto &e : other.seq)
+      seq.push_back (e->clone_meta_item_inner ());
+
+    return *this;
+  }
+
+  // default move constructors
+  MetaItemSeq (MetaItemSeq &&other) = default;
+  MetaItemSeq &operator= (MetaItemSeq &&other) = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  bool check_cfg_predicate (const Session &session) const override;
+
+  Attribute to_attribute () const override;
+
+protected:
+  // Use covariance to implement clone function as returning this type
+  MetaItemSeq *clone_meta_item_inner_impl () const override
+  {
+    return new MetaItemSeq (*this);
+  }
+};
+
+// Preferred specialisation for single-identifier meta items.
+class MetaWord : public MetaItem
+{
+  Identifier ident;
+  Location ident_locus;
+
+public:
+  MetaWord (Identifier ident, Location ident_locus)
+    : ident (std::move (ident)), ident_locus (ident_locus)
+  {}
+
+  std::string as_string () const override { return ident; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  bool check_cfg_predicate (const Session &session) const override;
+
+  Attribute to_attribute () const override;
+
+protected:
+  // Use covariance to implement clone function as returning this type
+  MetaWord *clone_meta_item_inner_impl () const override
+  {
+    return new MetaWord (*this);
+  }
+};
+
+// Preferred specialisation for "identifier '=' string literal" meta items.
+class MetaNameValueStr : public MetaItem
+{
+  Identifier ident;
+  Location ident_locus;
+
+  // NOTE: str stored without quotes
+  std::string str;
+  Location str_locus;
+
+public:
+  MetaNameValueStr (Identifier ident, Location ident_locus, std::string str,
+		    Location str_locus)
+    : ident (std::move (ident)), ident_locus (ident_locus),
+      str (std::move (str)), str_locus (str_locus)
+  {}
+
+  std::string as_string () const override
+  {
+    return ident + " = \"" + str + "\"";
+  }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // HACK: used to simplify parsing - creates a copy of this
+  std::unique_ptr<MetaNameValueStr> to_meta_name_value_str () const override
+  {
+    return std::unique_ptr<MetaNameValueStr> (clone_meta_item_inner_impl ());
+  }
+
+  bool check_cfg_predicate (const Session &session) const override;
+
+  Attribute to_attribute () const override;
+
+  inline std::pair<Identifier, std::string> get_name_value_pair () const
+  {
+    return std::pair<Identifier, std::string> (ident, str);
+  }
+
+  bool is_key_value_pair () const override { return true; }
+
+protected:
+  // Use covariance to implement clone function as returning this type
+  MetaNameValueStr *clone_meta_item_inner_impl () const override
+  {
+    return new MetaNameValueStr (*this);
+  }
+};
+
+// doubles up as MetaListIdents - determine via iterating through each path?
+// Preferred specialisation for "identifier '(' SimplePath, SimplePath, ... ')'"
+class MetaListPaths : public MetaItem
+{
+  Identifier ident;
+  Location ident_locus;
+  std::vector<SimplePath> paths;
+
+public:
+  MetaListPaths (Identifier ident, Location ident_locus,
+		 std::vector<SimplePath> paths)
+    : ident (std::move (ident)), ident_locus (ident_locus),
+      paths (std::move (paths))
+  {}
+
+  std::string as_string () const override;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  bool check_cfg_predicate (const Session &session) const override;
+
+  Attribute to_attribute () const override;
+
+private:
+  bool check_path_exists_in_cfg (const Session &session,
+				 const SimplePath &path) const;
+
+protected:
+  // Use covariance to implement clone function as returning this type
+  MetaListPaths *clone_meta_item_inner_impl () const override
+  {
+    return new MetaListPaths (*this);
+  }
+};
+
+// Preferred specialisation for "identifier '(' MetaNameValueStr, ... ')'"
+class MetaListNameValueStr : public MetaItem
+{
+  Identifier ident;
+  Location ident_locus;
+  std::vector<MetaNameValueStr> strs;
+
+public:
+  MetaListNameValueStr (Identifier ident, Location ident_locus,
+			std::vector<MetaNameValueStr> strs)
+    : ident (std::move (ident)), ident_locus (ident_locus),
+      strs (std::move (strs))
+  {}
+
+  std::string as_string () const override;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  bool check_cfg_predicate (const Session &session) const override;
+
+  Attribute to_attribute () const override;
+
+protected:
+  // Use covariance to implement clone function as returning this type
+  MetaListNameValueStr *clone_meta_item_inner_impl () const override
+  {
+    return new MetaListNameValueStr (*this);
+  }
+};
+
+// Object that parses macros from a token stream.
+/* TODO: would "AttributeParser" be a better name? MetaItems are only for
+ * attributes, I believe */
+struct AttributeParser
+{
+private:
+  // TODO: might as well rewrite to use lexer tokens
+  std::vector<std::unique_ptr<Token> > token_stream;
+  int stream_pos;
+
+public:
+  AttributeParser (std::vector<std::unique_ptr<Token> > token_stream,
+		   int stream_start_pos = 0)
+    : token_stream (std::move (token_stream)), stream_pos (stream_start_pos)
+  {}
+
+  ~AttributeParser () = default;
+
+  std::vector<std::unique_ptr<MetaItemInner> > parse_meta_item_seq ();
+
+private:
+  // Parses a MetaItemInner.
+  std::unique_ptr<MetaItemInner> parse_meta_item_inner ();
+  // Returns whether token can end a meta item.
+  bool is_end_meta_item_tok (TokenId id) const;
+  // Parses a simple path.
+  SimplePath parse_simple_path ();
+  // Parses a segment of a simple path (but not scope resolution operator).
+  SimplePathSegment parse_simple_path_segment ();
+  // Parses a MetaItemLitExpr.
+  std::unique_ptr<MetaItemLitExpr> parse_meta_item_lit ();
+  // Parses a literal.
+  Literal parse_literal ();
+  // Parses a meta item that begins with a simple path.
+  std::unique_ptr<MetaItem> parse_path_meta_item ();
+
+  // TODO: should this be const?
+  std::unique_ptr<Token> &peek_token (int i = 0)
+  {
+    return token_stream[stream_pos + i];
+  }
+
+  void skip_token (int i = 0) { stream_pos += 1 + i; }
+};
+} // namespace AST
+} // namespace Rust
+
+/* <https://stackoverflow.com/a/35304501> */
+namespace std {
+template <> struct hash<Rust::AST::MacroFragSpec::Kind>
+{
+  size_t operator() (const Rust::AST::MacroFragSpec::Kind &t) const noexcept
+  {
+    return size_t (t);
+  }
+};
+} // namespace std
+
+#endif
diff --git a/gcc/rust/ast/rust-path.h b/gcc/rust/ast/rust-path.h
new file mode 100644
index 00000000000..cc79e278f05
--- /dev/null
+++ b/gcc/rust/ast/rust-path.h
@@ -0,0 +1,1297 @@
+// 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_AST_PATH_H
+#define RUST_AST_PATH_H
+/* "Path" (identifier within namespaces, essentially) handling. Required include
+ * for virtually all AST-related functionality. */
+
+#include "rust-ast.h"
+#include "system.h"
+
+namespace Rust {
+namespace AST {
+
+// The "identifier" (not generic args) aspect of each path expression segment
+class PathIdentSegment
+{
+  std::string segment_name;
+  Location locus;
+
+  // only allow identifiers, "super", "self", "Self", "crate", or "$crate"
+public:
+  PathIdentSegment (std::string segment_name, Location locus)
+    : segment_name (std::move (segment_name)), locus (locus)
+  {}
+
+  // Creates an error PathIdentSegment.
+  static PathIdentSegment create_error ()
+  {
+    return PathIdentSegment ("", Location ());
+  }
+
+  // Returns whether PathIdentSegment is in an error state.
+  bool is_error () const { return segment_name.empty (); }
+
+  std::string as_string () const { return segment_name; }
+
+  Location get_locus () const { return locus; }
+
+  bool is_super_segment () const { return as_string ().compare ("super") == 0; }
+  bool is_crate_segment () const { return as_string ().compare ("crate") == 0; }
+  bool is_lower_self () const { return as_string ().compare ("self") == 0; }
+  bool is_big_self () const { return as_string ().compare ("Self") == 0; }
+};
+
+// A binding of an identifier to a type used in generic arguments in paths
+struct GenericArgsBinding
+{
+private:
+  Identifier identifier;
+  std::unique_ptr<Type> type;
+  Location locus;
+
+public:
+  // Returns whether binding is in an error state.
+  bool is_error () const
+  {
+    return type == nullptr;
+    // and also identifier is empty, but cheaper computation
+  }
+
+  // Creates an error state generic args binding.
+  static GenericArgsBinding create_error ()
+  {
+    return GenericArgsBinding ("", nullptr);
+  }
+
+  // Pointer type for type in constructor to enable polymorphism
+  GenericArgsBinding (Identifier ident, std::unique_ptr<Type> type_ptr,
+		      Location locus = Location ())
+    : identifier (std::move (ident)), type (std::move (type_ptr)), locus (locus)
+  {}
+
+  // Copy constructor has to deep copy the type as it is a unique pointer
+  GenericArgsBinding (GenericArgsBinding const &other)
+    : identifier (other.identifier), locus (other.locus)
+  {
+    // guard to protect from null pointer dereference
+    if (other.type != nullptr)
+      type = other.type->clone_type ();
+  }
+
+  // default destructor
+  ~GenericArgsBinding () = default;
+
+  // Overload assignment operator to deep copy the pointed-to type
+  GenericArgsBinding &operator= (GenericArgsBinding const &other)
+  {
+    identifier = other.identifier;
+    locus = other.locus;
+
+    // guard to protect from null pointer dereference
+    if (other.type != nullptr)
+      type = other.type->clone_type ();
+    else
+      type = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  GenericArgsBinding (GenericArgsBinding &&other) = default;
+  GenericArgsBinding &operator= (GenericArgsBinding &&other) = default;
+
+  std::string as_string () const;
+
+  // TODO: is this better? Or is a "vis_pattern" better?
+  std::unique_ptr<Type> &get_type ()
+  {
+    rust_assert (type != nullptr);
+    return type;
+  }
+
+  Location get_locus () const { return locus; }
+
+  Identifier get_identifier () const { return identifier; }
+};
+
+/* Class representing a const generic application */
+class GenericArg
+{
+public:
+  /**
+   * const generic arguments cannot always be differentiated with generic type
+   * arguments during parsing, e.g:
+   * ```rust
+   * let a: Foo<N>;
+   * ```
+   *
+   * Is N a type? A constant defined elsewhere? The parser cannot know, and must
+   * not draw any conclusions. We must wait until later passes of the compiler
+   * to decide whether this refers to a constant item or a type.
+   *
+   * On the other hand, simple expressions like literals or block expressions
+   * will always be constant expressions: There is no ambiguity at all.
+   */
+  enum class Kind
+  {
+    Error,
+    Const,  // A const value
+    Type,   // A type argument (not discernable during parsing)
+    Either, // Either a type or a const value, cleared up during resolving
+  };
+
+  static GenericArg create_error ()
+  {
+    return GenericArg (nullptr, nullptr, "", Kind::Error, Location ());
+  }
+
+  static GenericArg create_const (std::unique_ptr<Expr> expression)
+  {
+    auto locus = expression->get_locus ();
+    return GenericArg (std::move (expression), nullptr, "", Kind::Const, locus);
+  }
+
+  static GenericArg create_type (std::unique_ptr<Type> type)
+  {
+    auto locus = type->get_locus ();
+    return GenericArg (nullptr, std::move (type), "", Kind::Type, locus);
+  }
+
+  static GenericArg create_ambiguous (Identifier path, Location locus)
+  {
+    return GenericArg (nullptr, nullptr, std::move (path), Kind::Either, locus);
+  }
+
+  GenericArg (const GenericArg &other)
+    : path (other.path), kind (other.kind), locus (other.locus)
+  {
+    if (other.expression)
+      expression = other.expression->clone_expr ();
+    if (other.type)
+      type = other.type->clone_type ();
+  }
+
+  GenericArg operator= (const GenericArg &other)
+  {
+    kind = other.kind;
+    path = other.path;
+    locus = other.locus;
+
+    if (other.expression)
+      expression = other.expression->clone_expr ();
+    if (other.type)
+      type = other.type->clone_type ();
+
+    return *this;
+  }
+
+  bool is_error () const { return kind == Kind::Error; }
+
+  Kind get_kind () const { return kind; }
+  const Location &get_locus () const { return locus; }
+
+  std::unique_ptr<Expr> &get_expression ()
+  {
+    rust_assert (kind == Kind::Const);
+
+    return expression;
+  }
+
+  std::unique_ptr<Type> &get_type ()
+  {
+    rust_assert (kind == Kind::Type);
+
+    return type;
+  }
+
+  const std::string &get_path () const
+  {
+    rust_assert (kind == Kind::Either);
+
+    return path;
+  }
+
+  std::string as_string () const
+  {
+    switch (get_kind ())
+      {
+      case Kind::Error:
+	gcc_unreachable ();
+      case Kind::Either:
+	return "Ambiguous: " + path;
+      case Kind::Const:
+	return "Const: { " + expression->as_string () + " }";
+      case Kind::Type:
+	return "Type: " + type->as_string ();
+      }
+
+    return "";
+  }
+
+  /**
+   * Disambiguate an ambiguous generic argument to a const generic argument,
+   * unequivocally
+   */
+  GenericArg disambiguate_to_const () const;
+
+  /**
+   * Disambiguate an ambiguous generic argument to a type argument,
+   * unequivocally
+   */
+  GenericArg disambiguate_to_type () const;
+
+private:
+  GenericArg (std::unique_ptr<Expr> expression, std::unique_ptr<Type> type,
+	      Identifier path, Kind kind, Location locus)
+    : expression (std::move (expression)), type (std::move (type)),
+      path (std::move (path)), kind (kind), locus (locus)
+  {}
+
+  /**
+   * Expression associated with a `Clear` const generic application
+   * A null pointer here is allowed in the case that the const argument is
+   * ambiguous.
+   */
+  std::unique_ptr<Expr> expression;
+
+  /**
+   * If the argument ends up being a type argument instead. A null pointer will
+   * be present here until the resolving phase.
+   */
+  std::unique_ptr<Type> type;
+
+  /**
+   * Optional path which cannot be differentiated between a constant item and
+   * a type. Only used for ambiguous const generic arguments, otherwise
+   * empty.
+   */
+  Identifier path;
+
+  /* Which kind of const generic application are we dealing with */
+  Kind kind;
+
+  Location locus;
+};
+
+/**
+ * Representation of const generic parameters
+ */
+class ConstGenericParam : public GenericParam
+{
+  /* Name of the parameter */
+  Identifier name;
+
+  /* Mandatory type of the const parameter - a null pointer is an error */
+  std::unique_ptr<AST::Type> type;
+
+  /**
+   * Default value for the const generic parameter
+   */
+  GenericArg default_value;
+
+  Attribute outer_attr;
+  Location locus;
+
+public:
+  ConstGenericParam (Identifier name, std::unique_ptr<AST::Type> type,
+		     GenericArg default_value, Attribute outer_attr,
+		     Location locus)
+    : name (name), type (std::move (type)),
+      default_value (std::move (default_value)), outer_attr (outer_attr),
+      locus (locus)
+  {}
+
+  ConstGenericParam (const ConstGenericParam &other)
+    : GenericParam (), name (other.name), type (other.type->clone_type ()),
+      default_value (other.default_value), outer_attr (other.outer_attr),
+      locus (other.locus)
+  {}
+
+  bool has_type () const { return type != nullptr; }
+  bool has_default_value () const { return !default_value.is_error (); }
+
+  const Identifier &get_name () const { return name; }
+
+  std::unique_ptr<AST::Type> &get_type ()
+  {
+    rust_assert (has_type ());
+
+    return type;
+  }
+
+  GenericArg &get_default_value ()
+  {
+    rust_assert (has_default_value ());
+
+    return default_value;
+  }
+
+  const GenericArg &get_default_value () const
+  {
+    rust_assert (has_default_value ());
+
+    return default_value;
+  }
+
+  std::string as_string () const override;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  Location get_locus () const override final { return locus; }
+
+  Kind get_kind () const override final { return Kind::Const; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ConstGenericParam *clone_generic_param_impl () const override
+  {
+    return new ConstGenericParam (*this);
+  }
+};
+
+// Generic arguments allowed in each path expression segment - inline?
+struct GenericArgs
+{
+  std::vector<Lifetime> lifetime_args;
+  std::vector<GenericArg> generic_args;
+  std::vector<GenericArgsBinding> binding_args;
+  Location locus;
+
+public:
+  // Returns true if there are any generic arguments
+  bool has_generic_args () const
+  {
+    return !(lifetime_args.empty () && generic_args.empty ()
+	     && binding_args.empty ());
+  }
+
+  GenericArgs (std::vector<Lifetime> lifetime_args,
+	       std::vector<GenericArg> generic_args,
+	       std::vector<GenericArgsBinding> binding_args,
+	       Location locus = Location ())
+    : lifetime_args (std::move (lifetime_args)),
+      generic_args (std::move (generic_args)),
+      binding_args (std::move (binding_args)), locus (locus)
+  {}
+
+  // copy constructor with vector clone
+  GenericArgs (GenericArgs const &other)
+    : lifetime_args (other.lifetime_args), generic_args (other.generic_args),
+      binding_args (other.binding_args), locus (other.locus)
+  {}
+
+  ~GenericArgs () = default;
+
+  // overloaded assignment operator to vector clone
+  GenericArgs &operator= (GenericArgs const &other)
+  {
+    lifetime_args = other.lifetime_args;
+    generic_args = other.generic_args;
+    binding_args = other.binding_args;
+    locus = other.locus;
+
+    return *this;
+  }
+
+  // move constructors
+  GenericArgs (GenericArgs &&other) = default;
+  GenericArgs &operator= (GenericArgs &&other) = default;
+
+  // Creates an empty GenericArgs (no arguments)
+  static GenericArgs create_empty () { return GenericArgs ({}, {}, {}); }
+
+  std::string as_string () const;
+
+  // TODO: is this better? Or is a "vis_pattern" better?
+  std::vector<GenericArg> &get_generic_args () { return generic_args; }
+
+  // TODO: is this better? Or is a "vis_pattern" better?
+  std::vector<GenericArgsBinding> &get_binding_args () { return binding_args; }
+
+  std::vector<Lifetime> &get_lifetime_args () { return lifetime_args; };
+
+  Location get_locus () { return locus; }
+};
+
+/* A segment of a path in expression, including an identifier aspect and maybe
+ * generic args */
+class PathExprSegment
+{ // or should this extend PathIdentSegment?
+private:
+  PathIdentSegment segment_name;
+  GenericArgs generic_args;
+  Location locus;
+  NodeId node_id;
+
+public:
+  // Returns true if there are any generic arguments
+  bool has_generic_args () const { return generic_args.has_generic_args (); }
+
+  // Constructor for segment (from IdentSegment and GenericArgs)
+  PathExprSegment (PathIdentSegment segment_name, Location locus,
+		   GenericArgs generic_args = GenericArgs::create_empty ())
+    : segment_name (std::move (segment_name)),
+      generic_args (std::move (generic_args)), locus (locus),
+      node_id (Analysis::Mappings::get ()->get_next_node_id ())
+  {}
+
+  /* Constructor for segment with generic arguments (from segment name and all
+   * args) */
+  PathExprSegment (std::string segment_name, Location locus,
+		   std::vector<Lifetime> lifetime_args = {},
+		   std::vector<GenericArg> generic_args = {},
+		   std::vector<GenericArgsBinding> binding_args = {})
+    : segment_name (PathIdentSegment (std::move (segment_name), locus)),
+      generic_args (GenericArgs (std::move (lifetime_args),
+				 std::move (generic_args),
+				 std::move (binding_args))),
+      locus (locus), node_id (Analysis::Mappings::get ()->get_next_node_id ())
+  {}
+
+  // Returns whether path expression segment is in an error state.
+  bool is_error () const { return segment_name.is_error (); }
+
+  // Creates an error-state path expression segment.
+  static PathExprSegment create_error ()
+  {
+    return PathExprSegment (PathIdentSegment::create_error (), Location ());
+  }
+
+  std::string as_string () const;
+
+  Location get_locus () const { return locus; }
+
+  // TODO: is this better? Or is a "vis_pattern" better?
+  GenericArgs &get_generic_args ()
+  {
+    rust_assert (has_generic_args ());
+    return generic_args;
+  }
+
+  PathIdentSegment &get_ident_segment () { return segment_name; }
+  const PathIdentSegment &get_ident_segment () const { return segment_name; }
+
+  NodeId get_node_id () const { return node_id; }
+
+  bool is_super_path_seg () const
+  {
+    return !has_generic_args () && get_ident_segment ().is_super_segment ();
+  }
+
+  bool is_crate_path_seg () const
+  {
+    return !has_generic_args () && get_ident_segment ().is_crate_segment ();
+  }
+  bool is_lower_self_seg () const
+  {
+    return !has_generic_args () && get_ident_segment ().is_lower_self ();
+  }
+};
+
+// AST node representing a pattern that involves a "path" - abstract base
+// class
+class PathPattern : public Pattern
+{
+  std::vector<PathExprSegment> segments;
+
+protected:
+  PathPattern (std::vector<PathExprSegment> segments)
+    : segments (std::move (segments))
+  {}
+
+  // Returns whether path has segments.
+  bool has_segments () const { return !segments.empty (); }
+
+  /* Converts path segments to their equivalent SimplePath segments if
+   * possible, and creates a SimplePath from them. */
+  SimplePath convert_to_simple_path (bool with_opening_scope_resolution) const;
+
+  // Removes all segments of the path.
+  void remove_all_segments ()
+  {
+    segments.clear ();
+    segments.shrink_to_fit ();
+  }
+
+public:
+  /* Returns whether the path is a single segment (excluding qualified path
+   * initial as segment). */
+  bool is_single_segment () const { return segments.size () == 1; }
+
+  std::string as_string () const override;
+
+  // TODO: this seems kinda dodgy
+  std::vector<PathExprSegment> &get_segments () { return segments; }
+  const std::vector<PathExprSegment> &get_segments () const { return segments; }
+};
+
+/* AST node representing a path-in-expression pattern (path that allows
+ * generic arguments) */
+class PathInExpression : public PathPattern, public PathExpr
+{
+  std::vector<Attribute> outer_attrs;
+  bool has_opening_scope_resolution;
+  Location locus;
+  NodeId _node_id;
+
+public:
+  std::string as_string () const override;
+
+  // Constructor
+  PathInExpression (std::vector<PathExprSegment> path_segments,
+		    std::vector<Attribute> outer_attrs, Location locus,
+		    bool has_opening_scope_resolution = false)
+    : PathPattern (std::move (path_segments)),
+      outer_attrs (std::move (outer_attrs)),
+      has_opening_scope_resolution (has_opening_scope_resolution),
+      locus (locus), _node_id (Analysis::Mappings::get ()->get_next_node_id ())
+  {}
+
+  // Creates an error state path in expression.
+  static PathInExpression create_error ()
+  {
+    return PathInExpression ({}, {}, Location ());
+  }
+
+  // Returns whether path in expression is in an error state.
+  bool is_error () const { return !has_segments (); }
+
+  /* Converts PathInExpression to SimplePath if possible (i.e. no generic
+   * arguments). Otherwise returns an empty SimplePath. */
+  SimplePath as_simple_path () const
+  {
+    /* delegate to parent class as can't access segments. however,
+     * QualifiedPathInExpression conversion to simple path wouldn't make
+     * sense, so the method in the parent class should be protected, not
+     * public. Have to pass in opening scope resolution as parent class has no
+     * access to it.
+     */
+    return convert_to_simple_path (has_opening_scope_resolution);
+  }
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Invalid if path is empty (error state), so base stripping on that.
+  void mark_for_strip () override { remove_all_segments (); }
+  bool is_marked_for_strip () const override { return is_error (); }
+
+  bool opening_scope_resolution () const
+  {
+    return has_opening_scope_resolution;
+  }
+
+  NodeId get_node_id () const override { return _node_id; }
+
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+  void set_outer_attrs (std::vector<Attribute> new_attrs) override
+  {
+    outer_attrs = std::move (new_attrs);
+  }
+
+  NodeId get_pattern_node_id () const override final { return get_node_id (); }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  PathInExpression *clone_pattern_impl () const final override
+  {
+    return clone_path_in_expression_impl ();
+  }
+
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  PathInExpression *clone_expr_without_block_impl () const final override
+  {
+    return clone_path_in_expression_impl ();
+  }
+
+  /*virtual*/ PathInExpression *clone_path_in_expression_impl () const
+  {
+    return new PathInExpression (*this);
+  }
+};
+
+/* Base class for segments used in type paths - not abstract (represents an
+ * ident-only segment) */
+class TypePathSegment
+{
+public:
+  enum SegmentType
+  {
+    REG,
+    GENERIC,
+    FUNCTION
+  };
+
+private:
+  PathIdentSegment ident_segment;
+  Location locus;
+
+protected:
+  /* This is protected because it is only really used by derived classes, not
+   * the base. */
+  bool has_separating_scope_resolution;
+  NodeId node_id;
+
+  // Clone function implementation - not pure virtual as overrided by
+  // subclasses
+  virtual TypePathSegment *clone_type_path_segment_impl () const
+  {
+    return new TypePathSegment (*this);
+  }
+
+public:
+  virtual ~TypePathSegment () {}
+
+  virtual SegmentType get_type () const { return SegmentType::REG; }
+
+  // Unique pointer custom clone function
+  std::unique_ptr<TypePathSegment> clone_type_path_segment () const
+  {
+    return std::unique_ptr<TypePathSegment> (clone_type_path_segment_impl ());
+  }
+
+  TypePathSegment (PathIdentSegment ident_segment,
+		   bool has_separating_scope_resolution, Location locus)
+    : ident_segment (std::move (ident_segment)), locus (locus),
+      has_separating_scope_resolution (has_separating_scope_resolution),
+      node_id (Analysis::Mappings::get ()->get_next_node_id ())
+  {}
+
+  TypePathSegment (std::string segment_name,
+		   bool has_separating_scope_resolution, Location locus)
+    : ident_segment (PathIdentSegment (std::move (segment_name), locus)),
+      locus (locus),
+      has_separating_scope_resolution (has_separating_scope_resolution),
+      node_id (Analysis::Mappings::get ()->get_next_node_id ())
+  {}
+
+  virtual std::string as_string () const { return ident_segment.as_string (); }
+
+  /* Returns whether the type path segment is in an error state. May be
+   * virtual in future. */
+  bool is_error () const { return ident_segment.is_error (); }
+
+  /* Returns whether segment is identifier only (as opposed to generic args or
+   * function). Overridden in derived classes with other segments. */
+  virtual bool is_ident_only () const { return true; }
+
+  Location get_locus () const { return locus; }
+
+  // not pure virtual as class not abstract
+  virtual void accept_vis (ASTVisitor &vis);
+
+  bool get_separating_scope_resolution () const
+  {
+    return has_separating_scope_resolution;
+  }
+
+  PathIdentSegment &get_ident_segment () { return ident_segment; };
+  const PathIdentSegment &get_ident_segment () const { return ident_segment; };
+
+  NodeId get_node_id () const { return node_id; }
+
+  bool is_crate_path_seg () const
+  {
+    return get_ident_segment ().is_crate_segment ();
+  }
+  bool is_super_path_seg () const
+  {
+    return get_ident_segment ().is_super_segment ();
+  }
+  bool is_big_self_seg () const { return get_ident_segment ().is_big_self (); }
+  bool is_lower_self_seg () const
+  {
+    return get_ident_segment ().is_lower_self ();
+  }
+};
+
+// Segment used in type path with generic args
+class TypePathSegmentGeneric : public TypePathSegment
+{
+  GenericArgs generic_args;
+
+public:
+  SegmentType get_type () const override { return SegmentType::GENERIC; }
+
+  bool has_generic_args () const { return generic_args.has_generic_args (); }
+
+  bool is_ident_only () const override { return false; }
+
+  // Constructor with PathIdentSegment and GenericArgs
+  TypePathSegmentGeneric (PathIdentSegment ident_segment,
+			  bool has_separating_scope_resolution,
+			  GenericArgs generic_args, Location locus)
+    : TypePathSegment (std::move (ident_segment),
+		       has_separating_scope_resolution, locus),
+      generic_args (std::move (generic_args))
+  {}
+
+  // Constructor from segment name and all args
+  TypePathSegmentGeneric (std::string segment_name,
+			  bool has_separating_scope_resolution,
+			  std::vector<Lifetime> lifetime_args,
+			  std::vector<GenericArg> generic_args,
+			  std::vector<GenericArgsBinding> binding_args,
+			  Location locus)
+    : TypePathSegment (std::move (segment_name),
+		       has_separating_scope_resolution, locus),
+      generic_args (GenericArgs (std::move (lifetime_args),
+				 std::move (generic_args),
+				 std::move (binding_args)))
+  {}
+
+  std::string as_string () const override;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: is this better? Or is a "vis_pattern" better?
+  GenericArgs &get_generic_args ()
+  {
+    rust_assert (has_generic_args ());
+    return generic_args;
+  }
+
+protected:
+  // Use covariance to override base class method
+  TypePathSegmentGeneric *clone_type_path_segment_impl () const override
+  {
+    return new TypePathSegmentGeneric (*this);
+  }
+};
+
+// A function as represented in a type path
+struct TypePathFunction
+{
+private:
+  // TODO: remove
+  /*bool has_inputs;
+  TypePathFnInputs inputs;*/
+  // inlined from TypePathFnInputs
+  std::vector<std::unique_ptr<Type> > inputs;
+
+  // bool has_type;
+  std::unique_ptr<Type> return_type;
+
+  // FIXME: think of better way to mark as invalid than taking up storage
+  bool is_invalid;
+
+  Location locus;
+
+protected:
+  // Constructor only used to create invalid type path functions.
+  TypePathFunction (bool is_invalid, Location locus)
+    : is_invalid (is_invalid), locus (locus)
+  {}
+
+public:
+  // Returns whether the return type of the function has been specified.
+  bool has_return_type () const { return return_type != nullptr; }
+
+  // Returns whether the function has inputs.
+  bool has_inputs () const { return !inputs.empty (); }
+
+  // Returns whether function is in an error state.
+  bool is_error () const { return is_invalid; }
+
+  // Creates an error state function.
+  static TypePathFunction create_error ()
+  {
+    return TypePathFunction (true, Location ());
+  }
+
+  // Constructor
+  TypePathFunction (std::vector<std::unique_ptr<Type> > inputs, Location locus,
+		    std::unique_ptr<Type> type = nullptr)
+    : inputs (std::move (inputs)), return_type (std::move (type)),
+      is_invalid (false), locus (locus)
+  {}
+
+  // Copy constructor with clone
+  TypePathFunction (TypePathFunction const &other)
+    : is_invalid (other.is_invalid)
+  {
+    // guard to protect from null pointer dereference
+    if (other.return_type != nullptr)
+      return_type = other.return_type->clone_type ();
+
+    inputs.reserve (other.inputs.size ());
+    for (const auto &e : other.inputs)
+      inputs.push_back (e->clone_type ());
+  }
+
+  ~TypePathFunction () = default;
+
+  // Overloaded assignment operator to clone type
+  TypePathFunction &operator= (TypePathFunction const &other)
+  {
+    is_invalid = other.is_invalid;
+
+    // guard to protect from null pointer dereference
+    if (other.return_type != nullptr)
+      return_type = other.return_type->clone_type ();
+    else
+      return_type = nullptr;
+
+    inputs.reserve (other.inputs.size ());
+    for (const auto &e : other.inputs)
+      inputs.push_back (e->clone_type ());
+
+    return *this;
+  }
+
+  // move constructors
+  TypePathFunction (TypePathFunction &&other) = default;
+  TypePathFunction &operator= (TypePathFunction &&other) = default;
+
+  std::string as_string () const;
+
+  // TODO: this mutable getter seems really dodgy. Think up better way.
+  const std::vector<std::unique_ptr<Type> > &get_params () const
+  {
+    return inputs;
+  }
+  std::vector<std::unique_ptr<Type> > &get_params () { return inputs; }
+
+  // TODO: is this better? Or is a "vis_pattern" better?
+  std::unique_ptr<Type> &get_return_type ()
+  {
+    rust_assert (has_return_type ());
+    return return_type;
+  }
+};
+
+// Segment used in type path with a function argument
+class TypePathSegmentFunction : public TypePathSegment
+{
+  TypePathFunction function_path;
+
+public:
+  SegmentType get_type () const override { return SegmentType::FUNCTION; }
+
+  // Constructor with PathIdentSegment and TypePathFn
+  TypePathSegmentFunction (PathIdentSegment ident_segment,
+			   bool has_separating_scope_resolution,
+			   TypePathFunction function_path, Location locus)
+    : TypePathSegment (std::move (ident_segment),
+		       has_separating_scope_resolution, locus),
+      function_path (std::move (function_path))
+  {}
+
+  // Constructor with segment name and TypePathFn
+  TypePathSegmentFunction (std::string segment_name,
+			   bool has_separating_scope_resolution,
+			   TypePathFunction function_path, Location locus)
+    : TypePathSegment (std::move (segment_name),
+		       has_separating_scope_resolution, locus),
+      function_path (std::move (function_path))
+  {}
+
+  std::string as_string () const override;
+
+  bool is_ident_only () const override { return false; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: is this better? Or is a "vis_pattern" better?
+  TypePathFunction &get_type_path_function ()
+  {
+    rust_assert (!function_path.is_error ());
+    return function_path;
+  }
+
+protected:
+  // Use covariance to override base class method
+  TypePathSegmentFunction *clone_type_path_segment_impl () const override
+  {
+    return new TypePathSegmentFunction (*this);
+  }
+};
+
+// Path used inside types
+class TypePath : public TypeNoBounds
+{
+  bool has_opening_scope_resolution;
+  std::vector<std::unique_ptr<TypePathSegment> > segments;
+  Location locus;
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  TypePath *clone_type_no_bounds_impl () const override
+  {
+    return new TypePath (*this);
+  }
+
+public:
+  /* Returns whether the TypePath has an opening scope resolution operator
+   * (i.e. is global path or crate-relative path, not module-relative) */
+  bool has_opening_scope_resolution_op () const
+  {
+    return has_opening_scope_resolution;
+  }
+
+  // Returns whether the TypePath is in an invalid state.
+  bool is_error () const { return segments.empty (); }
+
+  // Creates an error state TypePath.
+  static TypePath create_error ()
+  {
+    return TypePath (std::vector<std::unique_ptr<TypePathSegment> > (),
+		     Location ());
+  }
+
+  // Constructor
+  TypePath (std::vector<std::unique_ptr<TypePathSegment> > segments,
+	    Location locus, bool has_opening_scope_resolution = false)
+    : TypeNoBounds (),
+      has_opening_scope_resolution (has_opening_scope_resolution),
+      segments (std::move (segments)), locus (locus)
+  {}
+
+  // Copy constructor with vector clone
+  TypePath (TypePath const &other)
+    : has_opening_scope_resolution (other.has_opening_scope_resolution),
+      locus (other.locus)
+  {
+    segments.reserve (other.segments.size ());
+    for (const auto &e : other.segments)
+      segments.push_back (e->clone_type_path_segment ());
+  }
+
+  // Overloaded assignment operator with clone
+  TypePath &operator= (TypePath const &other)
+  {
+    has_opening_scope_resolution = other.has_opening_scope_resolution;
+    locus = other.locus;
+
+    segments.reserve (other.segments.size ());
+    for (const auto &e : other.segments)
+      segments.push_back (e->clone_type_path_segment ());
+
+    return *this;
+  }
+
+  // move constructors
+  TypePath (TypePath &&other) = default;
+  TypePath &operator= (TypePath &&other) = default;
+
+  std::string as_string () const override;
+
+  /* Converts TypePath to SimplePath if possible (i.e. no generic or function
+   * arguments). Otherwise returns an empty SimplePath. */
+  SimplePath as_simple_path () const;
+
+  // Creates a trait bound with a clone of this type path as its only element.
+  TraitBound *to_trait_bound (bool in_parens) const override;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: this seems kinda dodgy
+  std::vector<std::unique_ptr<TypePathSegment> > &get_segments ()
+  {
+    return segments;
+  }
+  const std::vector<std::unique_ptr<TypePathSegment> > &get_segments () const
+  {
+    return segments;
+  }
+
+  size_t get_num_segments () const { return segments.size (); }
+};
+
+struct QualifiedPathType
+{
+private:
+  std::unique_ptr<Type> type_to_invoke_on;
+  TypePath trait_path;
+  Location locus;
+  NodeId node_id;
+
+public:
+  // Constructor
+  QualifiedPathType (std::unique_ptr<Type> invoke_on_type,
+		     Location locus = Location (),
+		     TypePath trait_path = TypePath::create_error ())
+    : type_to_invoke_on (std::move (invoke_on_type)),
+      trait_path (std::move (trait_path)), locus (locus),
+      node_id (Analysis::Mappings::get ()->get_next_node_id ())
+  {}
+
+  // Copy constructor uses custom deep copy for Type to preserve polymorphism
+  QualifiedPathType (QualifiedPathType const &other)
+    : trait_path (other.trait_path), locus (other.locus)
+  {
+    node_id = other.node_id;
+    // guard to prevent null dereference
+    if (other.type_to_invoke_on != nullptr)
+      type_to_invoke_on = other.type_to_invoke_on->clone_type ();
+  }
+
+  // default destructor
+  ~QualifiedPathType () = default;
+
+  // overload assignment operator to use custom clone method
+  QualifiedPathType &operator= (QualifiedPathType const &other)
+  {
+    node_id = other.node_id;
+    trait_path = other.trait_path;
+    locus = other.locus;
+
+    // guard to prevent null dereference
+    if (other.type_to_invoke_on != nullptr)
+      type_to_invoke_on = other.type_to_invoke_on->clone_type ();
+    else
+      type_to_invoke_on = nullptr;
+
+    return *this;
+  }
+
+  // move constructor
+  QualifiedPathType (QualifiedPathType &&other) = default;
+  QualifiedPathType &operator= (QualifiedPathType &&other) = default;
+
+  // Returns whether the qualified path type has a rebind as clause.
+  bool has_as_clause () const { return !trait_path.is_error (); }
+
+  // Returns whether the qualified path type is in an error state.
+  bool is_error () const { return type_to_invoke_on == nullptr; }
+
+  // Creates an error state qualified path type.
+  static QualifiedPathType create_error ()
+  {
+    return QualifiedPathType (nullptr);
+  }
+
+  std::string as_string () const;
+
+  Location get_locus () const { return locus; }
+
+  // TODO: is this better? Or is a "vis_pattern" better?
+  std::unique_ptr<Type> &get_type ()
+  {
+    rust_assert (type_to_invoke_on != nullptr);
+    return type_to_invoke_on;
+  }
+
+  // TODO: is this better? Or is a "vis_pattern" better?
+  TypePath &get_as_type_path ()
+  {
+    rust_assert (has_as_clause ());
+    return trait_path;
+  }
+
+  NodeId get_node_id () const { return node_id; }
+};
+
+/* AST node representing a qualified path-in-expression pattern (path that
+ * allows specifying trait functions) */
+class QualifiedPathInExpression : public PathPattern, public PathExpr
+{
+  std::vector<Attribute> outer_attrs;
+  QualifiedPathType path_type;
+  Location locus;
+  NodeId _node_id;
+
+public:
+  std::string as_string () const override;
+
+  QualifiedPathInExpression (QualifiedPathType qual_path_type,
+			     std::vector<PathExprSegment> path_segments,
+			     std::vector<Attribute> outer_attrs, Location locus)
+    : PathPattern (std::move (path_segments)),
+      outer_attrs (std::move (outer_attrs)),
+      path_type (std::move (qual_path_type)), locus (locus),
+      _node_id (Analysis::Mappings::get ()->get_next_node_id ())
+  {}
+
+  /* TODO: maybe make a shortcut constructor that has QualifiedPathType
+   * elements as params */
+
+  // Returns whether qualified path in expression is in an error state.
+  bool is_error () const { return path_type.is_error (); }
+
+  // Creates an error qualified path in expression.
+  static QualifiedPathInExpression create_error ()
+  {
+    return QualifiedPathInExpression (QualifiedPathType::create_error (), {},
+				      {}, Location ());
+  }
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Invalid if path_type is error, so base stripping on that.
+  void mark_for_strip () override
+  {
+    path_type = QualifiedPathType::create_error ();
+  }
+  bool is_marked_for_strip () const override { return is_error (); }
+
+  // TODO: is this better? Or is a "vis_pattern" better?
+  QualifiedPathType &get_qualified_path_type ()
+  {
+    rust_assert (!path_type.is_error ());
+    return path_type;
+  }
+
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+
+  void set_outer_attrs (std::vector<Attribute> new_attrs) override
+  {
+    outer_attrs = std::move (new_attrs);
+  }
+
+  NodeId get_node_id () const override { return _node_id; }
+
+  NodeId get_pattern_node_id () const override final { return get_node_id (); }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  QualifiedPathInExpression *clone_pattern_impl () const final override
+  {
+    return clone_qual_path_in_expression_impl ();
+  }
+
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  QualifiedPathInExpression *
+  clone_expr_without_block_impl () const final override
+  {
+    return clone_qual_path_in_expression_impl ();
+  }
+
+  /*virtual*/ QualifiedPathInExpression *
+  clone_qual_path_in_expression_impl () const
+  {
+    return new QualifiedPathInExpression (*this);
+  }
+};
+
+/* Represents a qualified path in a type; used for disambiguating trait
+ * function calls */
+class QualifiedPathInType : public TypeNoBounds
+{
+  QualifiedPathType path_type;
+  std::unique_ptr<TypePathSegment> associated_segment;
+  std::vector<std::unique_ptr<TypePathSegment> > segments;
+  Location locus;
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  QualifiedPathInType *clone_type_no_bounds_impl () const override
+  {
+    return new QualifiedPathInType (*this);
+  }
+
+public:
+  QualifiedPathInType (
+    QualifiedPathType qual_path_type,
+    std::unique_ptr<TypePathSegment> associated_segment,
+    std::vector<std::unique_ptr<TypePathSegment> > path_segments,
+    Location locus)
+    : path_type (std::move (qual_path_type)),
+      associated_segment (std::move (associated_segment)),
+      segments (std::move (path_segments)), locus (locus)
+  {}
+
+  /* TODO: maybe make a shortcut constructor that has QualifiedPathType
+   * elements as params */
+
+  // Copy constructor with vector clone
+  QualifiedPathInType (QualifiedPathInType const &other)
+    : path_type (other.path_type), locus (other.locus)
+  {
+    segments.reserve (other.segments.size ());
+    for (const auto &e : other.segments)
+      segments.push_back (e->clone_type_path_segment ());
+  }
+
+  // Overloaded assignment operator with vector clone
+  QualifiedPathInType &operator= (QualifiedPathInType const &other)
+  {
+    path_type = other.path_type;
+    locus = other.locus;
+
+    segments.reserve (other.segments.size ());
+    for (const auto &e : other.segments)
+      segments.push_back (e->clone_type_path_segment ());
+
+    return *this;
+  }
+
+  // move constructors
+  QualifiedPathInType (QualifiedPathInType &&other) = default;
+  QualifiedPathInType &operator= (QualifiedPathInType &&other) = default;
+
+  // Returns whether qualified path in type is in an error state.
+  bool is_error () const { return path_type.is_error (); }
+
+  // Creates an error state qualified path in type.
+  static QualifiedPathInType create_error ()
+  {
+    return QualifiedPathInType (
+      QualifiedPathType::create_error (), nullptr,
+      std::vector<std::unique_ptr<TypePathSegment> > (), Location ());
+  }
+
+  std::string as_string () const override;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: is this better? Or is a "vis_pattern" better?
+  QualifiedPathType &get_qualified_path_type ()
+  {
+    rust_assert (!path_type.is_error ());
+    return path_type;
+  }
+
+  std::unique_ptr<TypePathSegment> &get_associated_segment ()
+  {
+    return associated_segment;
+  }
+
+  // TODO: this seems kinda dodgy
+  std::vector<std::unique_ptr<TypePathSegment> > &get_segments ()
+  {
+    return segments;
+  }
+  const std::vector<std::unique_ptr<TypePathSegment> > &get_segments () const
+  {
+    return segments;
+  }
+
+  Location get_locus () const override final { return locus; }
+};
+} // namespace AST
+} // namespace Rust
+
+#endif
diff --git a/gcc/rust/ast/rust-pattern.h b/gcc/rust/ast/rust-pattern.h
new file mode 100644
index 00000000000..247af5dbe05
--- /dev/null
+++ b/gcc/rust/ast/rust-pattern.h
@@ -0,0 +1,1576 @@
+// 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_AST_PATTERN_H
+#define RUST_AST_PATTERN_H
+
+#include "rust-ast.h"
+
+namespace Rust {
+namespace AST {
+// Literal pattern AST node (comparing to a literal)
+class LiteralPattern : public Pattern
+{
+  Literal lit;
+  Location locus;
+  NodeId node_id;
+
+public:
+  std::string as_string () const override;
+
+  // Constructor for a literal pattern
+  LiteralPattern (Literal lit, Location locus)
+    : lit (std::move (lit)), locus (locus),
+      node_id (Analysis::Mappings::get ()->get_next_node_id ())
+  {}
+
+  LiteralPattern (std::string val, Literal::LitType type, Location locus)
+    : lit (Literal (std::move (val), type, PrimitiveCoreType::CORETYPE_STR)),
+      locus (locus), node_id (Analysis::Mappings::get ()->get_next_node_id ())
+  {}
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  NodeId get_node_id () const { return node_id; }
+
+  NodeId get_pattern_node_id () const override final { return node_id; }
+
+  Literal &get_literal () { return lit; }
+
+  const Literal &get_literal () const { return lit; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  virtual LiteralPattern *clone_pattern_impl () const override
+  {
+    return new LiteralPattern (*this);
+  }
+};
+
+// Identifier pattern AST node (bind value matched to a variable)
+class IdentifierPattern : public Pattern
+{
+  Identifier variable_ident;
+  bool is_ref;
+  bool is_mut;
+
+  // bool has_pattern;
+  std::unique_ptr<Pattern> to_bind;
+  Location locus;
+  NodeId node_id;
+
+public:
+  std::string as_string () const override;
+
+  // Returns whether the IdentifierPattern has a pattern to bind.
+  bool has_pattern_to_bind () const { return to_bind != nullptr; }
+
+  // Constructor
+  IdentifierPattern (Identifier ident, Location locus, bool is_ref = false,
+		     bool is_mut = false,
+		     std::unique_ptr<Pattern> to_bind = nullptr)
+    : Pattern (), variable_ident (std::move (ident)), is_ref (is_ref),
+      is_mut (is_mut), to_bind (std::move (to_bind)), locus (locus),
+      node_id (Analysis::Mappings::get ()->get_next_node_id ())
+  {}
+
+  IdentifierPattern (NodeId node_id, Identifier ident, Location locus,
+		     bool is_ref = false, bool is_mut = false,
+		     std::unique_ptr<Pattern> to_bind = nullptr)
+    : Pattern (), variable_ident (std::move (ident)), is_ref (is_ref),
+      is_mut (is_mut), to_bind (std::move (to_bind)), locus (locus),
+      node_id (node_id)
+  {}
+
+  // Copy constructor with clone
+  IdentifierPattern (IdentifierPattern const &other)
+    : variable_ident (other.variable_ident), is_ref (other.is_ref),
+      is_mut (other.is_mut), locus (other.locus), node_id (other.node_id)
+  {
+    // fix to get prevent null pointer dereference
+    if (other.to_bind != nullptr)
+      to_bind = other.to_bind->clone_pattern ();
+  }
+
+  // Overload assignment operator to use clone
+  IdentifierPattern &operator= (IdentifierPattern const &other)
+  {
+    variable_ident = other.variable_ident;
+    is_ref = other.is_ref;
+    is_mut = other.is_mut;
+    locus = other.locus;
+    node_id = other.node_id;
+
+    // fix to prevent null pointer dereference
+    if (other.to_bind != nullptr)
+      to_bind = other.to_bind->clone_pattern ();
+    else
+      to_bind = nullptr;
+
+    return *this;
+  }
+
+  // default move semantics
+  IdentifierPattern (IdentifierPattern &&other) = default;
+  IdentifierPattern &operator= (IdentifierPattern &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: is this better? Or is a "vis_pattern" better?
+  std::unique_ptr<Pattern> &get_pattern_to_bind ()
+  {
+    rust_assert (has_pattern_to_bind ());
+    return to_bind;
+  }
+
+  Identifier get_ident () const { return variable_ident; }
+
+  bool get_is_mut () const { return is_mut; }
+  bool get_is_ref () const { return is_ref; }
+
+  NodeId get_node_id () const { return node_id; }
+
+  NodeId get_pattern_node_id () const override final { return node_id; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IdentifierPattern *clone_pattern_impl () const override
+  {
+    return new IdentifierPattern (*this);
+  }
+};
+
+// AST node for using the '_' wildcard "match any value" pattern
+class WildcardPattern : public Pattern
+{
+  Location locus;
+  NodeId node_id;
+
+public:
+  std::string as_string () const override { return std::string (1, '_'); }
+
+  WildcardPattern (Location locus)
+    : locus (locus), node_id (Analysis::Mappings::get ()->get_next_node_id ())
+  {}
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  NodeId get_node_id () const { return node_id; }
+
+  NodeId get_pattern_node_id () const override final { return node_id; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  WildcardPattern *clone_pattern_impl () const override
+  {
+    return new WildcardPattern (*this);
+  }
+};
+
+// Base range pattern bound (lower or upper limit) - abstract
+class RangePatternBound
+{
+public:
+  enum RangePatternBoundType
+  {
+    LITERAL,
+    PATH,
+    QUALPATH
+  };
+
+  virtual ~RangePatternBound () {}
+
+  // Unique pointer custom clone function
+  std::unique_ptr<RangePatternBound> clone_range_pattern_bound () const
+  {
+    return std::unique_ptr<RangePatternBound> (
+      clone_range_pattern_bound_impl ());
+  }
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (ASTVisitor &vis) = 0;
+
+  virtual RangePatternBoundType get_bound_type () const = 0;
+
+protected:
+  // pure virtual as RangePatternBound is abstract
+  virtual RangePatternBound *clone_range_pattern_bound_impl () const = 0;
+};
+
+// Literal-based pattern bound
+class RangePatternBoundLiteral : public RangePatternBound
+{
+  Literal literal;
+  /* Can only be a char, byte, int, or float literal - same impl here as
+   * previously */
+
+  // Minus prefixed to literal (if integer or floating-point)
+  bool has_minus;
+
+  Location locus;
+
+public:
+  // Constructor
+  RangePatternBoundLiteral (Literal literal, Location locus,
+			    bool has_minus = false)
+    : literal (literal), has_minus (has_minus), locus (locus)
+  {}
+
+  std::string as_string () const override;
+
+  Literal get_literal () const { return literal; }
+
+  bool get_has_minus () const { return has_minus; }
+
+  Location get_locus () const { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  RangePatternBoundType get_bound_type () const override
+  {
+    return RangePatternBoundType::LITERAL;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangePatternBoundLiteral *clone_range_pattern_bound_impl () const override
+  {
+    return new RangePatternBoundLiteral (*this);
+  }
+};
+
+// Path-based pattern bound
+class RangePatternBoundPath : public RangePatternBound
+{
+  PathInExpression path;
+
+  /* TODO: should this be refactored so that PathInExpression is a subclass of
+   * RangePatternBound? */
+
+public:
+  RangePatternBoundPath (PathInExpression path) : path (std::move (path)) {}
+
+  std::string as_string () const override { return path.as_string (); }
+
+  Location get_locus () const { return path.get_locus (); }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: this mutable getter seems kinda dodgy
+  PathInExpression &get_path () { return path; }
+  const PathInExpression &get_path () const { return path; }
+
+  RangePatternBoundType get_bound_type () const override
+  {
+    return RangePatternBoundType::PATH;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangePatternBoundPath *clone_range_pattern_bound_impl () const override
+  {
+    return new RangePatternBoundPath (*this);
+  }
+};
+
+// Qualified path-based pattern bound
+class RangePatternBoundQualPath : public RangePatternBound
+{
+  QualifiedPathInExpression path;
+
+  /* TODO: should this be refactored so that QualifiedPathInExpression is a
+   * subclass of RangePatternBound? */
+
+public:
+  RangePatternBoundQualPath (QualifiedPathInExpression path)
+    : path (std::move (path))
+  {}
+
+  std::string as_string () const override { return path.as_string (); }
+
+  Location get_locus () const { return path.get_locus (); }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: this mutable getter seems kinda dodgy
+  QualifiedPathInExpression &get_qualified_path () { return path; }
+  const QualifiedPathInExpression &get_qualified_path () const { return path; }
+
+  RangePatternBoundType get_bound_type () const override
+  {
+    return RangePatternBoundType::QUALPATH;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangePatternBoundQualPath *clone_range_pattern_bound_impl () const override
+  {
+    return new RangePatternBoundQualPath (*this);
+  }
+};
+
+// AST node for matching within a certain range (range pattern)
+class RangePattern : public Pattern
+{
+  std::unique_ptr<RangePatternBound> lower;
+  std::unique_ptr<RangePatternBound> upper;
+
+  bool has_ellipsis_syntax;
+
+  /* location only stored to avoid a dereference - lower pattern should give
+   * correct location so maybe change in future */
+  Location locus;
+  NodeId node_id;
+
+public:
+  std::string as_string () const override;
+
+  // Constructor
+  RangePattern (std::unique_ptr<RangePatternBound> lower,
+		std::unique_ptr<RangePatternBound> upper, Location locus,
+		bool has_ellipsis_syntax = false)
+    : lower (std::move (lower)), upper (std::move (upper)),
+      has_ellipsis_syntax (has_ellipsis_syntax), locus (locus),
+      node_id (Analysis::Mappings::get ()->get_next_node_id ())
+  {}
+
+  // Copy constructor with clone
+  RangePattern (RangePattern const &other)
+    : lower (other.lower->clone_range_pattern_bound ()),
+      upper (other.upper->clone_range_pattern_bound ()),
+      has_ellipsis_syntax (other.has_ellipsis_syntax), locus (other.locus),
+      node_id (other.node_id)
+  {}
+
+  // Overloaded assignment operator to clone
+  RangePattern &operator= (RangePattern const &other)
+  {
+    lower = other.lower->clone_range_pattern_bound ();
+    upper = other.upper->clone_range_pattern_bound ();
+    has_ellipsis_syntax = other.has_ellipsis_syntax;
+    locus = other.locus;
+    node_id = other.node_id;
+
+    return *this;
+  }
+
+  // default move semantics
+  RangePattern (RangePattern &&other) = default;
+  RangePattern &operator= (RangePattern &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: is this better? or is a "vis_bound" better?
+  std::unique_ptr<RangePatternBound> &get_lower_bound ()
+  {
+    rust_assert (lower != nullptr);
+    return lower;
+  }
+
+  std::unique_ptr<RangePatternBound> &get_upper_bound ()
+  {
+    rust_assert (upper != nullptr);
+    return upper;
+  }
+
+  NodeId get_node_id () const { return node_id; }
+
+  NodeId get_pattern_node_id () const override final { return node_id; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangePattern *clone_pattern_impl () const override
+  {
+    return new RangePattern (*this);
+  }
+};
+
+// AST node for pattern based on dereferencing the pointers given
+class ReferencePattern : public Pattern
+{
+  bool has_two_amps;
+  bool is_mut;
+  std::unique_ptr<Pattern> pattern;
+  Location locus;
+  NodeId node_id;
+
+public:
+  std::string as_string () const override;
+
+  ReferencePattern (std::unique_ptr<Pattern> pattern, bool is_mut_reference,
+		    bool ref_has_two_amps, Location locus)
+    : has_two_amps (ref_has_two_amps), is_mut (is_mut_reference),
+      pattern (std::move (pattern)), locus (locus),
+      node_id (Analysis::Mappings::get ()->get_next_node_id ())
+  {}
+
+  // Copy constructor requires clone
+  ReferencePattern (ReferencePattern const &other)
+    : has_two_amps (other.has_two_amps), is_mut (other.is_mut),
+      pattern (other.pattern->clone_pattern ()), locus (other.locus),
+      node_id (other.node_id)
+  {}
+
+  // Overload assignment operator to clone
+  ReferencePattern &operator= (ReferencePattern const &other)
+  {
+    pattern = other.pattern->clone_pattern ();
+    is_mut = other.is_mut;
+    has_two_amps = other.has_two_amps;
+    locus = other.locus;
+    node_id = other.node_id;
+
+    return *this;
+  }
+
+  // default move semantics
+  ReferencePattern (ReferencePattern &&other) = default;
+  ReferencePattern &operator= (ReferencePattern &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: is this better? Or is a "vis_pattern" better?
+  std::unique_ptr<Pattern> &get_referenced_pattern ()
+  {
+    rust_assert (pattern != nullptr);
+    return pattern;
+  }
+
+  NodeId get_node_id () const { return node_id; }
+
+  NodeId get_pattern_node_id () const override final { return node_id; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ReferencePattern *clone_pattern_impl () const override
+  {
+    return new ReferencePattern (*this);
+  }
+};
+
+#if 0
+// aka StructPatternEtCetera; potential element in struct pattern
+struct StructPatternEtc
+{
+private:
+  std::vector<Attribute> outer_attrs;
+
+  // should this store location data?
+
+public:
+  StructPatternEtc (std::vector<Attribute> outer_attribs)
+    : outer_attrs (std::move (outer_attribs))
+  {}
+
+  // Creates an empty StructPatternEtc
+  static StructPatternEtc create_empty ()
+  {
+    return StructPatternEtc (std::vector<Attribute> ());
+  }
+};
+#endif
+
+// Base class for a single field in a struct pattern - abstract
+class StructPatternField
+{
+  std::vector<Attribute> outer_attrs;
+  Location locus;
+
+protected:
+  NodeId node_id;
+
+public:
+  enum ItemType
+  {
+    TUPLE_PAT,
+    IDENT_PAT,
+    IDENT
+  };
+
+  virtual ~StructPatternField () {}
+
+  // Unique pointer custom clone function
+  std::unique_ptr<StructPatternField> clone_struct_pattern_field () const
+  {
+    return std::unique_ptr<StructPatternField> (
+      clone_struct_pattern_field_impl ());
+  }
+
+  virtual std::string as_string () const;
+
+  Location get_locus () const { return locus; }
+
+  virtual void accept_vis (ASTVisitor &vis) = 0;
+
+  virtual void mark_for_strip () = 0;
+  virtual bool is_marked_for_strip () const = 0;
+  virtual ItemType get_item_type () const = 0;
+
+  NodeId get_node_id () const { return node_id; }
+
+  // TODO: seems kinda dodgy. Think of better way.
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+
+protected:
+  StructPatternField (std::vector<Attribute> outer_attribs, Location locus,
+		      NodeId node_id)
+    : outer_attrs (std::move (outer_attribs)), locus (locus), node_id (node_id)
+  {}
+
+  // Clone function implementation as pure virtual method
+  virtual StructPatternField *clone_struct_pattern_field_impl () const = 0;
+};
+
+// Tuple pattern single field in a struct pattern
+class StructPatternFieldTuplePat : public StructPatternField
+{
+  TupleIndex index;
+  std::unique_ptr<Pattern> tuple_pattern;
+
+public:
+  StructPatternFieldTuplePat (TupleIndex index,
+			      std::unique_ptr<Pattern> tuple_pattern,
+			      std::vector<Attribute> outer_attribs,
+			      Location locus)
+    : StructPatternField (std::move (outer_attribs), locus,
+			  Analysis::Mappings::get ()->get_next_node_id ()),
+      index (index), tuple_pattern (std::move (tuple_pattern))
+  {}
+
+  // Copy constructor requires clone
+  StructPatternFieldTuplePat (StructPatternFieldTuplePat const &other)
+    : StructPatternField (other), index (other.index)
+  {
+    // guard to prevent null dereference (only required if error state)
+    node_id = other.get_node_id ();
+    if (other.tuple_pattern != nullptr)
+      tuple_pattern = other.tuple_pattern->clone_pattern ();
+  }
+
+  // Overload assignment operator to perform clone
+  StructPatternFieldTuplePat &
+  operator= (StructPatternFieldTuplePat const &other)
+  {
+    StructPatternField::operator= (other);
+    index = other.index;
+    // outer_attrs = other.outer_attrs;
+    node_id = other.get_node_id ();
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.tuple_pattern != nullptr)
+      tuple_pattern = other.tuple_pattern->clone_pattern ();
+    else
+      tuple_pattern = nullptr;
+
+    return *this;
+  }
+
+  // default move semantics
+  StructPatternFieldTuplePat (StructPatternFieldTuplePat &&other) = default;
+  StructPatternFieldTuplePat &operator= (StructPatternFieldTuplePat &&other)
+    = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // based on idea of tuple pattern no longer existing
+  void mark_for_strip () override { tuple_pattern = nullptr; }
+  bool is_marked_for_strip () const override
+  {
+    return tuple_pattern == nullptr;
+  }
+
+  // TODO: is this better? Or is a "vis_pattern" better?
+  std::unique_ptr<Pattern> &get_index_pattern ()
+  {
+    rust_assert (tuple_pattern != nullptr);
+    return tuple_pattern;
+  }
+
+  ItemType get_item_type () const override final { return ItemType::TUPLE_PAT; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  StructPatternFieldTuplePat *clone_struct_pattern_field_impl () const override
+  {
+    return new StructPatternFieldTuplePat (*this);
+  }
+};
+
+// Identifier pattern single field in a struct pattern
+class StructPatternFieldIdentPat : public StructPatternField
+{
+  Identifier ident;
+  std::unique_ptr<Pattern> ident_pattern;
+
+public:
+  StructPatternFieldIdentPat (Identifier ident,
+			      std::unique_ptr<Pattern> ident_pattern,
+			      std::vector<Attribute> outer_attrs,
+			      Location locus)
+    : StructPatternField (std::move (outer_attrs), locus,
+			  Analysis::Mappings::get ()->get_next_node_id ()),
+      ident (std::move (ident)), ident_pattern (std::move (ident_pattern))
+  {}
+
+  // Copy constructor requires clone
+  StructPatternFieldIdentPat (StructPatternFieldIdentPat const &other)
+    : StructPatternField (other), ident (other.ident)
+  {
+    // guard to prevent null dereference (only required if error state)
+    node_id = other.get_node_id ();
+    if (other.ident_pattern != nullptr)
+      ident_pattern = other.ident_pattern->clone_pattern ();
+  }
+
+  // Overload assignment operator to clone
+  StructPatternFieldIdentPat &
+  operator= (StructPatternFieldIdentPat const &other)
+  {
+    StructPatternField::operator= (other);
+    ident = other.ident;
+    // outer_attrs = other.outer_attrs;
+    node_id = other.get_node_id ();
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.ident_pattern != nullptr)
+      ident_pattern = other.ident_pattern->clone_pattern ();
+    else
+      ident_pattern = nullptr;
+
+    return *this;
+  }
+
+  // default move semantics
+  StructPatternFieldIdentPat (StructPatternFieldIdentPat &&other) = default;
+  StructPatternFieldIdentPat &operator= (StructPatternFieldIdentPat &&other)
+    = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // based on idea of identifier pattern no longer existing
+  void mark_for_strip () override { ident_pattern = nullptr; }
+  bool is_marked_for_strip () const override
+  {
+    return ident_pattern == nullptr;
+  }
+
+  const Identifier &get_identifier () const { return ident; }
+
+  // TODO: is this better? Or is a "vis_pattern" better?
+  std::unique_ptr<Pattern> &get_ident_pattern ()
+  {
+    rust_assert (ident_pattern != nullptr);
+    return ident_pattern;
+  }
+
+  ItemType get_item_type () const override final { return ItemType::IDENT_PAT; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  StructPatternFieldIdentPat *clone_struct_pattern_field_impl () const override
+  {
+    return new StructPatternFieldIdentPat (*this);
+  }
+};
+
+// Identifier only (with no pattern) single field in a struct pattern
+class StructPatternFieldIdent : public StructPatternField
+{
+  bool has_ref;
+  bool has_mut;
+  Identifier ident;
+
+public:
+  StructPatternFieldIdent (Identifier ident, bool is_ref, bool is_mut,
+			   std::vector<Attribute> outer_attrs, Location locus)
+    : StructPatternField (std::move (outer_attrs), locus,
+			  Analysis::Mappings::get ()->get_next_node_id ()),
+      has_ref (is_ref), has_mut (is_mut), ident (std::move (ident))
+  {}
+
+  std::string as_string () const override;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // based on idea of identifier no longer existing
+  void mark_for_strip () override { ident = {}; }
+  bool is_marked_for_strip () const override { return ident.empty (); }
+
+  const Identifier &get_identifier () const { return ident; }
+
+  ItemType get_item_type () const override final { return ItemType::IDENT; }
+
+  bool is_ref () const { return has_ref; }
+
+  bool is_mut () const { return has_mut; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  StructPatternFieldIdent *clone_struct_pattern_field_impl () const override
+  {
+    return new StructPatternFieldIdent (*this);
+  }
+};
+
+// Elements of a struct pattern
+struct StructPatternElements
+{
+private:
+  // bool has_struct_pattern_fields;
+  std::vector<std::unique_ptr<StructPatternField> > fields;
+
+  bool has_struct_pattern_etc;
+  std::vector<Attribute> struct_pattern_etc_attrs;
+  // StructPatternEtc etc;
+
+  // must have at least one of the two and maybe both
+
+  // should this store location data?
+
+public:
+  // Returns whether there are any struct pattern fields
+  bool has_struct_pattern_fields () const { return !fields.empty (); }
+
+  /* Returns whether the struct pattern elements is entirely empty (no fields,
+   * no etc). */
+  bool is_empty () const
+  {
+    return !has_struct_pattern_fields () && !has_struct_pattern_etc;
+  }
+
+  bool has_etc () const { return has_struct_pattern_etc; }
+
+  // Constructor for StructPatternElements with both (potentially)
+  StructPatternElements (
+    std::vector<std::unique_ptr<StructPatternField> > fields,
+    std::vector<Attribute> etc_attrs)
+    : fields (std::move (fields)), has_struct_pattern_etc (true),
+      struct_pattern_etc_attrs (std::move (etc_attrs))
+  {}
+
+  // Constructor for StructPatternElements with no StructPatternEtc
+  StructPatternElements (
+    std::vector<std::unique_ptr<StructPatternField> > fields)
+    : fields (std::move (fields)), has_struct_pattern_etc (false),
+      struct_pattern_etc_attrs ()
+  {}
+
+  // Copy constructor with vector clone
+  StructPatternElements (StructPatternElements const &other)
+    : has_struct_pattern_etc (other.has_struct_pattern_etc),
+      struct_pattern_etc_attrs (other.struct_pattern_etc_attrs)
+  {
+    fields.reserve (other.fields.size ());
+    for (const auto &e : other.fields)
+      fields.push_back (e->clone_struct_pattern_field ());
+  }
+
+  // Overloaded assignment operator with vector clone
+  StructPatternElements &operator= (StructPatternElements const &other)
+  {
+    struct_pattern_etc_attrs = other.struct_pattern_etc_attrs;
+    has_struct_pattern_etc = other.has_struct_pattern_etc;
+
+    fields.reserve (other.fields.size ());
+    for (const auto &e : other.fields)
+      fields.push_back (e->clone_struct_pattern_field ());
+
+    return *this;
+  }
+
+  // move constructors
+  StructPatternElements (StructPatternElements &&other) = default;
+  StructPatternElements &operator= (StructPatternElements &&other) = default;
+
+  // Creates an empty StructPatternElements
+  static StructPatternElements create_empty ()
+  {
+    return StructPatternElements (
+      std::vector<std::unique_ptr<StructPatternField> > ());
+  }
+
+  std::string as_string () const;
+
+  // TODO: seems kinda dodgy. Think of better way.
+  std::vector<std::unique_ptr<StructPatternField> > &
+  get_struct_pattern_fields ()
+  {
+    return fields;
+  }
+  const std::vector<std::unique_ptr<StructPatternField> > &
+  get_struct_pattern_fields () const
+  {
+    return fields;
+  }
+
+  std::vector<Attribute> &get_etc_outer_attrs ()
+  {
+    return struct_pattern_etc_attrs;
+  }
+  const std::vector<Attribute> &get_etc_outer_attrs () const
+  {
+    return struct_pattern_etc_attrs;
+  }
+
+  void strip_etc ()
+  {
+    has_struct_pattern_etc = false;
+    struct_pattern_etc_attrs.clear ();
+    struct_pattern_etc_attrs.shrink_to_fit ();
+  }
+};
+
+// Struct pattern AST node representation
+class StructPattern : public Pattern
+{
+  PathInExpression path;
+
+  // bool has_struct_pattern_elements;
+  StructPatternElements elems;
+
+  NodeId node_id;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  // Constructs a struct pattern from specified StructPatternElements
+  StructPattern (PathInExpression struct_path, Location locus,
+		 StructPatternElements elems
+		 = StructPatternElements::create_empty ())
+    : path (std::move (struct_path)), elems (std::move (elems)),
+      node_id (Analysis::Mappings::get ()->get_next_node_id ()), locus (locus)
+  {}
+
+  /* TODO: constructor to construct via elements included in
+   * StructPatternElements */
+
+  /* Returns whether struct pattern has any struct pattern elements (if not, it
+   * is empty). */
+  bool has_struct_pattern_elems () const { return !elems.is_empty (); }
+
+  Location get_locus () const { return path.get_locus (); }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: seems kinda dodgy. Think of better way.
+  StructPatternElements &get_struct_pattern_elems () { return elems; }
+  const StructPatternElements &get_struct_pattern_elems () const
+  {
+    return elems;
+  }
+
+  PathInExpression &get_path () { return path; }
+  const PathInExpression &get_path () const { return path; }
+
+  NodeId get_node_id () const { return node_id; }
+
+  NodeId get_pattern_node_id () const override final { return node_id; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  StructPattern *clone_pattern_impl () const override
+  {
+    return new StructPattern (*this);
+  }
+};
+
+// Base abstract class for patterns used in TupleStructPattern
+class TupleStructItems
+{
+public:
+  enum ItemType
+  {
+    RANGE,
+    NO_RANGE
+  };
+
+  virtual ~TupleStructItems () {}
+
+  // TODO: should this store location data?
+
+  // Unique pointer custom clone function
+  std::unique_ptr<TupleStructItems> clone_tuple_struct_items () const
+  {
+    return std::unique_ptr<TupleStructItems> (clone_tuple_struct_items_impl ());
+  }
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (ASTVisitor &vis) = 0;
+
+  virtual ItemType get_item_type () const = 0;
+
+protected:
+  // pure virtual clone implementation
+  virtual TupleStructItems *clone_tuple_struct_items_impl () const = 0;
+};
+
+// Class for non-ranged tuple struct pattern patterns
+class TupleStructItemsNoRange : public TupleStructItems
+{
+  std::vector<std::unique_ptr<Pattern> > patterns;
+
+public:
+  TupleStructItemsNoRange (std::vector<std::unique_ptr<Pattern> > patterns)
+    : patterns (std::move (patterns))
+  {}
+
+  // Copy constructor with vector clone
+  TupleStructItemsNoRange (TupleStructItemsNoRange const &other)
+  {
+    patterns.reserve (other.patterns.size ());
+    for (const auto &e : other.patterns)
+      patterns.push_back (e->clone_pattern ());
+  }
+
+  // Overloaded assignment operator with vector clone
+  TupleStructItemsNoRange &operator= (TupleStructItemsNoRange const &other)
+  {
+    patterns.reserve (other.patterns.size ());
+    for (const auto &e : other.patterns)
+      patterns.push_back (e->clone_pattern ());
+
+    return *this;
+  }
+
+  // move constructors
+  TupleStructItemsNoRange (TupleStructItemsNoRange &&other) = default;
+  TupleStructItemsNoRange &operator= (TupleStructItemsNoRange &&other)
+    = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: seems kinda dodgy. Think of better way.
+  std::vector<std::unique_ptr<Pattern> > &get_patterns () { return patterns; }
+  const std::vector<std::unique_ptr<Pattern> > &get_patterns () const
+  {
+    return patterns;
+  }
+
+  ItemType get_item_type () const override final { return ItemType::NO_RANGE; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TupleStructItemsNoRange *clone_tuple_struct_items_impl () const override
+  {
+    return new TupleStructItemsNoRange (*this);
+  }
+};
+
+// Class for ranged tuple struct pattern patterns
+class TupleStructItemsRange : public TupleStructItems
+{
+  std::vector<std::unique_ptr<Pattern> > lower_patterns;
+  std::vector<std::unique_ptr<Pattern> > upper_patterns;
+
+public:
+  TupleStructItemsRange (std::vector<std::unique_ptr<Pattern> > lower_patterns,
+			 std::vector<std::unique_ptr<Pattern> > upper_patterns)
+    : lower_patterns (std::move (lower_patterns)),
+      upper_patterns (std::move (upper_patterns))
+  {}
+
+  // Copy constructor with vector clone
+  TupleStructItemsRange (TupleStructItemsRange const &other)
+  {
+    lower_patterns.reserve (other.lower_patterns.size ());
+    for (const auto &e : other.lower_patterns)
+      lower_patterns.push_back (e->clone_pattern ());
+
+    upper_patterns.reserve (other.upper_patterns.size ());
+    for (const auto &e : other.upper_patterns)
+      upper_patterns.push_back (e->clone_pattern ());
+  }
+
+  // Overloaded assignment operator to clone
+  TupleStructItemsRange &operator= (TupleStructItemsRange const &other)
+  {
+    lower_patterns.reserve (other.lower_patterns.size ());
+    for (const auto &e : other.lower_patterns)
+      lower_patterns.push_back (e->clone_pattern ());
+
+    upper_patterns.reserve (other.upper_patterns.size ());
+    for (const auto &e : other.upper_patterns)
+      upper_patterns.push_back (e->clone_pattern ());
+
+    return *this;
+  }
+
+  // move constructors
+  TupleStructItemsRange (TupleStructItemsRange &&other) = default;
+  TupleStructItemsRange &operator= (TupleStructItemsRange &&other) = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: seems kinda dodgy. Think of better way.
+  std::vector<std::unique_ptr<Pattern> > &get_lower_patterns ()
+  {
+    return lower_patterns;
+  }
+  const std::vector<std::unique_ptr<Pattern> > &get_lower_patterns () const
+  {
+    return lower_patterns;
+  }
+
+  // TODO: seems kinda dodgy. Think of better way.
+  std::vector<std::unique_ptr<Pattern> > &get_upper_patterns ()
+  {
+    return upper_patterns;
+  }
+  const std::vector<std::unique_ptr<Pattern> > &get_upper_patterns () const
+  {
+    return upper_patterns;
+  }
+
+  ItemType get_item_type () const override final { return ItemType::RANGE; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TupleStructItemsRange *clone_tuple_struct_items_impl () const override
+  {
+    return new TupleStructItemsRange (*this);
+  }
+};
+
+// AST node representing a tuple struct pattern
+class TupleStructPattern : public Pattern
+{
+  PathInExpression path;
+  std::unique_ptr<TupleStructItems> items;
+  NodeId node_id;
+
+  /* TOOD: should this store location data? current accessor uses path location
+   * data */
+
+public:
+  std::string as_string () const override;
+
+  // Returns whether the pattern has tuple struct items.
+  bool has_items () const { return items != nullptr; }
+
+  TupleStructPattern (PathInExpression tuple_struct_path,
+		      std::unique_ptr<TupleStructItems> items)
+    : path (std::move (tuple_struct_path)), items (std::move (items)),
+      node_id (Analysis::Mappings::get ()->get_next_node_id ())
+  {}
+
+  // Copy constructor required to clone
+  TupleStructPattern (TupleStructPattern const &other) : path (other.path)
+  {
+    // guard to protect from null dereference
+    node_id = other.node_id;
+    if (other.items != nullptr)
+      items = other.items->clone_tuple_struct_items ();
+  }
+
+  // Operator overload assignment operator to clone
+  TupleStructPattern &operator= (TupleStructPattern const &other)
+  {
+    path = other.path;
+    node_id = other.node_id;
+
+    // guard to protect from null dereference
+    if (other.items != nullptr)
+      items = other.items->clone_tuple_struct_items ();
+    else
+      items = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  TupleStructPattern (TupleStructPattern &&other) = default;
+  TupleStructPattern &operator= (TupleStructPattern &&other) = default;
+
+  Location get_locus () const override { return path.get_locus (); }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: seems kinda dodgy. Think of better way.
+  std::unique_ptr<TupleStructItems> &get_items ()
+  {
+    rust_assert (has_items ());
+    return items;
+  }
+
+  PathInExpression &get_path () { return path; }
+  const PathInExpression &get_path () const { return path; }
+
+  NodeId get_node_id () const { return node_id; }
+
+  NodeId get_pattern_node_id () const override final { return node_id; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TupleStructPattern *clone_pattern_impl () const override
+  {
+    return new TupleStructPattern (*this);
+  }
+};
+
+// Base abstract class representing TuplePattern patterns
+class TuplePatternItems
+{
+public:
+  enum TuplePatternItemType
+  {
+    MULTIPLE,
+    RANGED,
+  };
+
+  virtual ~TuplePatternItems () {}
+
+  // TODO: should this store location data?
+
+  // Unique pointer custom clone function
+  std::unique_ptr<TuplePatternItems> clone_tuple_pattern_items () const
+  {
+    return std::unique_ptr<TuplePatternItems> (
+      clone_tuple_pattern_items_impl ());
+  }
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (ASTVisitor &vis) = 0;
+
+  virtual TuplePatternItemType get_pattern_type () const = 0;
+
+protected:
+  // pure virtual clone implementation
+  virtual TuplePatternItems *clone_tuple_pattern_items_impl () const = 0;
+};
+
+// Class representing TuplePattern patterns where there is only a single pattern
+/*class TuplePatternItemsSingle : public TuplePatternItems {
+    // Pattern pattern;
+    std::unique_ptr<Pattern> pattern;
+
+  public:
+    TuplePatternItemsSingle(Pattern* pattern) : pattern(pattern) {}
+
+    // Copy constructor uses clone
+    TuplePatternItemsSingle(TuplePatternItemsSingle const& other) :
+      pattern(other.pattern->clone_pattern()) {}
+
+    // Destructor - define here if required
+
+    // Overload assignment operator to clone
+    TuplePatternItemsSingle& operator=(TuplePatternItemsSingle const& other) {
+	pattern = other.pattern->clone_pattern();
+
+	return *this;
+    }
+
+    // move constructors
+    TuplePatternItemsSingle(TuplePatternItemsSingle&& other) = default;
+    TuplePatternItemsSingle& operator=(TuplePatternItemsSingle&& other) =
+default;
+
+  protected:
+    // Use covariance to implement clone function as returning this object
+rather than base virtual TuplePatternItemsSingle*
+clone_tuple_pattern_items_impl() const override { return new
+TuplePatternItemsSingle(*this);
+    }
+};*/
+// removed in favour of single-element TuplePatternItemsMultiple
+
+// Class representing TuplePattern patterns where there are multiple patterns
+class TuplePatternItemsMultiple : public TuplePatternItems
+{
+  std::vector<std::unique_ptr<Pattern> > patterns;
+
+public:
+  TuplePatternItemsMultiple (std::vector<std::unique_ptr<Pattern> > patterns)
+    : patterns (std::move (patterns))
+  {}
+
+  // Copy constructor with vector clone
+  TuplePatternItemsMultiple (TuplePatternItemsMultiple const &other)
+  {
+    patterns.reserve (other.patterns.size ());
+    for (const auto &e : other.patterns)
+      patterns.push_back (e->clone_pattern ());
+  }
+
+  // Overloaded assignment operator to vector clone
+  TuplePatternItemsMultiple &operator= (TuplePatternItemsMultiple const &other)
+  {
+    patterns.reserve (other.patterns.size ());
+    for (const auto &e : other.patterns)
+      patterns.push_back (e->clone_pattern ());
+
+    return *this;
+  }
+
+  // move constructors
+  TuplePatternItemsMultiple (TuplePatternItemsMultiple &&other) = default;
+  TuplePatternItemsMultiple &operator= (TuplePatternItemsMultiple &&other)
+    = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: seems kinda dodgy. Think of better way.
+  std::vector<std::unique_ptr<Pattern> > &get_patterns () { return patterns; }
+  const std::vector<std::unique_ptr<Pattern> > &get_patterns () const
+  {
+    return patterns;
+  }
+
+  TuplePatternItemType get_pattern_type () const override
+  {
+    return TuplePatternItemType::MULTIPLE;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TuplePatternItemsMultiple *clone_tuple_pattern_items_impl () const override
+  {
+    return new TuplePatternItemsMultiple (*this);
+  }
+};
+
+// Class representing TuplePattern patterns where there are a range of patterns
+class TuplePatternItemsRanged : public TuplePatternItems
+{
+  std::vector<std::unique_ptr<Pattern> > lower_patterns;
+  std::vector<std::unique_ptr<Pattern> > upper_patterns;
+
+public:
+  TuplePatternItemsRanged (
+    std::vector<std::unique_ptr<Pattern> > lower_patterns,
+    std::vector<std::unique_ptr<Pattern> > upper_patterns)
+    : lower_patterns (std::move (lower_patterns)),
+      upper_patterns (std::move (upper_patterns))
+  {}
+
+  // Copy constructor with vector clone
+  TuplePatternItemsRanged (TuplePatternItemsRanged const &other)
+  {
+    lower_patterns.reserve (other.lower_patterns.size ());
+    for (const auto &e : other.lower_patterns)
+      lower_patterns.push_back (e->clone_pattern ());
+
+    upper_patterns.reserve (other.upper_patterns.size ());
+    for (const auto &e : other.upper_patterns)
+      upper_patterns.push_back (e->clone_pattern ());
+  }
+
+  // Overloaded assignment operator to clone
+  TuplePatternItemsRanged &operator= (TuplePatternItemsRanged const &other)
+  {
+    lower_patterns.reserve (other.lower_patterns.size ());
+    for (const auto &e : other.lower_patterns)
+      lower_patterns.push_back (e->clone_pattern ());
+
+    upper_patterns.reserve (other.upper_patterns.size ());
+    for (const auto &e : other.upper_patterns)
+      upper_patterns.push_back (e->clone_pattern ());
+
+    return *this;
+  }
+
+  // move constructors
+  TuplePatternItemsRanged (TuplePatternItemsRanged &&other) = default;
+  TuplePatternItemsRanged &operator= (TuplePatternItemsRanged &&other)
+    = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: seems kinda dodgy. Think of better way.
+  std::vector<std::unique_ptr<Pattern> > &get_lower_patterns ()
+  {
+    return lower_patterns;
+  }
+  const std::vector<std::unique_ptr<Pattern> > &get_lower_patterns () const
+  {
+    return lower_patterns;
+  }
+
+  // TODO: seems kinda dodgy. Think of better way.
+  std::vector<std::unique_ptr<Pattern> > &get_upper_patterns ()
+  {
+    return upper_patterns;
+  }
+  const std::vector<std::unique_ptr<Pattern> > &get_upper_patterns () const
+  {
+    return upper_patterns;
+  }
+
+  TuplePatternItemType get_pattern_type () const override
+  {
+    return TuplePatternItemType::RANGED;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TuplePatternItemsRanged *clone_tuple_pattern_items_impl () const override
+  {
+    return new TuplePatternItemsRanged (*this);
+  }
+};
+
+// AST node representing a tuple pattern
+class TuplePattern : public Pattern
+{
+  // bool has_tuple_pattern_items;
+  std::unique_ptr<TuplePatternItems> items;
+  Location locus;
+  NodeId node_id;
+
+public:
+  std::string as_string () const override;
+
+  // Returns true if the tuple pattern has items
+  bool has_tuple_pattern_items () const { return items != nullptr; }
+
+  TuplePattern (std::unique_ptr<TuplePatternItems> items, Location locus)
+    : items (std::move (items)), locus (locus),
+      node_id (Analysis::Mappings::get ()->get_next_node_id ())
+  {}
+
+  // Copy constructor requires clone
+  TuplePattern (TuplePattern const &other) : locus (other.locus)
+  {
+    // guard to prevent null dereference
+    node_id = other.node_id;
+    if (other.items != nullptr)
+      items = other.items->clone_tuple_pattern_items ();
+  }
+
+  // Overload assignment operator to clone
+  TuplePattern &operator= (TuplePattern const &other)
+  {
+    locus = other.locus;
+    node_id = other.node_id;
+
+    // guard to prevent null dereference
+    if (other.items != nullptr)
+      items = other.items->clone_tuple_pattern_items ();
+    else
+      items = nullptr;
+
+    return *this;
+  }
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: seems kinda dodgy. Think of better way.
+  std::unique_ptr<TuplePatternItems> &get_items ()
+  {
+    rust_assert (has_tuple_pattern_items ());
+    return items;
+  }
+
+  NodeId get_node_id () const { return node_id; }
+
+  NodeId get_pattern_node_id () const override final { return node_id; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TuplePattern *clone_pattern_impl () const override
+  {
+    return new TuplePattern (*this);
+  }
+};
+
+// AST node representing a pattern in parentheses, used to control precedence
+class GroupedPattern : public Pattern
+{
+  std::unique_ptr<Pattern> pattern_in_parens;
+  Location locus;
+  NodeId node_id;
+
+public:
+  std::string as_string () const override
+  {
+    return "(" + pattern_in_parens->as_string () + ")";
+  }
+
+  GroupedPattern (std::unique_ptr<Pattern> pattern_in_parens, Location locus)
+    : pattern_in_parens (std::move (pattern_in_parens)), locus (locus),
+      node_id (Analysis::Mappings::get ()->get_next_node_id ())
+  {}
+
+  // Copy constructor uses clone
+  GroupedPattern (GroupedPattern const &other)
+    : pattern_in_parens (other.pattern_in_parens->clone_pattern ()),
+      locus (other.locus), node_id (other.node_id)
+  {}
+
+  // Overload assignment operator to clone
+  GroupedPattern &operator= (GroupedPattern const &other)
+  {
+    pattern_in_parens = other.pattern_in_parens->clone_pattern ();
+    locus = other.locus;
+    node_id = other.node_id;
+
+    return *this;
+  }
+
+  // default move semantics
+  GroupedPattern (GroupedPattern &&other) = default;
+  GroupedPattern &operator= (GroupedPattern &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: seems kinda dodgy. Think of better way.
+  std::unique_ptr<Pattern> &get_pattern_in_parens ()
+  {
+    rust_assert (pattern_in_parens != nullptr);
+    return pattern_in_parens;
+  }
+
+  NodeId get_node_id () const { return node_id; }
+
+  NodeId get_pattern_node_id () const override final { return node_id; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  GroupedPattern *clone_pattern_impl () const override
+  {
+    return new GroupedPattern (*this);
+  }
+};
+
+// AST node representing patterns that can match slices and arrays
+class SlicePattern : public Pattern
+{
+  std::vector<std::unique_ptr<Pattern> > items;
+  Location locus;
+  NodeId node_id;
+
+public:
+  std::string as_string () const override;
+
+  SlicePattern (std::vector<std::unique_ptr<Pattern> > items, Location locus)
+    : items (std::move (items)), locus (locus),
+      node_id (Analysis::Mappings::get ()->get_next_node_id ())
+  {}
+
+  // Copy constructor with vector clone
+  SlicePattern (SlicePattern const &other) : locus (other.locus)
+  {
+    node_id = other.node_id;
+    items.reserve (other.items.size ());
+    for (const auto &e : other.items)
+      items.push_back (e->clone_pattern ());
+  }
+
+  // Overloaded assignment operator to vector clone
+  SlicePattern &operator= (SlicePattern const &other)
+  {
+    locus = other.locus;
+    node_id = other.node_id;
+
+    items.reserve (other.items.size ());
+    for (const auto &e : other.items)
+      items.push_back (e->clone_pattern ());
+
+    return *this;
+  }
+
+  // move constructors
+  SlicePattern (SlicePattern &&other) = default;
+  SlicePattern &operator= (SlicePattern &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: seems kinda dodgy. Think of better way.
+  std::vector<std::unique_ptr<Pattern> > &get_items () { return items; }
+  const std::vector<std::unique_ptr<Pattern> > &get_items () const
+  {
+    return items;
+  }
+
+  NodeId get_node_id () const { return node_id; }
+
+  NodeId get_pattern_node_id () const override final { return node_id; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  SlicePattern *clone_pattern_impl () const override
+  {
+    return new SlicePattern (*this);
+  }
+};
+
+// Moved definition to rust-path.h
+class PathPattern;
+
+// Forward decls for paths (defined in rust-path.h)
+class PathInExpression;
+class QualifiedPathInExpression;
+
+// Replaced with forward decl - defined in rust-macro.h
+class MacroInvocation;
+} // namespace AST
+} // namespace Rust
+
+#endif
diff --git a/gcc/rust/ast/rust-stmt.h b/gcc/rust/ast/rust-stmt.h
new file mode 100644
index 00000000000..9d95c3e27e8
--- /dev/null
+++ b/gcc/rust/ast/rust-stmt.h
@@ -0,0 +1,358 @@
+// 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_AST_STATEMENT_H
+#define RUST_AST_STATEMENT_H
+
+#include "rust-ast.h"
+#include "rust-path.h"
+#include "rust-expr.h"
+
+namespace Rust {
+namespace AST {
+// Just a semi-colon, which apparently is a statement.
+class EmptyStmt : public Stmt
+{
+  Location locus;
+
+  // TODO: find another way to store this to save memory?
+  bool marked_for_strip = false;
+
+public:
+  std::string as_string () const override { return std::string (1, ';'); }
+
+  EmptyStmt (Location locus) : locus (locus) {}
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Can't think of any invalid invariants, so store boolean.
+  void mark_for_strip () override { marked_for_strip = true; }
+  bool is_marked_for_strip () const override { return marked_for_strip; }
+
+  bool is_item () const override final { return false; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  EmptyStmt *clone_stmt_impl () const override { return new EmptyStmt (*this); }
+};
+
+/* Variable assignment let statement - type of "declaration statement" as it
+ * introduces new name into scope */
+class LetStmt : public Stmt
+{
+  // bool has_outer_attrs;
+  std::vector<Attribute> outer_attrs;
+
+  std::unique_ptr<Pattern> variables_pattern;
+
+  // bool has_type;
+  std::unique_ptr<Type> type;
+
+  // bool has_init_expr;
+  std::unique_ptr<Expr> init_expr;
+
+  Location locus;
+
+public:
+  Type *inferedType;
+
+  // Returns whether let statement has outer attributes.
+  bool has_outer_attrs () const { return !outer_attrs.empty (); }
+
+  // Returns whether let statement has a given return type.
+  bool has_type () const { return type != nullptr; }
+
+  // Returns whether let statement has an initialisation expression.
+  bool has_init_expr () const { return init_expr != nullptr; }
+
+  std::string as_string () const override;
+
+  LetStmt (std::unique_ptr<Pattern> variables_pattern,
+	   std::unique_ptr<Expr> init_expr, std::unique_ptr<Type> type,
+	   std::vector<Attribute> outer_attrs, Location locus)
+    : outer_attrs (std::move (outer_attrs)),
+      variables_pattern (std::move (variables_pattern)),
+      type (std::move (type)), init_expr (std::move (init_expr)), locus (locus)
+  {}
+
+  // Copy constructor with clone
+  LetStmt (LetStmt const &other)
+    : outer_attrs (other.outer_attrs), locus (other.locus)
+  {
+    // guard to prevent null dereference (only required if error state)
+    if (other.variables_pattern != nullptr)
+      variables_pattern = other.variables_pattern->clone_pattern ();
+
+    // guard to prevent null dereference (always required)
+    if (other.init_expr != nullptr)
+      init_expr = other.init_expr->clone_expr ();
+    if (other.type != nullptr)
+      type = other.type->clone_type ();
+  }
+
+  // Overloaded assignment operator to clone
+  LetStmt &operator= (LetStmt const &other)
+  {
+    outer_attrs = other.outer_attrs;
+    locus = other.locus;
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.variables_pattern != nullptr)
+      variables_pattern = other.variables_pattern->clone_pattern ();
+    else
+      variables_pattern = nullptr;
+
+    // guard to prevent null dereference (always required)
+    if (other.init_expr != nullptr)
+      init_expr = other.init_expr->clone_expr ();
+    else
+      init_expr = nullptr;
+    if (other.type != nullptr)
+      type = other.type->clone_type ();
+    else
+      type = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  LetStmt (LetStmt &&other) = default;
+  LetStmt &operator= (LetStmt &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Invalid if pattern is null, so base stripping on that.
+  void mark_for_strip () override { variables_pattern = nullptr; }
+  bool is_marked_for_strip () const override
+  {
+    return variables_pattern == nullptr;
+  }
+
+  // TODO: this mutable getter seems really dodgy. Think up better way.
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Expr> &get_init_expr ()
+  {
+    rust_assert (has_init_expr ());
+    return init_expr;
+  }
+
+  std::unique_ptr<Pattern> &get_pattern ()
+  {
+    rust_assert (variables_pattern != nullptr);
+    return variables_pattern;
+  }
+
+  std::unique_ptr<Type> &get_type ()
+  {
+    rust_assert (has_type ());
+    return type;
+  }
+
+  bool is_item () const override final { return false; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  LetStmt *clone_stmt_impl () const override { return new LetStmt (*this); }
+};
+
+/* Abstract base class for expression statements (statements containing an
+ * expression) */
+class ExprStmt : public Stmt
+{
+public:
+  enum ExprStmtType
+  {
+    WITH_BLOCK,
+    WITHOUT_BLOCK
+  };
+
+protected:
+  Location locus;
+
+public:
+  Location get_locus () const override final { return locus; }
+
+  bool is_item () const override final { return false; }
+
+  virtual ExprStmtType get_type () const = 0;
+
+protected:
+  ExprStmt (Location locus) : locus (locus) {}
+};
+
+/* Statement containing an expression without a block (or, due to technical
+ * difficulties, can only be guaranteed to hold an expression). */
+class ExprStmtWithoutBlock : public ExprStmt
+{
+  // TODO: ensure that this works
+  std::unique_ptr<ExprWithoutBlock> expr;
+  /* HACK: cannot ensure type safety of ExprWithoutBlock due to Pratt parsing,
+   * so have to store more general type of Expr. FIXME: fix this issue somehow
+   * or redesign AST. */
+  // std::unique_ptr<Expr> expr;
+
+public:
+  std::string as_string () const override;
+
+  ExprStmtWithoutBlock (std::unique_ptr<ExprWithoutBlock> expr, Location locus)
+    : ExprStmt (locus), expr (std::move (expr->to_stmt ()))
+  {}
+
+  /*ExprStmtWithoutBlock (std::unique_ptr<Expr> expr, Location locus)
+    : ExprStmt (locus), expr (std::move (expr))
+  {}*/
+
+  // Copy constructor with clone
+  ExprStmtWithoutBlock (ExprStmtWithoutBlock const &other) : ExprStmt (other)
+  {
+    // guard to prevent null dereference (only required if error state)
+    if (other.expr != nullptr)
+      expr = other.expr->clone_expr_without_block ();
+  }
+  /*ExprStmtWithoutBlock (ExprStmtWithoutBlock const &other)
+    : ExprStmt (other), expr (other.expr->clone_expr ())
+  {}*/
+
+  // Overloaded assignment operator to clone
+  ExprStmtWithoutBlock &operator= (ExprStmtWithoutBlock const &other)
+  {
+    ExprStmt::operator= (other);
+    // expr = other.expr->clone_expr ();
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.expr != nullptr)
+      expr = other.expr->clone_expr_without_block ();
+    else
+      expr = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  ExprStmtWithoutBlock (ExprStmtWithoutBlock &&other) = default;
+  ExprStmtWithoutBlock &operator= (ExprStmtWithoutBlock &&other) = default;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Invalid if expr is null, so base stripping on that.
+  void mark_for_strip () override { expr = nullptr; }
+  bool is_marked_for_strip () const override { return expr == nullptr; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<ExprWithoutBlock> &get_expr ()
+  {
+    rust_assert (expr != nullptr);
+    return expr;
+  }
+
+  ExprStmtType get_type () const override
+  {
+    return ExprStmtType::WITHOUT_BLOCK;
+  };
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ExprStmtWithoutBlock *clone_stmt_impl () const override
+  {
+    return new ExprStmtWithoutBlock (*this);
+  }
+};
+
+// Statement containing an expression with a block
+class ExprStmtWithBlock : public ExprStmt
+{
+  std::unique_ptr<ExprWithBlock> expr;
+  bool semicolon_followed;
+
+public:
+  std::string as_string () const override;
+
+  std::vector<LetStmt *> locals;
+
+  ExprStmtWithBlock (std::unique_ptr<ExprWithBlock> expr, Location locus,
+		     bool semicolon_followed)
+    : ExprStmt (locus), expr (std::move (expr)),
+      semicolon_followed (semicolon_followed)
+  {}
+
+  // Copy constructor with clone
+  ExprStmtWithBlock (ExprStmtWithBlock const &other) : ExprStmt (other)
+  {
+    // guard to prevent null dereference (only required if error state)
+    if (other.expr != nullptr)
+      expr = other.expr->clone_expr_with_block ();
+  }
+
+  // Overloaded assignment operator to clone
+  ExprStmtWithBlock &operator= (ExprStmtWithBlock const &other)
+  {
+    ExprStmt::operator= (other);
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.expr != nullptr)
+      expr = other.expr->clone_expr_with_block ();
+    else
+      expr = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  ExprStmtWithBlock (ExprStmtWithBlock &&other) = default;
+  ExprStmtWithBlock &operator= (ExprStmtWithBlock &&other) = default;
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // Invalid if expr is null, so base stripping on that.
+  void mark_for_strip () override { expr = nullptr; }
+  bool is_marked_for_strip () const override { return expr == nullptr; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<ExprWithBlock> &get_expr ()
+  {
+    rust_assert (expr != nullptr);
+    return expr;
+  }
+
+  bool is_semicolon_followed () const { return semicolon_followed; }
+
+  ExprStmtType get_type () const override { return ExprStmtType::WITH_BLOCK; };
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ExprStmtWithBlock *clone_stmt_impl () const override
+  {
+    return new ExprStmtWithBlock (*this);
+  }
+};
+
+} // namespace AST
+} // namespace Rust
+
+#endif
diff --git a/gcc/rust/ast/rust-type.h b/gcc/rust/ast/rust-type.h
new file mode 100644
index 00000000000..7e9e07d0c18
--- /dev/null
+++ b/gcc/rust/ast/rust-type.h
@@ -0,0 +1,962 @@
+// 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_AST_TYPE_H
+#define RUST_AST_TYPE_H
+
+#include "rust-ast.h"
+#include "rust-path.h"
+
+namespace Rust {
+namespace AST {
+// definitions moved to rust-ast.h
+class TypeParamBound;
+class Lifetime;
+
+// A trait bound
+class TraitBound : public TypeParamBound
+{
+  bool in_parens;
+  bool opening_question_mark;
+
+  // bool has_for_lifetimes;
+  // LifetimeParams for_lifetimes;
+  std::vector<LifetimeParam> for_lifetimes; // inlined LifetimeParams
+
+  TypePath type_path;
+
+  Location locus;
+
+public:
+  // Returns whether trait bound has "for" lifetimes
+  bool has_for_lifetimes () const { return !for_lifetimes.empty (); }
+
+  TraitBound (TypePath type_path, Location locus, bool in_parens = false,
+	      bool opening_question_mark = false,
+	      std::vector<LifetimeParam> for_lifetimes
+	      = std::vector<LifetimeParam> ())
+    : TypeParamBound (Analysis::Mappings::get ()->get_next_node_id ()),
+      in_parens (in_parens), opening_question_mark (opening_question_mark),
+      for_lifetimes (std::move (for_lifetimes)),
+      type_path (std::move (type_path)), locus (locus)
+  {}
+
+  TraitBound (NodeId id, TypePath type_path, Location locus,
+	      bool in_parens = false, bool opening_question_mark = false,
+	      std::vector<LifetimeParam> for_lifetimes
+	      = std::vector<LifetimeParam> ())
+    : TypeParamBound (id), in_parens (in_parens),
+      opening_question_mark (opening_question_mark),
+      for_lifetimes (std::move (for_lifetimes)),
+      type_path (std::move (type_path)), locus (locus)
+  {}
+
+  std::string as_string () const override;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: this mutable getter seems kinda dodgy
+  TypePath &get_type_path () { return type_path; }
+  const TypePath &get_type_path () const { return type_path; }
+
+  bool is_in_parens () const { return in_parens; }
+  bool has_opening_question_mark () const { return opening_question_mark; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TraitBound *clone_type_param_bound_impl () const override
+  {
+    return new TraitBound (node_id, type_path, locus, in_parens,
+			   opening_question_mark, for_lifetimes);
+  }
+};
+
+// definition moved to rust-ast.h
+class TypeNoBounds;
+
+// An impl trait? Poor reference material here.
+class ImplTraitType : public Type
+{
+  // TypeParamBounds type_param_bounds;
+  // inlined form
+  std::vector<std::unique_ptr<TypeParamBound> > type_param_bounds;
+
+  Location locus;
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ImplTraitType *clone_type_impl () const override
+  {
+    return new ImplTraitType (*this);
+  }
+
+public:
+  ImplTraitType (
+    std::vector<std::unique_ptr<TypeParamBound> > type_param_bounds,
+    Location locus)
+    : type_param_bounds (std::move (type_param_bounds)), locus (locus)
+  {}
+
+  // copy constructor with vector clone
+  ImplTraitType (ImplTraitType const &other) : locus (other.locus)
+  {
+    type_param_bounds.reserve (other.type_param_bounds.size ());
+    for (const auto &e : other.type_param_bounds)
+      type_param_bounds.push_back (e->clone_type_param_bound ());
+  }
+
+  // overloaded assignment operator to clone
+  ImplTraitType &operator= (ImplTraitType const &other)
+  {
+    locus = other.locus;
+
+    type_param_bounds.reserve (other.type_param_bounds.size ());
+    for (const auto &e : other.type_param_bounds)
+      type_param_bounds.push_back (e->clone_type_param_bound ());
+
+    return *this;
+  }
+
+  // move constructors
+  ImplTraitType (ImplTraitType &&other) = default;
+  ImplTraitType &operator= (ImplTraitType &&other) = default;
+
+  std::string as_string () const override;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: mutable getter seems kinda dodgy
+  std::vector<std::unique_ptr<TypeParamBound> > &get_type_param_bounds ()
+  {
+    return type_param_bounds;
+  }
+  const std::vector<std::unique_ptr<TypeParamBound> > &
+  get_type_param_bounds () const
+  {
+    return type_param_bounds;
+  }
+};
+
+// An opaque value of another type that implements a set of traits
+class TraitObjectType : public Type
+{
+  bool has_dyn;
+  std::vector<std::unique_ptr<TypeParamBound> > type_param_bounds;
+  Location locus;
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TraitObjectType *clone_type_impl () const override
+  {
+    return new TraitObjectType (*this);
+  }
+
+public:
+  TraitObjectType (
+    std::vector<std::unique_ptr<TypeParamBound> > type_param_bounds,
+    Location locus, bool is_dyn_dispatch)
+    : has_dyn (is_dyn_dispatch),
+      type_param_bounds (std::move (type_param_bounds)), locus (locus)
+  {}
+
+  // copy constructor with vector clone
+  TraitObjectType (TraitObjectType const &other)
+    : has_dyn (other.has_dyn), locus (other.locus)
+  {
+    type_param_bounds.reserve (other.type_param_bounds.size ());
+    for (const auto &e : other.type_param_bounds)
+      type_param_bounds.push_back (e->clone_type_param_bound ());
+  }
+
+  // overloaded assignment operator to clone
+  TraitObjectType &operator= (TraitObjectType const &other)
+  {
+    has_dyn = other.has_dyn;
+    locus = other.locus;
+    type_param_bounds.reserve (other.type_param_bounds.size ());
+    for (const auto &e : other.type_param_bounds)
+      type_param_bounds.push_back (e->clone_type_param_bound ());
+
+    return *this;
+  }
+
+  // move constructors
+  TraitObjectType (TraitObjectType &&other) = default;
+  TraitObjectType &operator= (TraitObjectType &&other) = default;
+
+  std::string as_string () const override;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  bool is_dyn () const { return has_dyn; }
+
+  // TODO: mutable getter seems kinda dodgy
+  std::vector<std::unique_ptr<TypeParamBound> > &get_type_param_bounds ()
+  {
+    return type_param_bounds;
+  }
+  const std::vector<std::unique_ptr<TypeParamBound> > &
+  get_type_param_bounds () const
+  {
+    return type_param_bounds;
+  }
+};
+
+// A type with parentheses around it, used to avoid ambiguity.
+class ParenthesisedType : public TypeNoBounds
+{
+  std::unique_ptr<Type> type_in_parens;
+  Location locus;
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ParenthesisedType *clone_type_no_bounds_impl () const override
+  {
+    return new ParenthesisedType (*this);
+  }
+
+public:
+  // Constructor uses Type pointer for polymorphism
+  ParenthesisedType (std::unique_ptr<Type> type_inside_parens, Location locus)
+    : type_in_parens (std::move (type_inside_parens)), locus (locus)
+  {}
+
+  /* Copy constructor uses custom deep copy method for type to preserve
+   * polymorphism */
+  ParenthesisedType (ParenthesisedType const &other)
+    : type_in_parens (other.type_in_parens->clone_type ()), locus (other.locus)
+  {}
+
+  // overload assignment operator to use custom clone method
+  ParenthesisedType &operator= (ParenthesisedType const &other)
+  {
+    type_in_parens = other.type_in_parens->clone_type ();
+    locus = other.locus;
+    return *this;
+  }
+
+  // default move semantics
+  ParenthesisedType (ParenthesisedType &&other) = default;
+  ParenthesisedType &operator= (ParenthesisedType &&other) = default;
+
+  std::string as_string () const override
+  {
+    return "(" + type_in_parens->as_string () + ")";
+  }
+
+  // Creates a trait bound (clone of this one's trait bound) - HACK
+  TraitBound *to_trait_bound (bool) const override
+  {
+    /* NOTE: obviously it is unknown whether the internal type is a trait bound
+     * due to polymorphism, so just let the internal type handle it. As
+     * parenthesised type, it must be in parentheses. */
+    return type_in_parens->to_trait_bound (true);
+  }
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: would a "vis_type" be better?
+  std::unique_ptr<Type> &get_type_in_parens ()
+  {
+    rust_assert (type_in_parens != nullptr);
+    return type_in_parens;
+  }
+};
+
+// Impl trait with a single bound? Poor reference material here.
+class ImplTraitTypeOneBound : public TypeNoBounds
+{
+  TraitBound trait_bound;
+  Location locus;
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ImplTraitTypeOneBound *clone_type_no_bounds_impl () const override
+  {
+    return new ImplTraitTypeOneBound (*this);
+  }
+
+public:
+  ImplTraitTypeOneBound (TraitBound trait_bound, Location locus)
+    : trait_bound (std::move (trait_bound)), locus (locus)
+  {}
+
+  std::string as_string () const override;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: would a "vis_type" be better?
+  TraitBound &get_trait_bound ()
+  {
+    // TODO: check to ensure invariants are met?
+    return trait_bound;
+  }
+};
+
+/* A trait object with a single trait bound. The "trait bound" is really just
+ * the trait. Basically like using an interface as a type in an OOP language. */
+class TraitObjectTypeOneBound : public TypeNoBounds
+{
+  bool has_dyn;
+  TraitBound trait_bound;
+  Location locus;
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TraitObjectTypeOneBound *clone_type_no_bounds_impl () const override
+  {
+    return new TraitObjectTypeOneBound (*this);
+  }
+
+public:
+  TraitObjectTypeOneBound (TraitBound trait_bound, Location locus,
+			   bool is_dyn_dispatch = false)
+    : has_dyn (is_dyn_dispatch), trait_bound (std::move (trait_bound)),
+      locus (locus)
+  {}
+
+  std::string as_string () const override;
+
+  // Creates a trait bound (clone of this one's trait bound) - HACK
+  TraitBound *to_trait_bound (bool) const override
+  {
+    /* NOTE: this assumes there is no dynamic dispatch specified- if there was,
+     * this cloning would not be required as parsing is unambiguous. */
+    return new TraitBound (trait_bound);
+  }
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: would a "vis_type" be better?
+  TraitBound &get_trait_bound ()
+  {
+    // TODO: check to ensure invariants are met?
+    return trait_bound;
+  }
+
+  bool is_dyn () const { return has_dyn; }
+};
+
+class TypePath; // definition moved to "rust-path.h"
+
+/* A type consisting of the "product" of others (the tuple's elements) in a
+ * specific order */
+class TupleType : public TypeNoBounds
+{
+  std::vector<std::unique_ptr<Type> > elems;
+  Location locus;
+
+public:
+  // Returns whether the tuple type is the unit type, i.e. has no elements.
+  bool is_unit_type () const { return elems.empty (); }
+
+  TupleType (std::vector<std::unique_ptr<Type> > elems, Location locus)
+    : elems (std::move (elems)), locus (locus)
+  {}
+
+  // copy constructor with vector clone
+  TupleType (TupleType const &other) : locus (other.locus)
+  {
+    elems.reserve (other.elems.size ());
+    for (const auto &e : other.elems)
+      elems.push_back (e->clone_type ());
+  }
+
+  // overloaded assignment operator to clone
+  TupleType &operator= (TupleType const &other)
+  {
+    locus = other.locus;
+
+    elems.reserve (other.elems.size ());
+    for (const auto &e : other.elems)
+      elems.push_back (e->clone_type ());
+
+    return *this;
+  }
+
+  // move constructors
+  TupleType (TupleType &&other) = default;
+  TupleType &operator= (TupleType &&other) = default;
+
+  std::string as_string () const override;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: mutable getter seems kinda dodgy
+  std::vector<std::unique_ptr<Type> > &get_elems () { return elems; }
+  const std::vector<std::unique_ptr<Type> > &get_elems () const
+  {
+    return elems;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TupleType *clone_type_no_bounds_impl () const override
+  {
+    return new TupleType (*this);
+  }
+};
+
+/* A type with no values, representing the result of computations that never
+ * complete. Expressions of NeverType can be coerced into any other types.
+ * Represented as "!". */
+class NeverType : public TypeNoBounds
+{
+  Location locus;
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  NeverType *clone_type_no_bounds_impl () const override
+  {
+    return new NeverType (*this);
+  }
+
+public:
+  NeverType (Location locus) : locus (locus) {}
+
+  std::string as_string () const override { return "! (never type)"; }
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+};
+
+// A type consisting of a pointer without safety or liveness guarantees
+class RawPointerType : public TypeNoBounds
+{
+public:
+  enum PointerType
+  {
+    MUT,
+    CONST
+  };
+
+private:
+  PointerType pointer_type;
+  std::unique_ptr<TypeNoBounds> type;
+  Location locus;
+
+public:
+  // Returns whether the pointer is mutable or constant.
+  PointerType get_pointer_type () const { return pointer_type; }
+
+  // Constructor requires pointer for polymorphism reasons
+  RawPointerType (PointerType pointer_type,
+		  std::unique_ptr<TypeNoBounds> type_no_bounds, Location locus)
+    : pointer_type (pointer_type), type (std::move (type_no_bounds)),
+      locus (locus)
+  {}
+
+  // Copy constructor calls custom polymorphic clone function
+  RawPointerType (RawPointerType const &other)
+    : pointer_type (other.pointer_type),
+      type (other.type->clone_type_no_bounds ()), locus (other.locus)
+  {}
+
+  // overload assignment operator to use custom clone method
+  RawPointerType &operator= (RawPointerType const &other)
+  {
+    pointer_type = other.pointer_type;
+    type = other.type->clone_type_no_bounds ();
+    locus = other.locus;
+    return *this;
+  }
+
+  // default move semantics
+  RawPointerType (RawPointerType &&other) = default;
+  RawPointerType &operator= (RawPointerType &&other) = default;
+
+  std::string as_string () const override;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: would a "vis_type" be better?
+  std::unique_ptr<TypeNoBounds> &get_type_pointed_to ()
+  {
+    rust_assert (type != nullptr);
+    return type;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RawPointerType *clone_type_no_bounds_impl () const override
+  {
+    return new RawPointerType (*this);
+  }
+};
+
+// A type pointing to memory owned by another value
+class ReferenceType : public TypeNoBounds
+{
+  // bool has_lifetime; // TODO: handle in lifetime or something?
+  Lifetime lifetime;
+
+  bool has_mut;
+  std::unique_ptr<TypeNoBounds> type;
+  Location locus;
+
+public:
+  // Returns whether the reference is mutable or immutable.
+  bool is_mut () const { return has_mut; }
+
+  // Returns whether the reference has a lifetime.
+  bool has_lifetime () const { return !lifetime.is_error (); }
+
+  // Constructor
+  ReferenceType (bool is_mut, std::unique_ptr<TypeNoBounds> type_no_bounds,
+		 Location locus, Lifetime lifetime = Lifetime::error ())
+    : lifetime (std::move (lifetime)), has_mut (is_mut),
+      type (std::move (type_no_bounds)), locus (locus)
+  {}
+
+  // Copy constructor with custom clone method
+  ReferenceType (ReferenceType const &other)
+    : lifetime (other.lifetime), has_mut (other.has_mut),
+      type (other.type->clone_type_no_bounds ()), locus (other.locus)
+  {}
+
+  // Operator overload assignment operator to custom clone the unique pointer
+  ReferenceType &operator= (ReferenceType const &other)
+  {
+    lifetime = other.lifetime;
+    has_mut = other.has_mut;
+    type = other.type->clone_type_no_bounds ();
+    locus = other.locus;
+
+    return *this;
+  }
+
+  // move constructors
+  ReferenceType (ReferenceType &&other) = default;
+  ReferenceType &operator= (ReferenceType &&other) = default;
+
+  std::string as_string () const override;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: would a "vis_type" be better?
+  std::unique_ptr<TypeNoBounds> &get_type_referenced ()
+  {
+    rust_assert (type != nullptr);
+    return type;
+  }
+
+  bool get_has_mut () const { return has_mut; }
+
+  Lifetime &get_lifetime () { return lifetime; }
+
+  std::unique_ptr<TypeNoBounds> &get_base_type () { return type; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ReferenceType *clone_type_no_bounds_impl () const override
+  {
+    return new ReferenceType (*this);
+  }
+};
+
+// A fixed-size sequence of elements of a specified type
+class ArrayType : public TypeNoBounds
+{
+  std::unique_ptr<Type> elem_type;
+  std::unique_ptr<Expr> size;
+  Location locus;
+
+public:
+  // Constructor requires pointers for polymorphism
+  ArrayType (std::unique_ptr<Type> type, std::unique_ptr<Expr> array_size,
+	     Location locus)
+    : elem_type (std::move (type)), size (std::move (array_size)), locus (locus)
+  {}
+
+  // Copy constructor requires deep copies of both unique pointers
+  ArrayType (ArrayType const &other)
+    : elem_type (other.elem_type->clone_type ()),
+      size (other.size->clone_expr ()), locus (other.locus)
+  {}
+
+  // Overload assignment operator to deep copy pointers
+  ArrayType &operator= (ArrayType const &other)
+  {
+    elem_type = other.elem_type->clone_type ();
+    size = other.size->clone_expr ();
+    locus = other.locus;
+    return *this;
+  }
+
+  // move constructors
+  ArrayType (ArrayType &&other) = default;
+  ArrayType &operator= (ArrayType &&other) = default;
+
+  std::string as_string () const override;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: would a "vis_type" be better?
+  std::unique_ptr<Type> &get_elem_type ()
+  {
+    rust_assert (elem_type != nullptr);
+    return elem_type;
+  }
+
+  // TODO: would a "vis_expr" be better?
+  std::unique_ptr<Expr> &get_size_expr ()
+  {
+    rust_assert (size != nullptr);
+    return size;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ArrayType *clone_type_no_bounds_impl () const override
+  {
+    return new ArrayType (*this);
+  }
+};
+
+/* A dynamically-sized type representing a "view" into a sequence of elements of
+ * a type */
+class SliceType : public TypeNoBounds
+{
+  std::unique_ptr<Type> elem_type;
+  Location locus;
+
+public:
+  // Constructor requires pointer for polymorphism
+  SliceType (std::unique_ptr<Type> type, Location locus)
+    : elem_type (std::move (type)), locus (locus)
+  {}
+
+  // Copy constructor requires deep copy of Type smart pointer
+  SliceType (SliceType const &other)
+    : elem_type (other.elem_type->clone_type ()), locus (other.locus)
+  {}
+
+  // Overload assignment operator to deep copy
+  SliceType &operator= (SliceType const &other)
+  {
+    elem_type = other.elem_type->clone_type ();
+    locus = other.locus;
+
+    return *this;
+  }
+
+  // move constructors
+  SliceType (SliceType &&other) = default;
+  SliceType &operator= (SliceType &&other) = default;
+
+  std::string as_string () const override;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: would a "vis_type" be better?
+  std::unique_ptr<Type> &get_elem_type ()
+  {
+    rust_assert (elem_type != nullptr);
+    return elem_type;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  SliceType *clone_type_no_bounds_impl () const override
+  {
+    return new SliceType (*this);
+  }
+};
+
+/* Type used in generic arguments to explicitly request type inference (wildcard
+ * pattern) */
+class InferredType : public TypeNoBounds
+{
+  Location locus;
+
+  // e.g. Vec<_> = whatever
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  InferredType *clone_type_no_bounds_impl () const override
+  {
+    return new InferredType (*this);
+  }
+
+public:
+  InferredType (Location locus) : locus (locus) {}
+
+  std::string as_string () const override;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+};
+
+class QualifiedPathInType; // definition moved to "rust-path.h"
+
+// A possibly named param used in a BaseFunctionType
+struct MaybeNamedParam
+{
+public:
+  enum ParamKind
+  {
+    UNNAMED,
+    IDENTIFIER,
+    WILDCARD
+  };
+
+private:
+  std::vector<Attribute> outer_attrs;
+
+  std::unique_ptr<Type> param_type;
+
+  ParamKind param_kind;
+  Identifier name; // technically, can be an identifier or '_'
+
+  Location locus;
+
+public:
+  MaybeNamedParam (Identifier name, ParamKind param_kind,
+		   std::unique_ptr<Type> param_type,
+		   std::vector<Attribute> outer_attrs, Location locus)
+    : outer_attrs (std::move (outer_attrs)),
+      param_type (std::move (param_type)), param_kind (param_kind),
+      name (std::move (name)), locus (locus)
+  {}
+
+  // Copy constructor with clone
+  MaybeNamedParam (MaybeNamedParam const &other)
+    : outer_attrs (other.outer_attrs), param_kind (other.param_kind),
+      name (other.name), locus (other.locus)
+  {
+    // guard to prevent null dereference
+    if (other.param_type != nullptr)
+      param_type = other.param_type->clone_type ();
+  }
+
+  ~MaybeNamedParam () = default;
+
+  // Overloaded assignment operator with clone
+  MaybeNamedParam &operator= (MaybeNamedParam const &other)
+  {
+    outer_attrs = other.outer_attrs;
+    name = other.name;
+    param_kind = other.param_kind;
+    locus = other.locus;
+
+    // guard to prevent null dereference
+    if (other.param_type != nullptr)
+      param_type = other.param_type->clone_type ();
+    else
+      param_type = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  MaybeNamedParam (MaybeNamedParam &&other) = default;
+  MaybeNamedParam &operator= (MaybeNamedParam &&other) = default;
+
+  std::string as_string () const;
+
+  // Returns whether the param is in an error state.
+  bool is_error () const { return param_type == nullptr; }
+
+  // Creates an error state param.
+  static MaybeNamedParam create_error ()
+  {
+    return MaybeNamedParam ("", UNNAMED, nullptr, {}, Location ());
+  }
+
+  Location get_locus () const { return locus; }
+
+  // TODO: this mutable getter seems really dodgy. Think up better way.
+  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
+  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }
+
+  // TODO: would a "vis_type" be better?
+  std::unique_ptr<Type> &get_type ()
+  {
+    rust_assert (param_type != nullptr);
+    return param_type;
+  }
+
+  ParamKind get_param_kind () const { return param_kind; }
+
+  Identifier get_name () const { return name; }
+};
+
+/* A function pointer type - can be created via coercion from function items and
+ * non- capturing closures. */
+class BareFunctionType : public TypeNoBounds
+{
+  // bool has_for_lifetimes;
+  // ForLifetimes for_lifetimes;
+  std::vector<LifetimeParam> for_lifetimes; // inlined version
+
+  FunctionQualifiers function_qualifiers;
+  std::vector<MaybeNamedParam> params;
+  bool is_variadic;
+  std::vector<Attribute> variadic_attrs;
+
+  // bool has_return_type;
+  // BareFunctionReturnType return_type;
+  std::unique_ptr<TypeNoBounds> return_type; // inlined version
+
+  Location locus;
+
+public:
+  // Whether a return type is defined with the function.
+  bool has_return_type () const { return return_type != nullptr; }
+
+  // Whether the function has ForLifetimes.
+  bool has_for_lifetimes () const { return !for_lifetimes.empty (); }
+
+  BareFunctionType (std::vector<LifetimeParam> lifetime_params,
+		    FunctionQualifiers qualifiers,
+		    std::vector<MaybeNamedParam> named_params, bool is_variadic,
+		    std::vector<Attribute> variadic_attrs,
+		    std::unique_ptr<TypeNoBounds> type, Location locus)
+    : for_lifetimes (std::move (lifetime_params)),
+      function_qualifiers (std::move (qualifiers)),
+      params (std::move (named_params)), is_variadic (is_variadic),
+      variadic_attrs (std::move (variadic_attrs)),
+      return_type (std::move (type)), locus (locus)
+  {
+    if (!variadic_attrs.empty ())
+      is_variadic = true;
+  }
+
+  // Copy constructor with clone
+  BareFunctionType (BareFunctionType const &other)
+    : for_lifetimes (other.for_lifetimes),
+      function_qualifiers (other.function_qualifiers), params (other.params),
+      is_variadic (other.is_variadic), variadic_attrs (other.variadic_attrs),
+      locus (other.locus)
+  {
+    // guard to prevent null dereference
+    if (other.return_type != nullptr)
+      return_type = other.return_type->clone_type_no_bounds ();
+  }
+
+  // Overload assignment operator to deep copy
+  BareFunctionType &operator= (BareFunctionType const &other)
+  {
+    for_lifetimes = other.for_lifetimes;
+    function_qualifiers = other.function_qualifiers;
+    params = other.params;
+    is_variadic = other.is_variadic;
+    variadic_attrs = other.variadic_attrs;
+    locus = other.locus;
+
+    // guard to prevent null dereference
+    if (other.return_type != nullptr)
+      return_type = other.return_type->clone_type_no_bounds ();
+    else
+      return_type = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  BareFunctionType (BareFunctionType &&other) = default;
+  BareFunctionType &operator= (BareFunctionType &&other) = default;
+
+  std::string as_string () const override;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (ASTVisitor &vis) override;
+
+  // TODO: this mutable getter seems kinda dodgy
+  std::vector<MaybeNamedParam> &get_function_params () { return params; }
+  const std::vector<MaybeNamedParam> &get_function_params () const
+  {
+    return params;
+  }
+
+  // TODO: would a "vis_type" be better?
+  std::unique_ptr<TypeNoBounds> &get_return_type ()
+  {
+    rust_assert (has_return_type ());
+    return return_type;
+  }
+
+  FunctionQualifiers get_function_qualifiers () { return function_qualifiers; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  BareFunctionType *clone_type_no_bounds_impl () const override
+  {
+    return new BareFunctionType (*this);
+  }
+};
+
+// Forward decl - defined in rust-macro.h
+class MacroInvocation;
+
+/* TODO: possible types
+ * struct type?
+ * "enum" (tagged union) type?
+ * C-like union type?
+ * function item type?
+ * closure expression types?
+ * primitive types (bool, int, float, char, str (the slice))
+ * Although supposedly TypePaths are used to reference these types (including
+ * primitives) */
+
+/* FIXME: Incomplete spec references:
+ *  anonymous type parameters, aka "impl Trait in argument position" - impl then
+ * trait bounds abstract return types, aka "impl Trait in return position" -
+ * impl then trait bounds */
+} // namespace AST
+} // namespace Rust
+
+#endif
diff --git a/gcc/rust/operator.h b/gcc/rust/operator.h
new file mode 100644
index 00000000000..6813db3ed13
--- /dev/null
+++ b/gcc/rust/operator.h
@@ -0,0 +1,72 @@
+// 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_OPERATOR_H
+#define RUST_OPERATOR_H
+
+enum class NegationOperator
+{
+  NEGATE,
+  NOT
+};
+
+enum class ArithmeticOrLogicalOperator
+{
+  ADD,	       // std::ops::Add
+  SUBTRACT,    // std::ops::Sub
+  MULTIPLY,    // std::ops::Mul
+  DIVIDE,      // std::ops::Div
+  MODULUS,     // std::ops::Rem
+  BITWISE_AND, // std::ops::BitAnd
+  BITWISE_OR,  // std::ops::BitOr
+  BITWISE_XOR, // std::ops::BitXor
+  LEFT_SHIFT,  // std::ops::Shl
+  RIGHT_SHIFT  // std::ops::Shr
+};
+
+enum class ComparisonOperator
+{
+  EQUAL,	    // std::cmp::PartialEq::eq
+  NOT_EQUAL,	    // std::cmp::PartialEq::ne
+  GREATER_THAN,	    // std::cmp::PartialEq::gt
+  LESS_THAN,	    // std::cmp::PartialEq::lt
+  GREATER_OR_EQUAL, // std::cmp::PartialEq::ge
+  LESS_OR_EQUAL	    // std::cmp::PartialEq::le
+};
+
+enum class LazyBooleanOperator
+{
+  LOGICAL_OR,
+  LOGICAL_AND
+};
+
+enum class CompoundAssignmentOperator
+{
+  ADD,	       // std::ops::AddAssign
+  SUBTRACT,    // std::ops::SubAssign
+  MULTIPLY,    // std::ops::MulAssign
+  DIVIDE,      // std::ops::DivAssign
+  MODULUS,     // std::ops::RemAssign
+  BITWISE_AND, // std::ops::BitAndAssign
+  BITWISE_OR,  // std::ops::BitOrAssign
+  BITWISE_XOR, // std::ops::BitXorAssign
+  LEFT_SHIFT,  // std::ops::ShlAssign
+  RIGHT_SHIFT  // std::ops::ShrAssign
+};
+
+#endif // RUST_OPERATOR_H
-- 
2.25.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [PATCH Rust front-end v2 09/37] gccrs: Add Lexer for Rust front-end
  2022-08-24 11:59 Rust frontend patches v2 herron.philip
                   ` (7 preceding siblings ...)
  2022-08-24 11:59 ` [PATCH Rust front-end v2 08/37] gccrs: Add the Rust front-end AST data structures herron.philip
@ 2022-08-24 11:59 ` herron.philip
  2022-09-14 13:30   ` Richard Biener
  2022-08-24 11:59 ` [PATCH Rust front-end v2 10/37] gccrs: Add Parser " herron.philip
                   ` (28 subsequent siblings)
  37 siblings, 1 reply; 59+ messages in thread
From: herron.philip @ 2022-08-24 11:59 UTC (permalink / raw)
  To: gcc-patches
  Cc: gcc-rust, The Other, Philip Herron, Arthur Cohen, Mark Wielaard

From: The Other <simplytheother@gmail.com>

The lexer is refered to as a ManagedTokenSource within the parser, this
lexer does not currently support unicode but serves as a starting point
to do so.

Co-authored-by: Philip Herron <philip.herron@embecosm.com>
Co-authored-by: Arthur Cohen <arthur.cohen@embecosm.com>
Co-authored-by: Mark Wielaard <mark@klomp.org>
---
 gcc/rust/lex/rust-codepoint.h  |   46 +
 gcc/rust/lex/rust-lex.cc       | 2729 ++++++++++++++++++++++++++++++++
 gcc/rust/lex/rust-lex.h        |  271 ++++
 gcc/rust/lex/rust-token.cc     |  135 ++
 gcc/rust/lex/rust-token.h      |  455 ++++++
 gcc/rust/rust-buffered-queue.h |  204 +++
 6 files changed, 3840 insertions(+)
 create mode 100644 gcc/rust/lex/rust-codepoint.h
 create mode 100644 gcc/rust/lex/rust-lex.cc
 create mode 100644 gcc/rust/lex/rust-lex.h
 create mode 100644 gcc/rust/lex/rust-token.cc
 create mode 100644 gcc/rust/lex/rust-token.h
 create mode 100644 gcc/rust/rust-buffered-queue.h

diff --git a/gcc/rust/lex/rust-codepoint.h b/gcc/rust/lex/rust-codepoint.h
new file mode 100644
index 00000000000..22da080bbb2
--- /dev/null
+++ b/gcc/rust/lex/rust-codepoint.h
@@ -0,0 +1,46 @@
+// 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_CODEPOINT_H
+#define RUST_CODEPOINT_H
+
+#include <string>
+
+namespace Rust {
+struct Codepoint
+{
+  uint32_t value;
+
+  // Creates a zero codepoint.
+  Codepoint () : value (0) {}
+
+  // Creates a codepoint from an encoded UTF-8 value.
+  Codepoint (uint32_t value) : value (value) {}
+
+  static Codepoint eof () { return Codepoint (UINT32_MAX); }
+  bool is_eof () const { return value == UINT32_MAX; }
+
+  // Returns a C++ string containing string value of codepoint.
+  std::string as_string ();
+
+  bool operator== (Codepoint other) const { return value == other.value; }
+  bool operator!= (Codepoint other) const { return !operator== (other); }
+};
+} // namespace Rust
+
+#endif
diff --git a/gcc/rust/lex/rust-lex.cc b/gcc/rust/lex/rust-lex.cc
new file mode 100644
index 00000000000..70e6b50209f
--- /dev/null
+++ b/gcc/rust/lex/rust-lex.cc
@@ -0,0 +1,2729 @@
+// 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/>.
+
+#include "rust-lex.h"
+
+#include "rust-system.h"      // for rust_assert and rust_unreachable
+#include "rust-diagnostics.h" // for rust_error_at
+#include "rust-linemap.h"
+#include "rust-session-manager.h"
+#include "safe-ctype.h"
+
+namespace Rust {
+// TODO: move to separate compilation unit?
+// overload += for uint32_t to allow 32-bit encoded utf-8 to be added
+std::string &
+operator+= (std::string &str, Codepoint char32)
+{
+  if (char32.value < 0x80)
+    {
+      str += static_cast<char> (char32.value);
+    }
+  else if (char32.value < (0x1F + 1) << (1 * 6))
+    {
+      str += static_cast<char> (0xC0 | ((char32.value >> 6) & 0x1F));
+      str += static_cast<char> (0x80 | ((char32.value >> 0) & 0x3F));
+    }
+  else if (char32.value < (0x0F + 1) << (2 * 6))
+    {
+      str += static_cast<char> (0xE0 | ((char32.value >> 12) & 0x0F));
+      str += static_cast<char> (0x80 | ((char32.value >> 6) & 0x3F));
+      str += static_cast<char> (0x80 | ((char32.value >> 0) & 0x3F));
+    }
+  else if (char32.value < (0x07 + 1) << (3 * 6))
+    {
+      str += static_cast<char> (0xF0 | ((char32.value >> 18) & 0x07));
+      str += static_cast<char> (0x80 | ((char32.value >> 12) & 0x3F));
+      str += static_cast<char> (0x80 | ((char32.value >> 6) & 0x3F));
+      str += static_cast<char> (0x80 | ((char32.value >> 0) & 0x3F));
+    }
+  else
+    {
+      rust_debug ("Invalid unicode codepoint found: '%u' ", char32.value);
+    }
+  return str;
+}
+
+std::string
+Codepoint::as_string ()
+{
+  std::string str;
+
+  // str += Codepoint (value);
+  str += *this;
+
+  return str;
+}
+
+/* Includes all allowable float digits EXCEPT _ and . as that needs lookahead
+ * for handling. */
+bool
+is_float_digit (char number)
+{
+  return ISDIGIT (number) || number == 'E' || number == 'e';
+}
+
+/* Basically ISXDIGIT from safe-ctype but may change if Rust's encoding or
+ * whatever is different */
+bool
+is_x_digit (char number)
+{
+  return ISXDIGIT (number);
+}
+
+bool
+is_octal_digit (char number)
+{
+  return number >= '0' && number <= '7';
+}
+
+bool
+is_bin_digit (char number)
+{
+  return number == '0' || number == '1';
+}
+
+bool
+check_valid_float_dot_end (char character)
+{
+  return character != '.' && character != '_' && !ISALPHA (character);
+}
+
+// ISSPACE from safe-ctype but may change in future
+bool
+is_whitespace (char character)
+{
+  return ISSPACE (character);
+}
+
+bool
+is_non_decimal_int_literal_separator (char character)
+{
+  return character == 'x' || character == 'o' || character == 'b';
+}
+
+Lexer::Lexer (const std::string &input)
+  : input (RAIIFile::create_error ()), current_line (1), current_column (1),
+    line_map (nullptr), raw_input_source (new BufferInputSource (input, 0)),
+    input_queue{*raw_input_source}, token_queue (TokenSource (this))
+{}
+
+Lexer::Lexer (const char *filename, RAIIFile file_input, Linemap *linemap)
+  : input (std::move (file_input)), current_line (1), current_column (1),
+    line_map (linemap),
+    raw_input_source (new FileInputSource (input.get_raw ())),
+    input_queue{*raw_input_source}, token_queue (TokenSource (this))
+{
+  // inform line_table that file is being entered and is in line 1
+  if (linemap)
+    line_map->start_file (filename, current_line);
+}
+
+Lexer::~Lexer ()
+{
+  /* ok apparently stop (which is equivalent of original code in destructor) is
+   * meant to be called after all files have finished parsing, for cleanup. On
+   * the other hand, actual code that it calls to leave a certain line map is
+   * mentioned in GCC docs as being useful for "just leaving an included header"
+   * and stuff like that, so this line mapping functionality may need fixing.
+   * FIXME: find out whether this occurs. */
+
+  // line_map->stop();
+}
+
+/* TODO: need to optimise somehow to avoid the virtual function call in the
+ * tight loop. Best idea at the moment is CRTP, but that might make lexer
+ * implementation annoying when storing the "base class" (i.e. would need
+ * template parameter everywhere), although in practice it would mostly just
+ * look ugly and make enclosing classes like Parser also require a type
+ * parameter. At this point a macro might be better. OK I guess macros can be
+ * replaced by constexpr if or something if possible. */
+Location
+Lexer::get_current_location ()
+{
+  if (line_map)
+    return line_map->get_location (current_column);
+  else
+    // If we have no linemap, we're lexing something without proper locations
+    return Location ();
+}
+
+int
+Lexer::peek_input (int n)
+{
+  return input_queue.peek (n);
+}
+
+int
+Lexer::peek_input ()
+{
+  return peek_input (0);
+}
+
+void
+Lexer::skip_input (int n)
+{
+  input_queue.skip (n);
+}
+
+void
+Lexer::skip_input ()
+{
+  skip_input (0);
+}
+
+void
+Lexer::replace_current_token (TokenPtr replacement)
+{
+  token_queue.replace_current_value (replacement);
+
+  rust_debug ("called 'replace_current_token' - this is deprecated");
+}
+
+/* shitty anonymous namespace that can only be accessed inside the compilation
+ * unit - used for classify_keyword binary search in sorted array of keywords
+ * created with x-macros. */
+namespace {
+// TODO: make constexpr when update to c++20
+const std::string keyword_index[] = {
+#define RS_TOKEN(x, y)
+#define RS_TOKEN_KEYWORD(name, keyword) keyword,
+  RS_TOKEN_LIST
+#undef RS_TOKEN_KEYWORD
+#undef RS_TOKEN
+};
+
+constexpr TokenId keyword_keys[] = {
+#define RS_TOKEN(x, y)
+#define RS_TOKEN_KEYWORD(name, keyword) name,
+  RS_TOKEN_LIST
+#undef RS_TOKEN_KEYWORD
+#undef RS_TOKEN
+};
+
+constexpr int num_keywords = sizeof (keyword_index) / sizeof (*keyword_index);
+} // namespace
+
+/* Determines whether the string passed in is a keyword or not. If it is, it
+ * returns the keyword name.  */
+TokenId
+Lexer::classify_keyword (const std::string &str)
+{
+  const std::string *last = keyword_index + num_keywords;
+  const std::string *idx = std::lower_bound (keyword_index, last, str);
+
+  if (idx == last || str != *idx)
+    return IDENTIFIER;
+
+  // TODO: possibly replace this x-macro system with something like hash map?
+
+  // We now have the expected token ID of the reserved keyword. However, some
+  // keywords are reserved starting in certain editions. For example, `try` is
+  // only a reserved keyword in editions >=2018. The language might gain new
+  // reserved keywords in the future.
+  //
+  // https://doc.rust-lang.org/reference/keywords.html#reserved-keywords
+  auto id = keyword_keys[idx - keyword_index];
+
+  // `try` is not a reserved keyword before 2018
+  if (Session::get_instance ().options.get_edition ()
+	== CompileOptions::Edition::E2015
+      && id == TRY)
+    return IDENTIFIER;
+
+  return id;
+}
+
+TokenPtr
+Lexer::build_token ()
+{
+  // loop to go through multiple characters to build a single token
+  while (true)
+    {
+      Location loc = get_current_location ();
+      current_char = peek_input ();
+      skip_input ();
+
+      // detect UTF8 bom
+      //
+      // Must be the first thing on the first line.
+      // There might be an optional BOM (Byte Order Mark), which for UTF-8 is
+      // the three bytes 0xEF, 0xBB and 0xBF. These can simply be skipped.
+      if (current_line == 1 && current_column == 1 && current_char == 0xef
+	  && peek_input () == 0xbb && peek_input (1) == 0xbf)
+	{
+	  skip_input (1);
+	  current_char = peek_input ();
+	  skip_input ();
+	}
+
+      // detect shebang
+      // Must be the first thing on the first line, starting with #!
+      // But since an attribute can also start with an #! we don't count it as a
+      // shebang line when after any whitespace or comments there is a [. If it
+      // is a shebang line we simple drop the line. Otherwise we don't consume
+      // any characters and fall through to the real tokenizer.
+      if (current_line == 1 && current_column == 1 && current_char == '#'
+	  && peek_input () == '!')
+	{
+	  int n = 1;
+	  while (true)
+	    {
+	      int next_char = peek_input (n);
+	      if (is_whitespace (next_char))
+		n++;
+	      else if ((next_char == '/' && peek_input (n + 1) == '/'
+			&& peek_input (n + 2) != '!'
+			&& peek_input (n + 2) != '/')
+		       || (next_char == '/' && peek_input (n + 1) == '/'
+			   && peek_input (n + 2) == '/'
+			   && peek_input (n + 3) == '/'))
+		{
+		  // two // or four ////
+		  // A single line comment
+		  // (but not an inner or outer doc comment)
+		  n += 2;
+		  next_char = peek_input (n);
+		  while (next_char != '\n' && next_char != EOF)
+		    {
+		      n++;
+		      next_char = peek_input (n);
+		    }
+		  if (next_char == '\n')
+		    n++;
+		}
+	      else if (next_char == '/' && peek_input (n + 1) == '*'
+		       && peek_input (n + 2) == '*'
+		       && peek_input (n + 3) == '/')
+		{
+		  /**/
+		  n += 4;
+		}
+	      else if (next_char == '/' && peek_input (n + 1) == '*'
+		       && peek_input (n + 2) == '*' && peek_input (n + 3) == '*'
+		       && peek_input (n + 4) == '/')
+		{
+		  /***/
+		  n += 5;
+		}
+	      else if ((next_char == '/' && peek_input (n + 1) == '*'
+			&& peek_input (n + 2) != '*'
+			&& peek_input (n + 2) != '!')
+		       || (next_char == '/' && peek_input (n + 1) == '*'
+			   && peek_input (n + 2) == '*'
+			   && peek_input (n + 3) == '*'))
+		{
+		  // one /* or three /***
+		  // Start of a block comment
+		  // (but not an inner or outer doc comment)
+		  n += 2;
+		  int level = 1;
+		  while (level > 0)
+		    {
+		      if (peek_input (n) == EOF)
+			break;
+		      else if (peek_input (n) == '/'
+			       && peek_input (n + 1) == '*')
+			{
+			  n += 2;
+			  level += 1;
+			}
+		      else if (peek_input (n) == '*'
+			       && peek_input (n + 1) == '/')
+			{
+			  n += 2;
+			  level -= 1;
+			}
+		      else
+			n++;
+		    }
+		}
+	      else if (next_char != '[')
+		{
+		  // definitely shebang, ignore the first line
+		  while (current_char != '\n' && current_char != EOF)
+		    {
+		      current_char = peek_input ();
+		      skip_input ();
+		    }
+
+		  // newline
+		  current_line++;
+		  current_column = 1;
+		  // tell line_table that new line starts
+		  start_line (current_line, max_column_hint);
+		  break;
+		}
+	      else
+		break; /* Definitely not a shebang line. */
+	    }
+	}
+
+      // return end of file token if end of file
+      if (current_char == EOF)
+	return Token::make (END_OF_FILE, loc);
+
+      // if not end of file, start tokenising
+      switch (current_char)
+	{
+	/* ignore whitespace characters for tokens but continue updating
+	 * location */
+	case '\n': // newline
+	  current_line++;
+	  current_column = 1;
+	  // tell line_table that new line starts
+	  start_line (current_line, max_column_hint);
+	  continue;
+	case '\r': // cr
+	  // Ignore, we expect a newline (lf) soon.
+	  continue;
+	case ' ': // space
+	  current_column++;
+	  continue;
+	case '\t': // tab
+	  // width of a tab is not well-defined, assume 8 spaces
+	  current_column += 8;
+	  continue;
+
+	// punctuation - actual tokens
+	case '=':
+	  if (peek_input () == '>')
+	    {
+	      // match arm arrow
+	      skip_input ();
+	      current_column += 2;
+
+	      return Token::make (MATCH_ARROW, loc);
+	    }
+	  else if (peek_input () == '=')
+	    {
+	      // equality operator
+	      skip_input ();
+	      current_column += 2;
+
+	      return Token::make (EQUAL_EQUAL, loc);
+	    }
+	  else
+	    {
+	      // assignment operator
+	      current_column++;
+	      return Token::make (EQUAL, loc);
+	    }
+	case '(':
+	  current_column++;
+	  return Token::make (LEFT_PAREN, loc);
+	case '-':
+	  if (peek_input () == '>')
+	    {
+	      // return type specifier
+	      skip_input ();
+	      current_column += 2;
+
+	      return Token::make (RETURN_TYPE, loc);
+	    }
+	  else if (peek_input () == '=')
+	    {
+	      // minus-assign
+	      skip_input ();
+	      current_column += 2;
+
+	      return Token::make (MINUS_EQ, loc);
+	    }
+	  else
+	    {
+	      // minus
+	      current_column++;
+	      return Token::make (MINUS, loc);
+	    }
+	case '+':
+	  if (peek_input () == '=')
+	    {
+	      // add-assign
+	      skip_input ();
+	      current_column += 2;
+
+	      return Token::make (PLUS_EQ, loc);
+	    }
+	  else
+	    {
+	      // add
+	      current_column++;
+	      return Token::make (PLUS, loc);
+	    }
+	case ')':
+	  current_column++;
+	  return Token::make (RIGHT_PAREN, loc);
+	case ';':
+	  current_column++;
+	  return Token::make (SEMICOLON, loc);
+	case '*':
+	  if (peek_input () == '=')
+	    {
+	      // multiplication-assign
+	      skip_input ();
+	      current_column += 2;
+
+	      return Token::make (ASTERISK_EQ, loc);
+	    }
+	  else
+	    {
+	      // multiplication
+	      current_column++;
+	      return Token::make (ASTERISK, loc);
+	    }
+	case ',':
+	  current_column++;
+	  return Token::make (COMMA, loc);
+	case '/':
+	  if (peek_input () == '=')
+	    {
+	      // division-assign
+	      skip_input ();
+	      current_column += 2;
+
+	      return Token::make (DIV_EQ, loc);
+	    }
+	  else if ((peek_input () == '/' && peek_input (1) != '!'
+		    && peek_input (1) != '/')
+		   || (peek_input () == '/' && peek_input (1) == '/'
+		       && peek_input (2) == '/'))
+	    {
+	      // two // or four ////
+	      // single line comment
+	      // (but not an inner or outer doc comment)
+	      skip_input ();
+	      current_column += 2;
+	      current_char = peek_input ();
+
+	      // basically ignore until line finishes
+	      while (current_char != '\n' && current_char != EOF)
+		{
+		  skip_input ();
+		  current_column++; // not used
+		  current_char = peek_input ();
+		}
+	      continue;
+	    }
+	  else if (peek_input () == '/'
+		   && (peek_input (1) == '!' || peek_input (1) == '/'))
+	    {
+	      /* single line doc comment, inner or outer.  */
+	      bool is_inner = peek_input (1) == '!';
+	      skip_input (1);
+	      current_column += 3;
+
+	      std::string str;
+	      str.reserve (32);
+	      current_char = peek_input ();
+	      while (current_char != '\n')
+		{
+		  skip_input ();
+		  if (current_char == '\r')
+		    {
+		      char next_char = peek_input ();
+		      if (next_char == '\n')
+			{
+			  current_char = '\n';
+			  break;
+			}
+		      rust_error_at (
+			loc, "Isolated CR %<\\r%> not allowed in doc comment");
+		      current_char = next_char;
+		      continue;
+		    }
+		  if (current_char == EOF)
+		    {
+		      rust_error_at (
+			loc, "unexpected EOF while looking for end of comment");
+		      break;
+		    }
+		  str += current_char;
+		  current_char = peek_input ();
+		}
+	      skip_input ();
+	      current_line++;
+	      current_column = 1;
+	      // tell line_table that new line starts
+	      start_line (current_line, max_column_hint);
+
+	      str.shrink_to_fit ();
+	      if (is_inner)
+		return Token::make_inner_doc_comment (loc, std::move (str));
+	      else
+		return Token::make_outer_doc_comment (loc, std::move (str));
+	    }
+	  else if (peek_input () == '*' && peek_input (1) == '*'
+		   && peek_input (2) == '/')
+	    {
+	      /**/
+	      skip_input (2);
+	      current_column += 4;
+	      continue;
+	    }
+	  else if (peek_input () == '*' && peek_input (1) == '*'
+		   && peek_input (2) == '*' && peek_input (3) == '/')
+	    {
+	      /***/
+	      skip_input (3);
+	      current_column += 5;
+	      continue;
+	    }
+	  else if ((peek_input () == '*' && peek_input (1) != '!'
+		    && peek_input (1) != '*')
+		   || (peek_input () == '*' && peek_input (1) == '*'
+		       && peek_input (2) == '*'))
+	    {
+	      // one /* or three /***
+	      // block comment
+	      // (but not an inner or outer doc comment)
+	      skip_input ();
+	      current_column += 2;
+
+	      int level = 1;
+	      while (level > 0)
+		{
+		  current_char = peek_input ();
+
+		  if (current_char == EOF)
+		    {
+		      rust_error_at (
+			loc, "unexpected EOF while looking for end of comment");
+		      break;
+		    }
+
+		  // if /* found
+		  if (current_char == '/' && peek_input (1) == '*')
+		    {
+		      // skip /* characters
+		      skip_input (1);
+
+		      current_column += 2;
+
+		      level += 1;
+		      continue;
+		    }
+
+		  // ignore until */ is found
+		  if (current_char == '*' && peek_input (1) == '/')
+		    {
+		      // skip */ characters
+		      skip_input (1);
+
+		      current_column += 2;
+
+		      level -= 1;
+		      continue;
+		    }
+
+		  if (current_char == '\n')
+		    {
+		      skip_input ();
+		      current_line++;
+		      current_column = 1;
+		      // tell line_table that new line starts
+		      start_line (current_line, max_column_hint);
+		      continue;
+		    }
+
+		  skip_input ();
+		  current_column++;
+		}
+
+	      // refresh new token
+	      continue;
+	    }
+	  else if (peek_input () == '*'
+		   && (peek_input (1) == '!' || peek_input (1) == '*'))
+	    {
+	      // block doc comment, inner /*! or outer /**
+	      bool is_inner = peek_input (1) == '!';
+	      skip_input (1);
+	      current_column += 3;
+
+	      std::string str;
+	      str.reserve (96);
+
+	      int level = 1;
+	      while (level > 0)
+		{
+		  current_char = peek_input ();
+
+		  if (current_char == EOF)
+		    {
+		      rust_error_at (
+			loc, "unexpected EOF while looking for end of comment");
+		      break;
+		    }
+
+		  // if /* found
+		  if (current_char == '/' && peek_input (1) == '*')
+		    {
+		      // skip /* characters
+		      skip_input (1);
+		      current_column += 2;
+
+		      level += 1;
+		      str += "/*";
+		      continue;
+		    }
+
+		  // ignore until */ is found
+		  if (current_char == '*' && peek_input (1) == '/')
+		    {
+		      // skip */ characters
+		      skip_input (1);
+		      current_column += 2;
+
+		      level -= 1;
+		      if (level > 0)
+			str += "*/";
+		      continue;
+		    }
+
+		  if (current_char == '\r' && peek_input (1) != '\n')
+		    rust_error_at (
+		      loc, "Isolated CR %<\\r%> not allowed in doc comment");
+
+		  if (current_char == '\n')
+		    {
+		      skip_input ();
+		      current_line++;
+		      current_column = 1;
+		      // tell line_table that new line starts
+		      start_line (current_line, max_column_hint);
+		      str += '\n';
+		      continue;
+		    }
+
+		  str += current_char;
+		  skip_input ();
+		  current_column++;
+		}
+
+	      str.shrink_to_fit ();
+	      if (is_inner)
+		return Token::make_inner_doc_comment (loc, std::move (str));
+	      else
+		return Token::make_outer_doc_comment (loc, std::move (str));
+	    }
+	  else
+	    {
+	      // division
+	      current_column++;
+	      return Token::make (DIV, loc);
+	    }
+	case '%':
+	  if (peek_input () == '=')
+	    {
+	      // modulo-assign
+	      skip_input ();
+	      current_column += 2;
+
+	      return Token::make (PERCENT_EQ, loc);
+	    }
+	  else
+	    {
+	      // modulo
+	      current_column++;
+	      return Token::make (PERCENT, loc);
+	    }
+	case '^':
+	  if (peek_input () == '=')
+	    {
+	      // xor-assign?
+	      skip_input ();
+	      current_column += 2;
+
+	      return Token::make (CARET_EQ, loc);
+	    }
+	  else
+	    {
+	      // xor?
+	      current_column++;
+	      return Token::make (CARET, loc);
+	    }
+	case '<':
+	  if (peek_input () == '<')
+	    {
+	      if (peek_input (1) == '=')
+		{
+		  // left-shift assign
+		  skip_input (1);
+		  current_column += 3;
+
+		  return Token::make (LEFT_SHIFT_EQ, loc);
+		}
+	      else
+		{
+		  // left-shift
+		  skip_input ();
+		  current_column += 2;
+
+		  return Token::make (LEFT_SHIFT, loc);
+		}
+	    }
+	  else if (peek_input () == '=')
+	    {
+	      // smaller than or equal to
+	      skip_input ();
+	      current_column += 2;
+
+	      return Token::make (LESS_OR_EQUAL, loc);
+	    }
+	  else
+	    {
+	      // smaller than
+	      current_column++;
+	      return Token::make (LEFT_ANGLE, loc);
+	    }
+	  break;
+	case '>':
+	  if (peek_input () == '>')
+	    {
+	      if (peek_input (1) == '=')
+		{
+		  // right-shift-assign
+		  skip_input (1);
+		  current_column += 3;
+
+		  return Token::make (RIGHT_SHIFT_EQ, loc);
+		}
+	      else
+		{
+		  // right-shift
+		  skip_input ();
+		  current_column += 2;
+
+		  return Token::make (RIGHT_SHIFT, loc);
+		}
+	    }
+	  else if (peek_input () == '=')
+	    {
+	      // larger than or equal to
+	      skip_input ();
+	      current_column += 2;
+
+	      return Token::make (GREATER_OR_EQUAL, loc);
+	    }
+	  else
+	    {
+	      // larger than
+	      current_column++;
+	      return Token::make (RIGHT_ANGLE, loc);
+	    }
+	case ':':
+	  if (peek_input () == ':')
+	    {
+	      // scope resolution ::
+	      skip_input ();
+	      current_column += 2;
+
+	      return Token::make (SCOPE_RESOLUTION, loc);
+	    }
+	  else
+	    {
+	      // single colon :
+	      current_column++;
+	      return Token::make (COLON, loc);
+	    }
+	case '!':
+	  // no special handling for macros in lexer?
+	  if (peek_input () == '=')
+	    {
+	      // not equal boolean operator
+	      skip_input ();
+	      current_column += 2;
+
+	      return Token::make (NOT_EQUAL, loc);
+	    }
+	  else
+	    {
+	      // not equal unary operator
+	      current_column++;
+
+	      return Token::make (EXCLAM, loc);
+	    }
+	case '?':
+	  current_column++;
+	  return Token::make (QUESTION_MARK, loc);
+	case '#':
+	  current_column++;
+	  return Token::make (HASH, loc);
+	case '[':
+	  current_column++;
+	  return Token::make (LEFT_SQUARE, loc);
+	case ']':
+	  current_column++;
+	  return Token::make (RIGHT_SQUARE, loc);
+	case '{':
+	  current_column++;
+	  return Token::make (LEFT_CURLY, loc);
+	case '}':
+	  current_column++;
+	  return Token::make (RIGHT_CURLY, loc);
+	case '@':
+	  current_column++;
+	  return Token::make (PATTERN_BIND, loc);
+	case '$':
+	  current_column++;
+	  return Token::make (DOLLAR_SIGN, loc);
+	case '~':
+	  current_column++;
+	  return Token::make (TILDE, loc);
+	case '\\':
+	  current_column++;
+	  return Token::make (BACKSLASH, loc);
+	case '`':
+	  current_column++;
+	  return Token::make (BACKTICK, loc);
+	case '|':
+	  if (peek_input () == '=')
+	    {
+	      // bitwise or-assign?
+	      skip_input ();
+	      current_column += 2;
+
+	      return Token::make (PIPE_EQ, loc);
+	    }
+	  else if (peek_input () == '|')
+	    {
+	      // logical or
+	      skip_input ();
+	      current_column += 2;
+
+	      return Token::make (OR, loc);
+	    }
+	  else
+	    {
+	      // bitwise or
+	      current_column++;
+
+	      return Token::make (PIPE, loc);
+	    }
+	case '&':
+	  if (peek_input () == '=')
+	    {
+	      // bitwise and-assign?
+	      skip_input ();
+	      current_column += 2;
+
+	      return Token::make (AMP_EQ, loc);
+	    }
+	  else if (peek_input () == '&')
+	    {
+	      // logical and
+	      skip_input ();
+	      current_column += 2;
+
+	      return Token::make (LOGICAL_AND, loc);
+	    }
+	  else
+	    {
+	      // bitwise and/reference
+	      current_column++;
+
+	      return Token::make (AMP, loc);
+	    }
+	case '.':
+	  if (peek_input () == '.')
+	    {
+	      if (peek_input (1) == '.')
+		{
+		  // ellipsis
+		  skip_input (1);
+		  current_column += 3;
+
+		  return Token::make (ELLIPSIS, loc);
+		}
+	      else if (peek_input (1) == '=')
+		{
+		  // ..=
+		  skip_input (1);
+		  current_column += 3;
+
+		  return Token::make (DOT_DOT_EQ, loc);
+		}
+	      else
+		{
+		  // ..
+		  skip_input ();
+		  current_column += 2;
+
+		  return Token::make (DOT_DOT, loc);
+		}
+	    }
+	  else /*if (!ISDIGIT (peek_input ()))*/
+	    {
+	      // single dot .
+	      // Only if followed by a non-number - otherwise is float
+	      // nope, float cannot start with '.'.
+	      current_column++;
+	      return Token::make (DOT, loc);
+	    }
+	}
+      // TODO: special handling of _ in the lexer? instead of being identifier
+
+      // byte character, byte string and raw byte string literals
+      if (current_char == 'b')
+	{
+	  if (peek_input () == '\'')
+	    return parse_byte_char (loc);
+	  else if (peek_input () == '"')
+	    return parse_byte_string (loc);
+	  else if (peek_input () == 'r'
+		   && (peek_input (1) == '#' || peek_input (1) == '"'))
+	    return parse_raw_byte_string (loc);
+	}
+
+      // raw identifiers and raw strings
+      if (current_char == 'r')
+	{
+	  int peek = peek_input ();
+	  int peek1 = peek_input (1);
+
+	  if (peek == '#' && (ISALPHA (peek1) || peek1 == '_'))
+	    {
+	      TokenPtr raw_ident_ptr = parse_raw_identifier (loc);
+	      if (raw_ident_ptr != nullptr)
+		return raw_ident_ptr;
+	      else
+		continue; /* input got parsed, it just wasn't valid. An error
+			     was produced. */
+	    }
+	  else
+	    {
+	      TokenPtr maybe_raw_string_ptr = maybe_parse_raw_string (loc);
+	      if (maybe_raw_string_ptr != nullptr)
+		return maybe_raw_string_ptr;
+	    }
+	}
+
+      // find identifiers and keywords
+      if (ISALPHA (current_char) || current_char == '_')
+	return parse_identifier_or_keyword (loc);
+
+      // int and float literals
+      if (ISDIGIT (current_char))
+	{ //  _ not allowed as first char
+	  if (current_char == '0'
+	      && is_non_decimal_int_literal_separator (peek_input ()))
+	    {
+	      // handle binary, octal, hex literals
+	      TokenPtr non_dec_int_lit_ptr
+		= parse_non_decimal_int_literals (loc);
+	      if (non_dec_int_lit_ptr != nullptr)
+		return non_dec_int_lit_ptr;
+	    }
+	  else
+	    {
+	      // handle decimals (integer or float)
+	      TokenPtr decimal_or_float_ptr = parse_decimal_int_or_float (loc);
+	      if (decimal_or_float_ptr != nullptr)
+		return decimal_or_float_ptr;
+	    }
+	}
+
+      // string literals
+      if (current_char == '"')
+	return parse_string (loc);
+
+      // char literals and lifetime names
+      if (current_char == '\'')
+	{
+	  TokenPtr char_or_lifetime_ptr = parse_char_or_lifetime (loc);
+	  if (char_or_lifetime_ptr != nullptr)
+	    return char_or_lifetime_ptr;
+	}
+
+      // DEBUG: check for specific character problems:
+      if (current_char == '0')
+	rust_debug ("'0' uncaught before unexpected character");
+      else if (current_char == ']')
+	rust_debug ("']' uncaught before unexpected character");
+      else if (current_char == 0x5d)
+	rust_debug ("whatever 0x5d is (not '0' or ']') uncaught before "
+		    "unexpected character");
+
+      // didn't match anything so error
+      rust_error_at (loc, "unexpected character %<%x%>", current_char);
+      current_column++;
+    }
+}
+
+// Parses in a type suffix.
+std::pair<PrimitiveCoreType, int>
+Lexer::parse_in_type_suffix ()
+{
+  std::string suffix;
+  suffix.reserve (5);
+
+  int additional_length_offset = 0;
+
+  // get suffix
+  while (ISALPHA (current_char) || ISDIGIT (current_char)
+	 || current_char == '_')
+    {
+      if (current_char == '_')
+	{
+	  // don't add _ to suffix
+	  skip_input ();
+	  current_char = peek_input ();
+
+	  additional_length_offset++;
+
+	  continue;
+	}
+
+      additional_length_offset++;
+
+      suffix += current_char;
+      skip_input ();
+      current_char = peek_input ();
+    }
+
+  if (suffix.empty ())
+    {
+      // no type suffix: do nothing but also no error
+      return std::make_pair (CORETYPE_UNKNOWN, additional_length_offset);
+    }
+  else if (suffix == "f32")
+    {
+      return std::make_pair (CORETYPE_F32, additional_length_offset);
+    }
+  else if (suffix == "f64")
+    {
+      return std::make_pair (CORETYPE_F64, additional_length_offset);
+    }
+  else if (suffix == "i8")
+    {
+      return std::make_pair (CORETYPE_I8, additional_length_offset);
+    }
+  else if (suffix == "i16")
+    {
+      return std::make_pair (CORETYPE_I16, additional_length_offset);
+    }
+  else if (suffix == "i32")
+    {
+      return std::make_pair (CORETYPE_I32, additional_length_offset);
+    }
+  else if (suffix == "i64")
+    {
+      return std::make_pair (CORETYPE_I64, additional_length_offset);
+    }
+  else if (suffix == "i128")
+    {
+      return std::make_pair (CORETYPE_I128, additional_length_offset);
+    }
+  else if (suffix == "isize")
+    {
+      return std::make_pair (CORETYPE_ISIZE, additional_length_offset);
+    }
+  else if (suffix == "u8")
+    {
+      return std::make_pair (CORETYPE_U8, additional_length_offset);
+    }
+  else if (suffix == "u16")
+    {
+      return std::make_pair (CORETYPE_U16, additional_length_offset);
+    }
+  else if (suffix == "u32")
+    {
+      return std::make_pair (CORETYPE_U32, additional_length_offset);
+    }
+  else if (suffix == "u64")
+    {
+      return std::make_pair (CORETYPE_U64, additional_length_offset);
+    }
+  else if (suffix == "u128")
+    {
+      return std::make_pair (CORETYPE_U128, additional_length_offset);
+    }
+  else if (suffix == "usize")
+    {
+      return std::make_pair (CORETYPE_USIZE, additional_length_offset);
+    }
+  else
+    {
+      rust_error_at (get_current_location (), "unknown number suffix %qs",
+		     suffix.c_str ());
+
+      return std::make_pair (CORETYPE_UNKNOWN, additional_length_offset);
+    }
+}
+
+// Parses in the exponent part (if any) of a float literal.
+std::pair<std::string, int>
+Lexer::parse_in_exponent_part ()
+{
+  int additional_length_offset = 0;
+  std::string str;
+  if (current_char == 'E' || current_char == 'e')
+    {
+      // add exponent to string as strtod works with it
+      str += current_char;
+      skip_input ();
+      current_char = peek_input ();
+
+      additional_length_offset++;
+
+      // special - and + handling
+      if (current_char == '-')
+	{
+	  str += '-';
+
+	  skip_input ();
+	  current_char = peek_input ();
+
+	  additional_length_offset++;
+	}
+      else if (current_char == '+')
+	{
+	  // don't add + but still skip input
+	  skip_input ();
+	  current_char = peek_input ();
+
+	  additional_length_offset++;
+	}
+
+      // parse another decimal number for exponent
+      auto str_length = parse_in_decimal ();
+      str += std::get<0> (str_length);
+      additional_length_offset += std::get<1> (str_length);
+    }
+  return std::make_pair (str, additional_length_offset);
+}
+
+// Parses a decimal integer.
+std::tuple<std::string, int, bool>
+Lexer::parse_in_decimal ()
+{
+  /* A pure decimal contains only digits.  */
+  bool pure_decimal = true;
+  int additional_length_offset = 0;
+  std::string str;
+  while (ISDIGIT (current_char) || current_char == '_')
+    {
+      if (current_char == '_')
+	{
+	  pure_decimal = false;
+	  // don't add _ to number
+	  skip_input ();
+	  current_char = peek_input ();
+
+	  additional_length_offset++;
+
+	  continue;
+	}
+
+      additional_length_offset++;
+
+      str += current_char;
+      skip_input ();
+      current_char = peek_input ();
+    }
+  return std::make_tuple (str, additional_length_offset, pure_decimal);
+}
+
+/* Parses escapes (and string continues) in "byte" strings and characters. Does
+ * not support unicode. */
+std::tuple<char, int, bool>
+Lexer::parse_escape (char opening_char)
+{
+  int additional_length_offset = 0;
+  char output_char = 0;
+
+  // skip to actual letter
+  skip_input ();
+  current_char = peek_input ();
+  additional_length_offset++;
+
+  switch (current_char)
+    {
+      case 'x': {
+	auto hex_escape_pair = parse_partial_hex_escape ();
+	long hexLong = hex_escape_pair.first;
+	additional_length_offset += hex_escape_pair.second;
+
+	if (hexLong > 255 || hexLong < 0)
+	  rust_error_at (
+	    get_current_location (),
+	    "byte \\x escape %<\\x%x%> out of range - allows up to %<\\xFF%>",
+	    static_cast<unsigned int> (hexLong));
+	/* TODO: restore capital for escape output - gcc pretty-printer doesn't
+	 * support %X directly */
+	char hexChar = static_cast<char> (hexLong);
+
+	output_char = hexChar;
+      }
+      break;
+    case 'n':
+      output_char = '\n';
+      break;
+    case 'r':
+      output_char = '\r';
+      break;
+    case 't':
+      output_char = '\t';
+      break;
+    case '\\':
+      output_char = '\\';
+      break;
+    case '0':
+      output_char = '\0';
+      break;
+    case '\'':
+      output_char = '\'';
+      break;
+    case '"':
+      output_char = '"';
+      break;
+    case 'u':
+      rust_error_at (get_current_location (),
+		     "cannot have a unicode escape \\u in a byte %s",
+		     opening_char == '\'' ? "character" : "string");
+      // Try to parse it anyway, just to skip it
+      parse_partial_unicode_escape ();
+      return std::make_tuple (output_char, additional_length_offset, false);
+    case '\r':
+    case '\n':
+      // string continue
+      return std::make_tuple (0, parse_partial_string_continue (), true);
+    default:
+      rust_error_at (get_current_location (),
+		     "unknown escape sequence %<\\%c%>", current_char);
+      // returns false if no parsing could be done
+      // return false;
+      return std::make_tuple (output_char, additional_length_offset, false);
+      break;
+    }
+  // all non-special cases (string continue) should skip their used char
+  skip_input ();
+  current_char = peek_input ();
+  additional_length_offset++;
+
+  // returns true if parsing was successful
+  // return true;
+  return std::make_tuple (output_char, additional_length_offset, false);
+}
+
+/* Parses an escape (or string continue) in a string or character. Supports
+ * unicode escapes. */
+std::tuple<Codepoint, int, bool>
+Lexer::parse_utf8_escape (char opening_char)
+{
+  Codepoint output_char;
+  int additional_length_offset = 0;
+
+  // skip to actual letter
+  skip_input ();
+  current_char = peek_input ();
+  additional_length_offset++;
+
+  switch (current_char)
+    {
+      case 'x': {
+	auto hex_escape_pair = parse_partial_hex_escape ();
+	long hexLong = hex_escape_pair.first;
+	additional_length_offset += hex_escape_pair.second;
+
+	if (hexLong > 127 || hexLong < 0)
+	  rust_error_at (
+	    get_current_location (),
+	    "ascii \\x escape %<\\x%x%> out of range - allows up to %<\\x7F%>",
+	    static_cast<unsigned int> (hexLong));
+	/* TODO: restore capital for escape output - gcc pretty-printer doesn't
+	 * support %X directly */
+	char hexChar = static_cast<char> (hexLong);
+
+	output_char = hexChar;
+      }
+      break;
+    case 'n':
+      output_char = '\n';
+      break;
+    case 'r':
+      output_char = '\r';
+      break;
+    case 't':
+      output_char = '\t';
+      break;
+    case '\\':
+      output_char = '\\';
+      break;
+    case '0':
+      output_char = '\0';
+      break;
+    case '\'':
+      output_char = '\'';
+      break;
+    case '"':
+      output_char = '"';
+      break;
+      case 'u': {
+	auto unicode_escape_pair = parse_partial_unicode_escape ();
+	output_char = unicode_escape_pair.first;
+	additional_length_offset += unicode_escape_pair.second;
+
+	return std::make_tuple (output_char, additional_length_offset, false);
+      }
+      break;
+    case '\r':
+    case '\n':
+      // string continue
+      return std::make_tuple (0, parse_partial_string_continue (), true);
+    default:
+      rust_error_at (get_current_location (),
+		     "unknown escape sequence %<\\%c%>", current_char);
+      // returns false if no parsing could be done
+      // return false;
+      return std::make_tuple (output_char, additional_length_offset, false);
+      break;
+    }
+  /* all non-special cases (unicode, string continue) should skip their used
+   * char */
+  skip_input ();
+  current_char = peek_input ();
+  additional_length_offset++;
+
+  // returns true if parsing was successful
+  // return true;
+  return std::make_tuple (output_char, additional_length_offset, false);
+}
+
+// Parses the body of a string continue that has been found in an escape.
+int
+Lexer::parse_partial_string_continue ()
+{
+  int additional_length_offset = 1;
+
+  // string continue
+  while (is_whitespace (current_char))
+    {
+      if (current_char == '\n')
+	{
+	  current_line++;
+	  current_column = 1;
+	  // tell line_table that new line starts
+	  start_line (current_line, max_column_hint);
+
+	  // reset "length"
+	  additional_length_offset = 1;
+
+	  // get next char
+	  skip_input ();
+	  current_char = peek_input ();
+
+	  continue;
+	}
+
+      skip_input ();
+      current_char = peek_input ();
+      additional_length_offset++;
+    }
+
+  return additional_length_offset;
+}
+
+/* Parses the body of a '\x' escape. Note that it does not check that the number
+ * is valid and smaller than 255. */
+std::pair<long, int>
+Lexer::parse_partial_hex_escape ()
+{
+  // hex char string (null-terminated)
+  char hexNum[3] = {0, 0, 0};
+
+  // first hex char
+  current_char = peek_input (1);
+  int additional_length_offset = 1;
+
+  if (!is_x_digit (current_char))
+    {
+      rust_error_at (get_current_location (),
+		     "invalid character %<\\x%c%> in \\x sequence",
+		     current_char);
+      return std::make_pair (0, 0);
+    }
+  hexNum[0] = current_char;
+
+  // second hex char
+  skip_input ();
+  current_char = peek_input (1);
+  additional_length_offset++;
+
+  if (!is_x_digit (current_char))
+    {
+      rust_error_at (get_current_location (),
+		     "invalid character %<\\x%c%c%> in \\x sequence", hexNum[0],
+		     current_char);
+      return std::make_pair (0, 1);
+    }
+  skip_input ();
+  hexNum[1] = current_char;
+
+  long hexLong = std::strtol (hexNum, nullptr, 16);
+
+  return std::make_pair (hexLong, additional_length_offset);
+}
+
+// Parses the body of a unicode escape.
+std::pair<Codepoint, int>
+Lexer::parse_partial_unicode_escape ()
+{
+  skip_input ();
+  current_char = peek_input ();
+  int additional_length_offset = 0;
+
+  if (current_char != '{')
+    {
+      rust_error_at (get_current_location (),
+		     "unicode escape should start with %<{%>");
+      /* Skip what should probaby have been between brackets.  */
+      while (is_x_digit (current_char) || current_char == '_')
+	{
+	  skip_input ();
+	  current_char = peek_input ();
+	  additional_length_offset++;
+	}
+      return std::make_pair (Codepoint (0), additional_length_offset);
+    }
+
+  skip_input ();
+  current_char = peek_input ();
+  additional_length_offset++;
+
+  if (current_char == '_')
+    {
+      rust_error_at (get_current_location (),
+		     "unicode escape cannot start with %<_%>");
+      skip_input ();
+      current_char = peek_input ();
+      additional_length_offset++;
+      // fallthrough and try to parse the rest anyway
+    }
+
+  // parse unicode escape - 1-6 hex digits
+  std::string num_str;
+  num_str.reserve (6);
+
+  // loop through to add entire hex number to string
+  while (is_x_digit (current_char) || current_char == '_')
+    {
+      if (current_char == '_')
+	{
+	  // don't add _ to number
+	  skip_input ();
+	  current_char = peek_input ();
+
+	  additional_length_offset++;
+
+	  continue;
+	}
+
+      additional_length_offset++;
+
+      // add raw hex numbers
+      num_str += current_char;
+
+      skip_input ();
+      current_char = peek_input ();
+    }
+
+  if (current_char == '}')
+    {
+      skip_input ();
+      current_char = peek_input ();
+      additional_length_offset++;
+    }
+  else
+    {
+      // actually an error, but allow propagation anyway Assume that
+      // wrong bracketm whitespace or single/double quotes are wrong
+      // termination, otherwise it is a wrong character, then skip to the actual
+      // terminator.
+      if (current_char == '{' || is_whitespace (current_char)
+	  || current_char == '\'' || current_char == '"')
+	{
+	  rust_error_at (get_current_location (),
+			 "expected terminating %<}%> in unicode escape");
+	  return std::make_pair (Codepoint (0), additional_length_offset);
+	}
+      else
+	{
+	  rust_error_at (get_current_location (),
+			 "invalid character %<%c%> in unicode escape",
+			 current_char);
+	  while (current_char != '}' && current_char != '{'
+		 && !is_whitespace (current_char) && current_char != '\''
+		 && current_char != '"')
+	    {
+	      skip_input ();
+	      current_char = peek_input ();
+	      additional_length_offset++;
+	    }
+	  // Consume the actual closing bracket if found
+	  if (current_char == '}')
+	    {
+	      skip_input ();
+	      current_char = peek_input ();
+	      additional_length_offset++;
+	    }
+	  return std::make_pair (Codepoint (0), additional_length_offset);
+	}
+    }
+
+  // ensure 1-6 hex characters
+  if (num_str.length () > 6 || num_str.length () < 1)
+    {
+      rust_error_at (get_current_location (),
+		     "unicode escape should be between 1 and 6 hex "
+		     "characters; it is %lu",
+		     (unsigned long) num_str.length ());
+      // return false;
+      return std::make_pair (Codepoint (0), additional_length_offset);
+    }
+
+  unsigned long hex_num = std::strtoul (num_str.c_str (), nullptr, 16);
+
+  if (hex_num > 0xd7ff && hex_num < 0xe000)
+    {
+      rust_error_at (
+	get_current_location (),
+	"unicode escape cannot be a surrogate value (D800 to DFFF)");
+      return std::make_pair (Codepoint (0), additional_length_offset);
+    }
+
+  if (hex_num > 0x10ffff)
+    {
+      rust_error_at (get_current_location (),
+		     "unicode escape cannot be larger than 10FFFF");
+      return std::make_pair (Codepoint (0), additional_length_offset);
+    }
+
+  // return true;
+  return std::make_pair (Codepoint (static_cast<uint32_t> (hex_num)),
+			 additional_length_offset);
+}
+
+// Parses a byte character.
+TokenPtr
+Lexer::parse_byte_char (Location loc)
+{
+  skip_input ();
+  current_column++;
+  // make current char the next character
+  current_char = peek_input ();
+
+  int length = 1;
+
+  // char to save
+  char byte_char = 0;
+
+  // detect escapes
+  if (current_char == '\\')
+    {
+      auto escape_length_pair = parse_escape ('\'');
+      byte_char = std::get<0> (escape_length_pair);
+      length += std::get<1> (escape_length_pair);
+
+      current_char = peek_input ();
+
+      if (current_char != '\'')
+	{
+	  rust_error_at (get_current_location (), "unclosed %<byte char%>");
+	}
+
+      skip_input ();
+      current_char = peek_input ();
+      length++; // go to next char
+    }
+  else if (current_char != '\'')
+    {
+      // otherwise, get character from direct input character
+      byte_char = current_char;
+
+      skip_input ();
+      current_char = peek_input ();
+      length++;
+
+      if (current_char != '\'')
+	{
+	  rust_error_at (get_current_location (), "unclosed %<byte char%>");
+	}
+
+      skip_input ();
+      current_char = peek_input ();
+      length++; // go to next char
+    }
+  else
+    {
+      rust_error_at (get_current_location (),
+		     "no character inside %<%> for %<byte char%>");
+    }
+
+  current_column += length;
+
+  return Token::make_byte_char (loc, byte_char);
+}
+
+// Parses a byte string.
+TokenPtr
+Lexer::parse_byte_string (Location loc)
+{
+  // byte string
+
+  // skip quote character
+  skip_input ();
+  current_column++;
+
+  std::string str;
+  str.reserve (16); // some sensible default
+
+  int length = 1;
+  current_char = peek_input ();
+
+  while (current_char != '"' && current_char != EOF)
+    {
+      if (current_char == '\\')
+	{
+	  auto escape_length_pair = parse_escape ('"');
+	  char output_char = std::get<0> (escape_length_pair);
+
+	  if (output_char == 0 && std::get<2> (escape_length_pair))
+	    length = std::get<1> (escape_length_pair) - 1;
+	  else
+	    length += std::get<1> (escape_length_pair);
+
+	  if (output_char != 0 || !std::get<2> (escape_length_pair))
+	    str += output_char;
+
+	  continue;
+	}
+
+      length++;
+
+      str += current_char;
+      skip_input ();
+      current_char = peek_input ();
+    }
+
+  current_column += length;
+
+  if (current_char == '"')
+    {
+      current_column++;
+
+      skip_input ();
+      current_char = peek_input ();
+    }
+  else if (current_char == EOF)
+    {
+      rust_error_at (get_current_location (), "unended byte string literal");
+      return Token::make (END_OF_FILE, get_current_location ());
+    }
+  else
+    {
+      gcc_unreachable ();
+    }
+
+  str.shrink_to_fit ();
+
+  return Token::make_byte_string (loc, std::move (str));
+}
+
+// Parses a raw byte string.
+TokenPtr
+Lexer::parse_raw_byte_string (Location loc)
+{
+  // raw byte string literals
+  std::string str;
+  str.reserve (16); // some sensible default
+
+  int length = 1;
+  int hash_count = 0;
+
+  // get hash count at beginnning
+  skip_input ();
+  current_char = peek_input ();
+  length++;
+  while (current_char == '#')
+    {
+      hash_count++;
+      length++;
+
+      skip_input ();
+      current_char = peek_input ();
+    }
+
+  if (current_char != '"')
+    {
+      rust_error_at (get_current_location (),
+		     "raw byte string has no opening %<\"%>");
+    }
+
+  skip_input ();
+  current_char = peek_input ();
+  length++;
+
+  while (true)
+    {
+      if (current_char == '"')
+	{
+	  bool enough_hashes = true;
+
+	  for (int i = 0; i < hash_count; i++)
+	    {
+	      if (peek_input (i + 1) != '#')
+		{
+		  enough_hashes = false;
+		  break;
+		}
+	    }
+
+	  if (enough_hashes)
+	    {
+	      // skip enough input and peek enough input
+	      skip_input (hash_count);
+	      current_char = peek_input ();
+	      length += hash_count + 1;
+	      break;
+	    }
+	}
+
+      if ((unsigned char) current_char > 127)
+	{
+	  rust_error_at (get_current_location (),
+			 "character %<%c%> in raw byte string out of range",
+			 current_char);
+	  current_char = 0;
+	}
+
+      length++;
+
+      str += current_char;
+      skip_input ();
+      current_char = peek_input ();
+    }
+
+  current_column += length;
+
+  str.shrink_to_fit ();
+
+  return Token::make_byte_string (loc, std::move (str));
+}
+
+// Parses a raw identifier.
+TokenPtr
+Lexer::parse_raw_identifier (Location loc)
+{
+  // raw identifier
+  std::string str;
+  str.reserve (16); // default
+
+  skip_input ();
+  current_char = peek_input ();
+
+  current_column += 2;
+
+  bool first_is_underscore = current_char == '_';
+
+  int length = 0;
+  current_char = peek_input ();
+  // loop through entire name
+  while (ISALPHA (current_char) || ISDIGIT (current_char)
+	 || current_char == '_')
+    {
+      length++;
+
+      str += current_char;
+      skip_input ();
+      current_char = peek_input ();
+    }
+
+  current_column += length;
+
+  // if just a single underscore, not an identifier
+  if (first_is_underscore && length == 1)
+    rust_error_at (get_current_location (),
+		   "%<_%> is not a valid raw identifier");
+
+  if (str == "crate" || str == "extern" || str == "self" || str == "super"
+      || str == "Self")
+    {
+      rust_error_at (get_current_location (),
+		     "%qs is a forbidden raw identifier", str.c_str ());
+
+      return nullptr;
+    }
+  else
+    {
+      str.shrink_to_fit ();
+
+      return Token::make_identifier (loc, std::move (str));
+    }
+}
+
+// skip broken string input (unterminated strings)
+void
+Lexer::skip_broken_string_input (int current_char)
+{
+  while (current_char != '"' && current_char != EOF)
+    {
+      if (current_char == '\n')
+	{
+	  current_line++;
+	  current_column = 1;
+	}
+      else
+	{
+	  current_column++;
+	}
+      skip_input ();
+      current_char = peek_input ();
+    }
+  if (current_char == '"')
+    {
+      current_column++;
+
+      skip_input ();
+      current_char = peek_input ();
+    }
+  rust_debug ("skipped to %d:%d due to bad quotes", current_line,
+	      current_column);
+}
+
+// Parses a unicode string.
+TokenPtr
+Lexer::parse_string (Location loc)
+{
+  Codepoint current_char32;
+
+  std::string str;
+  str.reserve (16); // some sensible default
+
+  int length = 1;
+  current_char32 = peek_codepoint_input ();
+
+  // FIXME: This fails if the input ends. How do we check for EOF?
+  while (current_char32.value != '"' && !current_char32.is_eof ())
+    {
+      if (current_char32.value == '\\')
+	{
+	  // parse escape
+	  auto utf8_escape_pair = parse_utf8_escape ('\'');
+	  current_char32 = std::get<0> (utf8_escape_pair);
+
+	  if (current_char32 == Codepoint (0) && std::get<2> (utf8_escape_pair))
+	    length = std::get<1> (utf8_escape_pair) - 1;
+	  else
+	    length += std::get<1> (utf8_escape_pair);
+
+	  if (current_char32 != Codepoint (0)
+	      || !std::get<2> (utf8_escape_pair))
+	    str += current_char32;
+
+	  // required as parsing utf8 escape only changes current_char
+	  current_char32 = peek_codepoint_input ();
+
+	  continue;
+	}
+
+      length += get_input_codepoint_length ();
+
+      str += current_char32;
+      skip_codepoint_input ();
+      current_char32 = peek_codepoint_input ();
+    }
+
+  current_column += length;
+
+  if (current_char32.value == '"')
+    {
+      current_column++;
+
+      skip_input ();
+      current_char = peek_input ();
+    }
+  else if (current_char32.is_eof ())
+    {
+      rust_error_at (get_current_location (), "unended string literal");
+      return Token::make (END_OF_FILE, get_current_location ());
+    }
+  else
+    {
+      gcc_unreachable ();
+    }
+
+  str.shrink_to_fit ();
+  return Token::make_string (loc, std::move (str));
+}
+
+// Parses an identifier or keyword.
+TokenPtr
+Lexer::parse_identifier_or_keyword (Location loc)
+{
+  std::string str;
+  str.reserve (16); // default
+  str += current_char;
+
+  bool first_is_underscore = current_char == '_';
+
+  int length = 1;
+  current_char = peek_input ();
+  // loop through entire name
+  while (ISALPHA (current_char) || ISDIGIT (current_char)
+	 || current_char == '_')
+    {
+      length++;
+
+      str += current_char;
+      skip_input ();
+      current_char = peek_input ();
+    }
+
+  current_column += length;
+
+  // if just a single underscore, not an identifier
+  if (first_is_underscore && length == 1)
+    return Token::make (UNDERSCORE, loc);
+
+  str.shrink_to_fit ();
+
+  TokenId keyword = classify_keyword (str);
+  if (keyword == IDENTIFIER)
+    return Token::make_identifier (loc, std::move (str));
+  else
+    return Token::make (keyword, loc);
+}
+
+// Possibly returns a raw string token if it exists - otherwise returns null.
+TokenPtr
+Lexer::maybe_parse_raw_string (Location loc)
+{
+  int peek_index = 0;
+  while (peek_input (peek_index) == '#')
+    peek_index++;
+
+  if (peek_input (peek_index) == '"')
+    return parse_raw_string (loc, peek_index);
+  else
+    return nullptr;
+}
+
+// Returns a raw string token.
+TokenPtr
+Lexer::parse_raw_string (Location loc, int initial_hash_count)
+{
+  // raw string literals
+  std::string str;
+  str.reserve (16); // some sensible default
+
+  int length = 1 + initial_hash_count;
+
+  if (initial_hash_count > 0)
+    skip_input (initial_hash_count - 1);
+
+  current_char = peek_input ();
+
+  if (current_char != '"')
+    rust_error_at (get_current_location (), "raw string has no opening %<\"%>");
+
+  length++;
+  skip_input ();
+  Codepoint current_char32 = peek_codepoint_input ();
+
+  while (!current_char32.is_eof ())
+    {
+      if (current_char32.value == '"')
+	{
+	  bool enough_hashes = true;
+
+	  for (int i = 0; i < initial_hash_count; i++)
+	    {
+	      if (peek_input (i + 1) != '#')
+		{
+		  enough_hashes = false;
+		  break;
+		}
+	    }
+
+	  if (enough_hashes)
+	    {
+	      // skip enough input and peek enough input
+	      skip_input (initial_hash_count);
+	      current_char = peek_input ();
+	      length += initial_hash_count + 1;
+	      break;
+	    }
+	}
+
+      length++;
+
+      str += current_char32;
+      skip_codepoint_input ();
+      current_char32 = peek_codepoint_input ();
+    }
+
+  current_column += length;
+
+  str.shrink_to_fit ();
+
+  return Token::make_string (loc, std::move (str));
+}
+
+template <typename IsDigitFunc>
+TokenPtr
+Lexer::parse_non_decimal_int_literal (Location loc, IsDigitFunc is_digit_func,
+				      std::string existent_str, int base)
+{
+  int length = 1;
+
+  skip_input ();
+  current_char = peek_input ();
+
+  length++;
+
+  // loop through to add entire number to string
+  while (is_digit_func (current_char) || current_char == '_')
+    {
+      if (current_char == '_')
+	{
+	  // don't add _ to number
+	  skip_input ();
+	  current_char = peek_input ();
+
+	  length++;
+
+	  continue;
+	}
+
+      length++;
+
+      // add raw numbers
+      existent_str += current_char;
+      skip_input ();
+      current_char = peek_input ();
+    }
+
+  // convert value to decimal representation
+  long dec_num = std::strtol (existent_str.c_str (), nullptr, base);
+
+  existent_str = std::to_string (dec_num);
+
+  // parse in type suffix if it exists
+  auto type_suffix_pair = parse_in_type_suffix ();
+  PrimitiveCoreType type_hint = type_suffix_pair.first;
+  length += type_suffix_pair.second;
+
+  current_column += length;
+
+  if (type_hint == CORETYPE_F32 || type_hint == CORETYPE_F64)
+    {
+      rust_error_at (get_current_location (),
+		     "invalid type suffix %qs for integer (%s) literal",
+		     get_type_hint_string (type_hint),
+		     base == 16
+		       ? "hex"
+		       : (base == 8 ? "octal"
+				    : (base == 2 ? "binary"
+						 : "<insert unknown base>")));
+      return nullptr;
+    }
+  return Token::make_int (loc, std::move (existent_str), type_hint);
+}
+
+// Parses a hex, binary or octal int literal.
+TokenPtr
+Lexer::parse_non_decimal_int_literals (Location loc)
+{
+  std::string str;
+  str.reserve (16); // some sensible default
+  str += current_char;
+
+  current_char = peek_input ();
+
+  if (current_char == 'x')
+    {
+      // hex (integer only)
+      return parse_non_decimal_int_literal (loc, is_x_digit, str + "x", 16);
+    }
+  else if (current_char == 'o')
+    {
+      // octal (integer only)
+      return parse_non_decimal_int_literal (loc, is_octal_digit,
+					    std::move (str), 8);
+    }
+  else if (current_char == 'b')
+    {
+      // binary (integer only)
+      return parse_non_decimal_int_literal (loc, is_bin_digit, std::move (str),
+					    2);
+    }
+  else
+    {
+      return nullptr;
+    }
+}
+
+// Parses a decimal-based int literal or float literal.
+TokenPtr
+Lexer::parse_decimal_int_or_float (Location loc)
+{
+  std::string str;
+  str.reserve (16); // some sensible default
+  str += current_char;
+
+  int length = 1;
+  bool first_zero = current_char == '0';
+
+  current_char = peek_input ();
+
+  // parse initial decimal integer (or first integer part of float) literal
+  auto initial_decimal = parse_in_decimal ();
+  str += std::get<0> (initial_decimal);
+  length += std::get<1> (initial_decimal);
+
+  // detect float literal
+  if (current_char == '.' && is_float_digit (peek_input (1)))
+    {
+      // float with a '.', parse another decimal into it
+
+      // add . to str
+      str += current_char;
+      skip_input ();
+      current_char = peek_input ();
+      length++;
+
+      // parse another decimal number for float
+      auto second_decimal = parse_in_decimal ();
+      str += std::get<0> (second_decimal);
+      length += std::get<1> (second_decimal);
+
+      // parse in exponent part if it exists
+      auto exponent_pair = parse_in_exponent_part ();
+      str += exponent_pair.first;
+      length += exponent_pair.second;
+
+      // parse in type suffix if it exists
+      auto type_suffix_pair = parse_in_type_suffix ();
+      PrimitiveCoreType type_hint = type_suffix_pair.first;
+      length += type_suffix_pair.second;
+
+      if (type_hint != CORETYPE_F32 && type_hint != CORETYPE_F64
+	  && type_hint != CORETYPE_UNKNOWN)
+	{
+	  rust_error_at (get_current_location (),
+			 "invalid type suffix %qs for floating-point literal",
+			 get_type_hint_string (type_hint));
+	  // ignore invalid type suffix as everything else seems fine
+	  type_hint = CORETYPE_UNKNOWN;
+	}
+
+      current_column += length;
+
+      str.shrink_to_fit ();
+      return Token::make_float (loc, std::move (str), type_hint);
+    }
+  else if (current_char == '.' && check_valid_float_dot_end (peek_input (1)))
+    {
+      // float that is just an integer with a terminating '.' character
+
+      // add . to str
+      str += current_char;
+      skip_input ();
+      current_char = peek_input ();
+      length++;
+
+      // add a '0' after the . to prevent ambiguity
+      str += '0';
+
+      // type hint not allowed
+
+      current_column += length;
+
+      str.shrink_to_fit ();
+      return Token::make_float (loc, std::move (str), CORETYPE_UNKNOWN);
+    }
+  else if (current_char == 'E' || current_char == 'e')
+    {
+      // exponent float with no '.' character
+
+      // parse exponent part
+      auto exponent_pair = parse_in_exponent_part ();
+      str += exponent_pair.first;
+      length += exponent_pair.second;
+
+      // parse in type suffix if it exists
+      auto type_suffix_pair = parse_in_type_suffix ();
+      PrimitiveCoreType type_hint = type_suffix_pair.first;
+      length += type_suffix_pair.second;
+
+      if (type_hint != CORETYPE_F32 && type_hint != CORETYPE_F64
+	  && type_hint != CORETYPE_UNKNOWN)
+	{
+	  rust_error_at (get_current_location (),
+			 "invalid type suffix %qs for floating-point literal",
+			 get_type_hint_string (type_hint));
+	  // ignore invalid type suffix as everything else seems fine
+	  type_hint = CORETYPE_UNKNOWN;
+	}
+
+      current_column += length;
+
+      str.shrink_to_fit ();
+      return Token::make_float (loc, std::move (str), type_hint);
+    }
+  else
+    {
+      // is an integer
+
+      // parse in type suffix if it exists
+      auto type_suffix_pair = parse_in_type_suffix ();
+      PrimitiveCoreType type_hint = type_suffix_pair.first;
+      /* A "real" pure decimal doesn't have a suffix and no zero prefix.  */
+      if (type_hint == CORETYPE_UNKNOWN)
+	{
+	  bool pure_decimal = std::get<2> (initial_decimal);
+	  if (pure_decimal && (!first_zero || str.size () == 1))
+	    type_hint = CORETYPE_PURE_DECIMAL;
+	}
+      length += type_suffix_pair.second;
+
+      current_column += length;
+
+      str.shrink_to_fit ();
+      return Token::make_int (loc, std::move (str), type_hint);
+    }
+}
+
+TokenPtr
+Lexer::parse_char_or_lifetime (Location loc)
+{
+  Codepoint current_char32;
+
+  int length = 1;
+
+  current_char32 = peek_codepoint_input ();
+  if (current_char32.is_eof ())
+    return nullptr;
+
+  // parse escaped char literal
+  if (current_char32.value == '\\')
+    {
+      // parse escape
+      auto utf8_escape_pair = parse_utf8_escape ('\'');
+      current_char32 = std::get<0> (utf8_escape_pair);
+      length += std::get<1> (utf8_escape_pair);
+
+      if (peek_codepoint_input ().value != '\'')
+	{
+	  rust_error_at (get_current_location (), "unended character literal");
+	}
+      else
+	{
+	  skip_codepoint_input ();
+	  current_char = peek_input ();
+	  length++;
+	}
+
+      current_column += length;
+
+      return Token::make_char (loc, current_char32);
+    }
+  else
+    {
+      skip_codepoint_input ();
+
+      if (peek_codepoint_input ().value == '\'')
+	{
+	  // parse non-escaped char literal
+
+	  // skip the ' character
+	  skip_input ();
+	  current_char = peek_input ();
+
+	  // TODO fix due to different widths of utf-8 chars?
+	  current_column += 3;
+
+	  return Token::make_char (loc, current_char32);
+	}
+      else if (ISDIGIT (current_char32.value) || ISALPHA (current_char32.value)
+	       || current_char32.value == '_')
+	{
+	  // parse lifetime name
+	  std::string str;
+	  str += current_char32;
+	  length++;
+
+	  current_char = peek_input ();
+	  while (ISDIGIT (current_char) || ISALPHA (current_char)
+		 || current_char == '_')
+	    {
+	      str += current_char;
+	      skip_input ();
+	      current_char = peek_input ();
+	      length++;
+	    }
+
+	  current_column += length;
+
+	  str.shrink_to_fit ();
+	  return Token::make_lifetime (loc, std::move (str));
+	}
+      else
+	{
+	  rust_error_at (
+	    get_current_location (),
+	    "expected %' after character constant in character literal");
+	  return nullptr;
+	}
+    }
+}
+
+// Returns the length of the codepoint at the current position.
+int
+Lexer::get_input_codepoint_length ()
+{
+  uint8_t input = peek_input ();
+
+  if ((int8_t) input == EOF)
+    return 0;
+
+  if (input < 128)
+    {
+      // ascii -- 1 byte
+      // return input;
+
+      return 1;
+    }
+  else if ((input & 0xC0) == 0x80)
+    {
+      // invalid (continuation; can't be first char)
+      // return 0xFFFE;
+
+      return 0;
+    }
+  else if ((input & 0xE0) == 0xC0)
+    {
+      // 2 bytes
+      uint8_t input2 = peek_input (1);
+      if ((input2 & 0xC0) != 0x80)
+	return 0;
+      // return 0xFFFE;
+
+      // uint32_t output = ((input & 0x1F) << 6) | ((input2 & 0x3F) << 0);
+      // return output;
+      return 2;
+    }
+  else if ((input & 0xF0) == 0xE0)
+    {
+      // 3 bytes
+      uint8_t input2 = peek_input (1);
+      if ((input2 & 0xC0) != 0x80)
+	return 0;
+      // return 0xFFFE;
+
+      uint8_t input3 = peek_input (2);
+      if ((input3 & 0xC0) != 0x80)
+	return 0;
+      // return 0xFFFE;
+
+      /*uint32_t output
+	= ((input & 0x0F) << 12) | ((input2 & 0x3F) << 6) | ((input3 & 0x3F) <<
+      0); return output;*/
+      return 3;
+    }
+  else if ((input & 0xF8) == 0xF0)
+    {
+      // 4 bytes
+      uint8_t input2 = peek_input (1);
+      if ((input2 & 0xC0) != 0x80)
+	return 0;
+      // return 0xFFFE;
+
+      uint8_t input3 = peek_input (2);
+      if ((input3 & 0xC0) != 0x80)
+	return 0;
+      // return 0xFFFE;
+
+      uint8_t input4 = peek_input (3);
+      if ((input4 & 0xC0) != 0x80)
+	return 0;
+      // return 0xFFFE;
+
+      /*uint32_t output = ((input & 0x07) << 18) | ((input2 & 0x3F) << 12)
+			| ((input3 & 0x3F) << 6) | ((input4 & 0x3F) << 0);
+      return output;*/
+      return 4;
+    }
+  else
+    {
+      rust_error_at (get_current_location (),
+		     "invalid UTF-8 [FIRST] (too long)");
+      return 0;
+    }
+}
+
+// Returns the codepoint at the current position.
+Codepoint
+Lexer::peek_codepoint_input ()
+{
+  uint8_t input = peek_input ();
+
+  if ((int8_t) input == EOF)
+    return Codepoint::eof ();
+
+  if (input < 128)
+    {
+      // ascii -- 1 byte
+      return {input};
+    }
+  else if ((input & 0xC0) == 0x80)
+    {
+      // invalid (continuation; can't be first char)
+      return {0xFFFE};
+    }
+  else if ((input & 0xE0) == 0xC0)
+    {
+      // 2 bytes
+      uint8_t input2 = peek_input (1);
+      if ((input2 & 0xC0) != 0x80)
+	return {0xFFFE};
+
+      uint32_t output = ((input & 0x1F) << 6) | ((input2 & 0x3F) << 0);
+      return {output};
+    }
+  else if ((input & 0xF0) == 0xE0)
+    {
+      // 3 bytes
+      uint8_t input2 = peek_input (1);
+      if ((input2 & 0xC0) != 0x80)
+	return {0xFFFE};
+
+      uint8_t input3 = peek_input (2);
+      if ((input3 & 0xC0) != 0x80)
+	return {0xFFFE};
+
+      uint32_t output = ((input & 0x0F) << 12) | ((input2 & 0x3F) << 6)
+			| ((input3 & 0x3F) << 0);
+      return {output};
+    }
+  else if ((input & 0xF8) == 0xF0)
+    {
+      // 4 bytes
+      uint8_t input2 = peek_input (1);
+      if ((input2 & 0xC0) != 0x80)
+	return {0xFFFE};
+
+      uint8_t input3 = peek_input (2);
+      if ((input3 & 0xC0) != 0x80)
+	return {0xFFFE};
+
+      uint8_t input4 = peek_input (3);
+      if ((input4 & 0xC0) != 0x80)
+	return {0xFFFE};
+
+      uint32_t output = ((input & 0x07) << 18) | ((input2 & 0x3F) << 12)
+			| ((input3 & 0x3F) << 6) | ((input4 & 0x3F) << 0);
+      return {output};
+    }
+  else
+    {
+      rust_error_at (get_current_location (),
+		     "invalid UTF-8 [SECND] (too long)");
+      return {0xFFFE};
+    }
+}
+
+void
+Lexer::skip_codepoint_input ()
+{
+  int toSkip = get_input_codepoint_length ();
+  gcc_assert (toSkip >= 1);
+
+  skip_input (toSkip - 1);
+}
+
+int
+Lexer::test_get_input_codepoint_n_length (int n_start_offset)
+{
+  uint8_t input = peek_input (n_start_offset);
+
+  if (input < 128)
+    {
+      // ascii -- 1 byte
+      // return input;
+      return 1;
+    }
+  else if ((input & 0xC0) == 0x80)
+    {
+      // invalid (continuation; can't be first char)
+      // return 0xFFFE;
+      return 0;
+    }
+  else if ((input & 0xE0) == 0xC0)
+    {
+      // 2 bytes
+      uint8_t input2 = peek_input (n_start_offset + 1);
+      if ((input2 & 0xC0) != 0x80)
+	// return 0xFFFE;
+	return 0;
+
+      // uint32_t output = ((input & 0x1F) << 6) | ((input2 & 0x3F) << 0);
+      // return output;
+      return 2;
+    }
+  else if ((input & 0xF0) == 0xE0)
+    {
+      // 3 bytes
+      uint8_t input2 = peek_input (n_start_offset + 1);
+      if ((input2 & 0xC0) != 0x80)
+	// return 0xFFFE;
+	return 0;
+
+      uint8_t input3 = peek_input (n_start_offset + 2);
+      if ((input3 & 0xC0) != 0x80)
+	// return 0xFFFE;
+	return 0;
+
+      /*uint32_t output
+	= ((input & 0x0F) << 12) | ((input2 & 0x3F) << 6) | ((input3 & 0x3F) <<
+      0); return output;*/
+      return 3;
+    }
+  else if ((input & 0xF8) == 0xF0)
+    {
+      // 4 bytes
+      uint8_t input2 = peek_input (n_start_offset + 1);
+      if ((input2 & 0xC0) != 0x80)
+	// return 0xFFFE;
+	return 0;
+
+      uint8_t input3 = peek_input (n_start_offset + 2);
+      if ((input3 & 0xC0) != 0x80)
+	// return 0xFFFE;
+	return 0;
+
+      uint8_t input4 = peek_input (n_start_offset + 3);
+      if ((input4 & 0xC0) != 0x80)
+	// return 0xFFFE;
+	return 0;
+
+      /*uint32_t output = ((input & 0x07) << 18) | ((input2 & 0x3F) << 12)
+			| ((input3 & 0x3F) << 6) | ((input4 & 0x3F) << 0);
+      return output;*/
+      return 4;
+    }
+  else
+    {
+      rust_error_at (get_current_location (),
+		     "invalid UTF-8 [THIRD] (too long)");
+      return 0;
+    }
+}
+
+// peeks the codepoint input at n codepoints ahead of current codepoint - try
+// not to use
+Codepoint
+Lexer::test_peek_codepoint_input (int n)
+{
+  int totalOffset = 0;
+
+  // add up all offsets into total offset? does this do what I want?
+  for (int i = 0; i < n; i++)
+    {
+      totalOffset += test_get_input_codepoint_n_length (totalOffset);
+    }
+  // issues: this would have (at least) O(n) lookup time, not O(1) like the
+  // rest?
+
+  // TODO: implement if still needed
+
+  // error out of function as it is not implemented
+  gcc_assert (1 == 0);
+  return {0};
+  /*
+	  uint8_t input = peek_input();
+
+	  if (input < 128) {
+	      // ascii -- 1 byte
+	      return input;
+	  } else if ((input & 0xC0) == 0x80) {
+	      // invalid (continuation; can't be first char)
+	      return 0xFFFE;
+	  } else if ((input & 0xE0) == 0xC0) {
+	      // 2 bytes
+	      uint8_t input2 = peek_input(1);
+	      if ((input2 & 0xC0) != 0x80)
+		  return 0xFFFE;
+
+	      uint32_t output = ((input & 0x1F) << 6) | ((input2 & 0x3F) << 0);
+	      return output;
+	  } else if ((input & 0xF0) == 0xE0) {
+	      // 3 bytes
+	      uint8_t input2 = peek_input(1);
+	      if ((input2 & 0xC0) != 0x80)
+		  return 0xFFFE;
+
+	      uint8_t input3 = peek_input(2);
+	      if ((input3 & 0xC0) != 0x80)
+		  return 0xFFFE;
+
+	      uint32_t output
+		= ((input & 0x0F) << 12) | ((input2 & 0x3F) << 6) | ((input3 &
+     0x3F) << 0); return output; } else if ((input & 0xF8) == 0xF0) {
+	      // 4 bytes
+	      uint8_t input2 = peek_input(1);
+	      if ((input2 & 0xC0) != 0x80)
+		  return 0xFFFE;
+
+	      uint8_t input3 = peek_input(2);
+	      if ((input3 & 0xC0) != 0x80)
+		  return 0xFFFE;
+
+	      uint8_t input4 = peek_input(3);
+	      if ((input4 & 0xC0) != 0x80)
+		  return 0xFFFE;
+
+	      uint32_t output = ((input & 0x07) << 18) | ((input2 & 0x3F) << 12)
+				| ((input3 & 0x3F) << 6) | ((input4 & 0x3F) <<
+     0); return output; } else { rust_error_at(get_current_location(), "invalid
+     UTF-8 (too long)"); return 0xFFFE;
+	  }*/
+}
+
+void
+Lexer::split_current_token (TokenId new_left, TokenId new_right)
+{
+  /* TODO: assert that this TokenId is a "simple token" like punctuation and not
+   * like "IDENTIFIER"? */
+  Location current_loc = peek_token ()->get_locus ();
+  TokenPtr new_left_tok = Token::make (new_left, current_loc);
+  TokenPtr new_right_tok = Token::make (new_right, current_loc + 1);
+
+  token_queue.replace_current_value (std::move (new_left_tok));
+  token_queue.insert (1, std::move (new_right_tok));
+}
+
+void
+Lexer::start_line (int current_line, int current_column)
+{
+  if (line_map)
+    line_map->start_line (current_line, current_column);
+}
+
+} // namespace Rust
diff --git a/gcc/rust/lex/rust-lex.h b/gcc/rust/lex/rust-lex.h
new file mode 100644
index 00000000000..d5a6c53719f
--- /dev/null
+++ b/gcc/rust/lex/rust-lex.h
@@ -0,0 +1,271 @@
+// 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_LEX_H
+#define RUST_LEX_H
+
+#include "rust-linemap.h"
+#include "rust-buffered-queue.h"
+#include "rust-token.h"
+
+namespace Rust {
+// Simple wrapper for FILE* that simplifies destruction.
+struct RAIIFile
+{
+private:
+  FILE *file;
+  const char *filename;
+
+  void close ()
+  {
+    if (file != nullptr && file != stdin)
+      fclose (file);
+  }
+
+public:
+  RAIIFile (const char *filename) : filename (filename)
+  {
+    if (strcmp (filename, "-") == 0)
+      file = stdin;
+    else
+      file = fopen (filename, "r");
+  }
+
+  /**
+   * Create a RAIIFile from an existing instance of FILE*
+   */
+  RAIIFile (FILE *raw, const char *filename = nullptr)
+    : file (raw), filename (filename)
+  {}
+
+  RAIIFile (const RAIIFile &other) = delete;
+  RAIIFile &operator= (const RAIIFile &other) = delete;
+
+  // have to specify setting file to nullptr, otherwise unintended fclose occurs
+  RAIIFile (RAIIFile &&other) : file (other.file), filename (other.filename)
+  {
+    other.file = nullptr;
+  }
+
+  RAIIFile &operator= (RAIIFile &&other)
+  {
+    close ();
+    file = other.file;
+    filename = other.filename;
+    other.file = nullptr;
+
+    return *this;
+  }
+
+  static RAIIFile create_error () { return RAIIFile (nullptr, nullptr); }
+
+  ~RAIIFile () { close (); }
+
+  FILE *get_raw () { return file; }
+  const char *get_filename () { return filename; }
+
+  bool ok () const { return file; }
+};
+
+class Lexer
+{
+private:
+  // Request new Location for current column in line_table
+  Location get_current_location ();
+
+  // Skips the current input char.
+  void skip_input ();
+  // Advances current input char to n + 1 chars ahead of current position.
+  void skip_input (int n);
+
+  // Returns char n chars ahead of current position.
+  int peek_input ();
+  // Peeks the current char.
+  int peek_input (int n);
+
+  // Classifies keyword (i.e. gets id for keyword).
+  TokenId classify_keyword (const std::string &str);
+
+  // Builds a token from the input queue.
+  TokenPtr build_token ();
+
+  std::tuple<std::string, int, bool> parse_in_decimal ();
+  std::pair<std::string, int> parse_in_exponent_part ();
+  std::pair<PrimitiveCoreType, int> parse_in_type_suffix ();
+  std::tuple<char, int, bool> parse_escape (char opening_char);
+  std::tuple<Codepoint, int, bool> parse_utf8_escape (char opening_char);
+  int parse_partial_string_continue ();
+  std::pair<long, int> parse_partial_hex_escape ();
+  std::pair<Codepoint, int> parse_partial_unicode_escape ();
+
+  int get_input_codepoint_length ();
+  int test_get_input_codepoint_n_length (int n_start_offset);
+  Codepoint peek_codepoint_input ();
+  Codepoint test_peek_codepoint_input (int n);
+  void skip_codepoint_input ();
+  void skip_broken_string_input (int current_char);
+
+  TokenPtr parse_byte_char (Location loc);
+  TokenPtr parse_byte_string (Location loc);
+  TokenPtr parse_raw_byte_string (Location loc);
+  TokenPtr parse_raw_identifier (Location loc);
+  TokenPtr parse_string (Location loc);
+  TokenPtr maybe_parse_raw_string (Location loc);
+  TokenPtr parse_raw_string (Location loc, int initial_hash_count);
+  TokenPtr parse_non_decimal_int_literals (Location loc);
+  TokenPtr parse_decimal_int_or_float (Location loc);
+  TokenPtr parse_char_or_lifetime (Location loc);
+  TokenPtr parse_identifier_or_keyword (Location loc);
+
+  template <typename IsDigitFunc>
+  TokenPtr parse_non_decimal_int_literal (Location loc,
+					  IsDigitFunc is_digit_func,
+					  std::string existent_str, int base);
+
+public:
+  // Construct lexer with input file and filename provided
+  Lexer (const char *filename, RAIIFile input, Linemap *linemap);
+
+  // Lex the contents of a string instead of a file
+  Lexer (const std::string &input);
+
+  // dtor
+  ~Lexer ();
+
+  // don't allow copy semantics (for now, at least)
+  Lexer (const Lexer &other) = delete;
+  Lexer &operator= (const Lexer &other) = delete;
+
+  // enable move semantics
+  Lexer (Lexer &&other) = default;
+  Lexer &operator= (Lexer &&other) = default;
+
+  // Returns token n tokens ahead of current position.
+  const_TokenPtr peek_token (int n) { return token_queue.peek (n); }
+  // Peeks the current token.
+  const_TokenPtr peek_token () { return peek_token (0); }
+
+  // Advances current token to n + 1 tokens ahead of current position.
+  void skip_token (int n) { token_queue.skip (n); }
+  // Skips the current token.
+  void skip_token () { skip_token (0); }
+
+  // Replaces the current token with a specified token.
+  void replace_current_token (TokenPtr replacement);
+  // FIXME: don't use anymore
+
+  /* Splits the current token into two. Intended for use with nested generics
+   * closes (i.e. T<U<X>> where >> is wrongly lexed as one token). Note that
+   * this will only work with "simple" tokens like punctuation. */
+  void split_current_token (TokenId new_left, TokenId new_right);
+
+  Linemap *get_line_map () { return line_map; }
+  std::string get_filename () { return std::string (input.get_filename ()); }
+
+private:
+  void start_line (int current_line, int current_column);
+
+  // File for use as input.
+  RAIIFile input;
+  // TODO is this actually required? could just have file storage in InputSource
+
+  // Current line number.
+  int current_line;
+  // Current column number.
+  int current_column;
+  // Current character.
+  int current_char;
+  // Line map.
+  Linemap *line_map;
+
+  /* Max column number that can be quickly allocated - higher may require
+   * allocating new linemap */
+  static const int max_column_hint = 80;
+
+  // Input source wrapper thing.
+  class InputSource
+  {
+  public:
+    virtual ~InputSource () {}
+
+    // Overload operator () to return next char from input stream.
+    virtual int next () = 0;
+  };
+
+  class FileInputSource : public InputSource
+  {
+  private:
+    // Input source file.
+    FILE *input;
+
+  public:
+    // Create new input source from file.
+    FileInputSource (FILE *input) : input (input) {}
+
+    int next () override { return fgetc (input); }
+  };
+
+  class BufferInputSource : public InputSource
+  {
+  private:
+    const std::string &buffer;
+    size_t offs;
+
+  public:
+    // Create new input source from file.
+    BufferInputSource (const std::string &b, size_t offset)
+      : buffer (b), offs (offset)
+    {}
+
+    int next () override
+    {
+      if (offs >= buffer.size ())
+	return EOF;
+
+      return buffer.at (offs++);
+    }
+  };
+
+  // The input source for the lexer.
+  // InputSource input_source;
+  // Input file queue.
+  std::unique_ptr<InputSource> raw_input_source;
+  buffered_queue<int, InputSource &> input_queue;
+
+  // Token source wrapper thing.
+  struct TokenSource
+  {
+    // The lexer object that will use this TokenSource.
+    Lexer *lexer;
+
+    // Create a new TokenSource with given lexer.
+    TokenSource (Lexer *parLexer) : lexer (parLexer) {}
+
+    // Overload operator () to build token in lexer.
+    TokenPtr next () { return lexer->build_token (); }
+  };
+
+  // The token source for the lexer.
+  // TokenSource token_source;
+  // Token stream queue.
+  buffered_queue<std::shared_ptr<Token>, TokenSource> token_queue;
+};
+
+} // namespace Rust
+
+#endif
diff --git a/gcc/rust/lex/rust-token.cc b/gcc/rust/lex/rust-token.cc
new file mode 100644
index 00000000000..68313c20b1c
--- /dev/null
+++ b/gcc/rust/lex/rust-token.cc
@@ -0,0 +1,135 @@
+// 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/>.
+
+#include "rust-token.h"
+
+#include "rust-diagnostics.h" // for error_at
+
+namespace Rust {
+// Hackily defined way to get token description for enum value using x-macros
+const char *
+get_token_description (TokenId id)
+{
+  switch (id)
+    {
+#define RS_TOKEN(name, descr)                                                  \
+  case name:                                                                   \
+    return descr;
+#define RS_TOKEN_KEYWORD(x, y) RS_TOKEN (x, y)
+      RS_TOKEN_LIST
+#undef RS_TOKEN_KEYWORD
+#undef RS_TOKEN
+    default:
+      gcc_unreachable ();
+    }
+}
+
+/* Hackily defined way to get token description as a string for enum value using
+ * x-macros */
+const char *
+token_id_to_str (TokenId id)
+{
+  switch (id)
+    {
+#define RS_TOKEN(name, _)                                                      \
+  case name:                                                                   \
+    return #name;
+#define RS_TOKEN_KEYWORD(x, y) RS_TOKEN (x, y)
+      RS_TOKEN_LIST
+#undef RS_TOKEN_KEYWORD
+#undef RS_TOKEN
+    default:
+      gcc_unreachable ();
+    }
+}
+
+const char *
+get_type_hint_string (PrimitiveCoreType type)
+{
+  switch (type)
+    {
+    case CORETYPE_BOOL:
+      return "bool";
+    case CORETYPE_CHAR:
+      return "char";
+    case CORETYPE_STR:
+      return "str";
+    // case CORETYPE_INT:
+    case CORETYPE_ISIZE:
+      return "isize";
+    // case CORETYPE_UINT:
+    case CORETYPE_USIZE:
+      return "usize";
+    case CORETYPE_F32:
+      return "f32";
+    case CORETYPE_F64:
+      return "f64";
+    case CORETYPE_I8:
+      return "i8";
+    case CORETYPE_I16:
+      return "i16";
+    case CORETYPE_I32:
+      return "i32";
+    case CORETYPE_I64:
+      return "i64";
+    case CORETYPE_I128:
+      return "i128";
+    case CORETYPE_U8:
+      return "u8";
+    case CORETYPE_U16:
+      return "u16";
+    case CORETYPE_U32:
+      return "u32";
+    case CORETYPE_U64:
+      return "u64";
+    case CORETYPE_U128:
+      return "u128";
+    case CORETYPE_PURE_DECIMAL:
+      return "pure_decimal";
+    case CORETYPE_UNKNOWN:
+    default:
+      return "unknown";
+    }
+}
+
+const char *
+Token::get_type_hint_str () const
+{
+  return get_type_hint_string (type_hint);
+}
+
+const std::string &
+Token::get_str () const
+{
+  // FIXME: attempt to return null again
+  // gcc_assert(str != NULL);
+
+  // HACK: allow referencing an empty string
+  static const std::string empty = "";
+
+  if (str == NULL)
+    {
+      rust_error_at (get_locus (),
+		     "attempted to get string for %<%s%>, which has no string. "
+		     "returning empty string instead",
+		     get_token_description ());
+      return empty;
+    }
+  return *str;
+}
+} // namespace Rust
diff --git a/gcc/rust/lex/rust-token.h b/gcc/rust/lex/rust-token.h
new file mode 100644
index 00000000000..3fa46a2cebe
--- /dev/null
+++ b/gcc/rust/lex/rust-token.h
@@ -0,0 +1,455 @@
+// 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_TOKEN_H
+#define RUST_TOKEN_H
+
+#include "rust-linemap.h"
+#include "rust-codepoint.h"
+
+// order: config, system, coretypes, input
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "input.h"
+
+namespace Rust {
+// "Primitive core types" in Rust - the different int and float types, as well
+// as some others
+enum PrimitiveCoreType
+{
+  CORETYPE_UNKNOWN,
+  // named primitives
+  CORETYPE_BOOL,
+  CORETYPE_CHAR,
+  CORETYPE_STR,
+  // okay technically int and uint are arch-dependent (pointer size)
+  CORETYPE_INT,
+  CORETYPE_UINT,
+  // numbered number primitives
+  CORETYPE_F32,
+  CORETYPE_F64,
+  CORETYPE_I8,
+  CORETYPE_I16,
+  CORETYPE_I32,
+  CORETYPE_I64,
+  CORETYPE_I128,
+  CORETYPE_U8,
+  CORETYPE_U16,
+  CORETYPE_U32,
+  CORETYPE_U64,
+  CORETYPE_U128,
+  // Pure decimals are used for tuple index.
+  // Also means there is no type hint.
+  CORETYPE_PURE_DECIMAL,
+  // arch-dependent pointer sizes
+  CORETYPE_ISIZE = CORETYPE_INT,
+  CORETYPE_USIZE = CORETYPE_UINT
+};
+
+// RS_TOKEN(name, description)
+// RS_TOKEN_KEYWORD(name, identifier)
+//
+// Keep RS_TOKEN_KEYWORD sorted
+
+/* note that abstract, async, become, box, do, final, macro, override, priv,
+ * try, typeof, unsized, virtual, and yield are unused */
+#define RS_TOKEN_LIST                                                          \
+  RS_TOKEN (FIRST_TOKEN, "<first-token-marker>")                               \
+  RS_TOKEN (END_OF_FILE, "end of file")                                        \
+  RS_TOKEN (EXCLAM, "!")                                                       \
+  RS_TOKEN (NOT_EQUAL, "!=")                                                   \
+  RS_TOKEN (PERCENT, "%")                                                      \
+  RS_TOKEN (PERCENT_EQ, "%=")                                                  \
+  RS_TOKEN (AMP, "&")                                                          \
+  RS_TOKEN (AMP_EQ, "&=")                                                      \
+  RS_TOKEN (LOGICAL_AND, "&&")                                                 \
+  RS_TOKEN (ASTERISK, "*")                                                     \
+  RS_TOKEN (ASTERISK_EQ, "*=")                                                 \
+  RS_TOKEN (PLUS, "+")                                                         \
+  RS_TOKEN (PLUS_EQ, "+=")                                                     \
+  RS_TOKEN (COMMA, ",")                                                        \
+  RS_TOKEN (MINUS, "-")                                                        \
+  RS_TOKEN (MINUS_EQ, "-=")                                                    \
+  RS_TOKEN (RETURN_TYPE, "->")                                                 \
+  RS_TOKEN (DOT, ".")                                                          \
+  RS_TOKEN (DOT_DOT, "..")                                                     \
+  RS_TOKEN (DOT_DOT_EQ, "..=")                                                 \
+  RS_TOKEN (ELLIPSIS, "...")                                                   \
+  RS_TOKEN (DIV, "/")                                                          \
+  RS_TOKEN (DIV_EQ, "/=")                                                      \
+  RS_TOKEN (COLON, ":")                                                        \
+  RS_TOKEN (SEMICOLON, ";")                                                    \
+  RS_TOKEN (LEFT_SHIFT, "<<")                                                  \
+  RS_TOKEN (LEFT_SHIFT_EQ, "<<=")                                              \
+  RS_TOKEN (LEFT_ANGLE, "<")                                                   \
+  RS_TOKEN (LESS_OR_EQUAL, "<=")                                               \
+  RS_TOKEN (EQUAL, "=")                                                        \
+  RS_TOKEN (EQUAL_EQUAL, "==")                                                 \
+  RS_TOKEN (MATCH_ARROW, "=>")                                                 \
+  RS_TOKEN (RIGHT_ANGLE, ">")                                                  \
+  RS_TOKEN (GREATER_OR_EQUAL, ">=")                                            \
+  RS_TOKEN (RIGHT_SHIFT, ">>")                                                 \
+  RS_TOKEN (RIGHT_SHIFT_EQ, ">>=")                                             \
+  RS_TOKEN (PATTERN_BIND, "@")                                                 \
+  RS_TOKEN (TILDE, "~")                                                        \
+  RS_TOKEN (BACKSLASH, "\\")                                                   \
+  RS_TOKEN (BACKTICK, "`")                                                     \
+  RS_TOKEN (CARET, "^")                                                        \
+  RS_TOKEN (CARET_EQ, "^=")                                                    \
+  RS_TOKEN (PIPE, "|")                                                         \
+  RS_TOKEN (PIPE_EQ, "|=")                                                     \
+  RS_TOKEN (OR, "||")                                                          \
+  RS_TOKEN (QUESTION_MARK, "?")                                                \
+  RS_TOKEN (HASH, "#")                                                         \
+  /* from here on, dodgy and may not be correct. not operators and may be      \
+   * symbols */                                                                \
+  /* RS_TOKEN(SPACE, " ") probably too dodgy */                                \
+  /* RS_TOKEN(NEWLINE, "\n")*/                                                 \
+  RS_TOKEN (SCOPE_RESOLUTION, "::") /* dodgy */                                \
+  RS_TOKEN (SINGLE_QUOTE, "'") /* should i differentiate from lifetime? */     \
+  RS_TOKEN (DOUBLE_QUOTE, "\"")                                                \
+  RS_TOKEN (UNDERSCORE,                                                        \
+	    "_") /* TODO: treat as reserved word like mrustc instead? */       \
+  RS_TOKEN (IDENTIFIER, "identifier")                                          \
+  RS_TOKEN (INT_LITERAL,                                                       \
+	    "integer literal") /* do different int and float types need        \
+				  different literal types? */                  \
+  RS_TOKEN (FLOAT_LITERAL, "float literal")                                    \
+  RS_TOKEN (STRING_LITERAL, "string literal")                                  \
+  RS_TOKEN (CHAR_LITERAL, "character literal")                                 \
+  RS_TOKEN (BYTE_STRING_LITERAL, "byte string literal")                        \
+  RS_TOKEN (BYTE_CHAR_LITERAL, "byte character literal")                       \
+  RS_TOKEN (LIFETIME, "lifetime") /* TODO: improve token type */               \
+  /* Have "interpolated" tokens (whatever that means)? identifer, path, type,  \
+   * pattern, */                                                               \
+  /* expression, statement, block, meta, item in mrustc (but not directly in   \
+   * lexer). */                                                                \
+  RS_TOKEN (LEFT_PAREN, "(")                                                   \
+  RS_TOKEN (RIGHT_PAREN, ")")                                                  \
+  RS_TOKEN (LEFT_CURLY, "{")                                                   \
+  RS_TOKEN (RIGHT_CURLY, "}")                                                  \
+  RS_TOKEN (LEFT_SQUARE, "[")                                                  \
+  RS_TOKEN (RIGHT_SQUARE, "]")                                                 \
+  /* Macros */                                                                 \
+  RS_TOKEN (DOLLAR_SIGN, "$")                                                  \
+  /* Doc Comments */                                                           \
+  RS_TOKEN (INNER_DOC_COMMENT, "#![doc]")                                      \
+  RS_TOKEN (OUTER_DOC_COMMENT, "#[doc]")                                       \
+  /* have "weak" union and 'static keywords? */                                \
+                                                                               \
+  RS_TOKEN_KEYWORD (ABSTRACT, "abstract") /* unused */                         \
+  RS_TOKEN_KEYWORD (AS, "as")                                                  \
+  RS_TOKEN_KEYWORD (ASYNC, "async")   /* unused */                             \
+  RS_TOKEN_KEYWORD (BECOME, "become") /* unused */                             \
+  RS_TOKEN_KEYWORD (BOX, "box")	      /* unused */                             \
+  RS_TOKEN_KEYWORD (BREAK, "break")                                            \
+  RS_TOKEN_KEYWORD (CONST, "const")                                            \
+  RS_TOKEN_KEYWORD (CONTINUE, "continue")                                      \
+  RS_TOKEN_KEYWORD (CRATE, "crate")                                            \
+  /* FIXME: Do we need to add $crate (DOLLAR_CRATE) as a reserved kw? */       \
+  RS_TOKEN_KEYWORD (DO, "do") /* unused */                                     \
+  RS_TOKEN_KEYWORD (DYN, "dyn")                                                \
+  RS_TOKEN_KEYWORD (ELSE, "else")                                              \
+  RS_TOKEN_KEYWORD (ENUM_TOK, "enum")                                          \
+  RS_TOKEN_KEYWORD (EXTERN_TOK, "extern")                                      \
+  RS_TOKEN_KEYWORD (FALSE_LITERAL, "false")                                    \
+  RS_TOKEN_KEYWORD (FINAL_TOK, "final") /* unused */                           \
+  RS_TOKEN_KEYWORD (FN_TOK, "fn")                                              \
+  RS_TOKEN_KEYWORD (FOR, "for")                                                \
+  RS_TOKEN_KEYWORD (IF, "if")                                                  \
+  RS_TOKEN_KEYWORD (IMPL, "impl")                                              \
+  RS_TOKEN_KEYWORD (IN, "in")                                                  \
+  RS_TOKEN_KEYWORD (LET, "let")                                                \
+  RS_TOKEN_KEYWORD (LOOP, "loop")                                              \
+  RS_TOKEN_KEYWORD (MACRO, "macro") /* unused */                               \
+  RS_TOKEN_KEYWORD (MATCH_TOK, "match")                                        \
+  RS_TOKEN_KEYWORD (MOD, "mod")                                                \
+  RS_TOKEN_KEYWORD (MOVE, "move")                                              \
+  RS_TOKEN_KEYWORD (MUT, "mut")                                                \
+  RS_TOKEN_KEYWORD (OVERRIDE_TOK, "override") /* unused */                     \
+  RS_TOKEN_KEYWORD (PRIV, "priv")	      /* unused */                     \
+  RS_TOKEN_KEYWORD (PUB, "pub")                                                \
+  RS_TOKEN_KEYWORD (REF, "ref")                                                \
+  RS_TOKEN_KEYWORD (RETURN_TOK, "return")                                      \
+  RS_TOKEN_KEYWORD (SELF_ALIAS,                                                \
+		    "Self") /* mrustc does not treat this as a reserved word*/ \
+  RS_TOKEN_KEYWORD (SELF, "self")                                              \
+  RS_TOKEN_KEYWORD (STATIC_TOK, "static")                                      \
+  RS_TOKEN_KEYWORD (STRUCT_TOK, "struct")                                      \
+  RS_TOKEN_KEYWORD (SUPER, "super")                                            \
+  RS_TOKEN_KEYWORD (TRAIT, "trait")                                            \
+  RS_TOKEN_KEYWORD (TRUE_LITERAL, "true")                                      \
+  RS_TOKEN_KEYWORD (TRY, "try") /* unused */                                   \
+  RS_TOKEN_KEYWORD (TYPE, "type")                                              \
+  RS_TOKEN_KEYWORD (TYPEOF, "typeof") /* unused */                             \
+  RS_TOKEN_KEYWORD (UNSAFE, "unsafe")                                          \
+  RS_TOKEN_KEYWORD (UNSIZED, "unsized") /* unused */                           \
+  RS_TOKEN_KEYWORD (USE, "use")                                                \
+  RS_TOKEN_KEYWORD (VIRTUAL, "virtual") /* unused */                           \
+  RS_TOKEN_KEYWORD (WHERE, "where")                                            \
+  RS_TOKEN_KEYWORD (WHILE, "while")                                            \
+  RS_TOKEN_KEYWORD (YIELD, "yield") /* unused */                               \
+                                                                               \
+  RS_TOKEN (LAST_TOKEN, "<last-token-marker>")
+
+// Contains all token types. Crappy implementation via x-macros.
+enum TokenId
+{
+#define RS_TOKEN(name, _) name,
+#define RS_TOKEN_KEYWORD(x, y) RS_TOKEN (x, y)
+  RS_TOKEN_LIST
+#undef RS_TOKEN_KEYWORD
+#undef RS_TOKEN
+};
+
+// dodgy "TokenPtr" declaration with Token forward declaration
+class Token;
+// A smart pointer (shared_ptr) to Token.
+typedef std::shared_ptr<Token> TokenPtr;
+// A smart pointer (shared_ptr) to a constant Token.
+typedef std::shared_ptr<const Token> const_TokenPtr;
+
+// Hackily defined way to get token description for enum value using x-macros
+const char *
+get_token_description (TokenId id);
+/* Hackily defined way to get token description as a string for enum value using
+ * x-macros */
+const char *
+token_id_to_str (TokenId id);
+// Get type hint description as a string.
+const char *
+get_type_hint_string (PrimitiveCoreType type);
+
+// Represents a single token. Create using factory static methods.
+class Token
+{
+private:
+  // Token kind.
+  TokenId token_id;
+  // Token location.
+  Location locus;
+  // Associated text (if any) of token.
+  std::unique_ptr<std::string> str;
+  // TODO: maybe remove issues and just store std::string as value?
+  /* Type hint for token based on lexer data (e.g. type suffix). Does not exist
+   * for most tokens. */
+  PrimitiveCoreType type_hint;
+
+  // Token constructor from token id and location. Has a null string.
+  Token (TokenId token_id, Location location)
+    : token_id (token_id), locus (location), str (nullptr),
+      type_hint (CORETYPE_UNKNOWN)
+  {}
+
+  // Token constructor from token id, location, and a string.
+  Token (TokenId token_id, Location location, std::string &&paramStr)
+    : token_id (token_id), locus (location),
+      str (new std::string (std::move (paramStr))), type_hint (CORETYPE_UNKNOWN)
+  {}
+
+  // Token constructor from token id, location, and a char.
+  Token (TokenId token_id, Location location, char paramChar)
+    : token_id (token_id), locus (location),
+      str (new std::string (1, paramChar)), type_hint (CORETYPE_UNKNOWN)
+  {}
+
+  // Token constructor from token id, location, and a "codepoint".
+  Token (TokenId token_id, Location location, Codepoint paramCodepoint)
+    : token_id (token_id), locus (location),
+      str (new std::string (paramCodepoint.as_string ())),
+      type_hint (CORETYPE_UNKNOWN)
+  {}
+
+  // Token constructor from token id, location, a string, and type hint.
+  Token (TokenId token_id, Location location, std::string &&paramStr,
+	 PrimitiveCoreType parType)
+    : token_id (token_id), locus (location),
+      str (new std::string (std::move (paramStr))), type_hint (parType)
+  {}
+
+public:
+  // No default constructor.
+  Token () = delete;
+  // Do not copy/assign tokens.
+  Token (const Token &) = delete;
+  Token &operator= (const Token &) = delete;
+
+  // Allow moving tokens.
+  Token (Token &&other) = default;
+  Token &operator= (Token &&other) = default;
+
+  ~Token () = default;
+
+  /* TODO: make_shared (which saves a heap allocation) does not work with the
+   * private constructor */
+
+  // Makes and returns a new TokenPtr (with null string).
+  static TokenPtr make (TokenId token_id, Location locus)
+  {
+    // return std::make_shared<Token> (token_id, locus);
+    return TokenPtr (new Token (token_id, locus));
+  }
+
+  // Makes and returns a new TokenPtr of type IDENTIFIER.
+  static TokenPtr make_identifier (Location locus, std::string &&str)
+  {
+    // return std::make_shared<Token> (IDENTIFIER, locus, str);
+    return TokenPtr (new Token (IDENTIFIER, locus, std::move (str)));
+  }
+
+  // Makes and returns a new TokenPtr of type INT_LITERAL.
+  static TokenPtr make_int (Location locus, std::string &&str,
+			    PrimitiveCoreType type_hint = CORETYPE_UNKNOWN)
+  {
+    // return std::make_shared<Token> (INT_LITERAL, locus, str, type_hint);
+    return TokenPtr (
+      new Token (INT_LITERAL, locus, std::move (str), type_hint));
+  }
+
+  // Makes and returns a new TokenPtr of type FLOAT_LITERAL.
+  static TokenPtr make_float (Location locus, std::string &&str,
+			      PrimitiveCoreType type_hint = CORETYPE_UNKNOWN)
+  {
+    // return std::make_shared<Token> (FLOAT_LITERAL, locus, str, type_hint);
+    return TokenPtr (
+      new Token (FLOAT_LITERAL, locus, std::move (str), type_hint));
+  }
+
+  // Makes and returns a new TokenPtr of type STRING_LITERAL.
+  static TokenPtr make_string (Location locus, std::string &&str)
+  {
+    // return std::make_shared<Token> (STRING_LITERAL, locus, str,
+    // CORETYPE_STR);
+    return TokenPtr (
+      new Token (STRING_LITERAL, locus, std::move (str), CORETYPE_STR));
+  }
+
+  // Makes and returns a new TokenPtr of type CHAR_LITERAL.
+  static TokenPtr make_char (Location locus, Codepoint char_lit)
+  {
+    // return std::make_shared<Token> (CHAR_LITERAL, locus, char_lit);
+    return TokenPtr (new Token (CHAR_LITERAL, locus, char_lit));
+  }
+
+  // Makes and returns a new TokenPtr of type BYTE_CHAR_LITERAL.
+  static TokenPtr make_byte_char (Location locus, char byte_char)
+  {
+    // return std::make_shared<Token> (BYTE_CHAR_LITERAL, locus, byte_char);
+    return TokenPtr (new Token (BYTE_CHAR_LITERAL, locus, byte_char));
+  }
+
+  // Makes and returns a new TokenPtr of type BYTE_STRING_LITERAL (fix).
+  static TokenPtr make_byte_string (Location locus, std::string &&str)
+  {
+    // return std::make_shared<Token> (BYTE_STRING_LITERAL, locus, str);
+    return TokenPtr (new Token (BYTE_STRING_LITERAL, locus, std::move (str)));
+  }
+
+  // Makes and returns a new TokenPtr of type INNER_DOC_COMMENT.
+  static TokenPtr make_inner_doc_comment (Location locus, std::string &&str)
+  {
+    return TokenPtr (new Token (INNER_DOC_COMMENT, locus, std::move (str)));
+  }
+
+  // Makes and returns a new TokenPtr of type OUTER_DOC_COMMENT.
+  static TokenPtr make_outer_doc_comment (Location locus, std::string &&str)
+  {
+    return TokenPtr (new Token (OUTER_DOC_COMMENT, locus, std::move (str)));
+  }
+
+  // Makes and returns a new TokenPtr of type LIFETIME.
+  static TokenPtr make_lifetime (Location locus, std::string &&str)
+  {
+    // return std::make_shared<Token> (LIFETIME, locus, str);
+    return TokenPtr (new Token (LIFETIME, locus, std::move (str)));
+  }
+
+  // Gets id of the token.
+  TokenId get_id () const { return token_id; }
+
+  // Gets location of the token.
+  Location get_locus () const { return locus; }
+
+  // Gets string description of the token.
+  const std::string &
+  get_str () const; /*{
+// FIXME: put in header again when fix null problem
+//gcc_assert(str != nullptr);
+if (str == nullptr) {
+error_at(get_locus(), "attempted to get string for '%s', which has no string.
+returning empty string instead.", get_token_description()); return "";
+}
+return *str;
+}*/
+
+  // Gets token's type hint info.
+  PrimitiveCoreType get_type_hint () const
+  {
+    return type_hint == CORETYPE_PURE_DECIMAL ? CORETYPE_UNKNOWN : type_hint;
+  }
+
+  // diagnostics (error reporting)
+  const char *get_token_description () const
+  {
+    return Rust::get_token_description (token_id);
+  }
+
+  // debugging
+  const char *token_id_to_str () const
+  {
+    return Rust::token_id_to_str (token_id);
+  }
+
+  // debugging
+  const char *get_type_hint_str () const;
+
+  /* Returns whether the token is a literal of any type (int, float, char,
+   * string, byte char, byte string). */
+  bool is_literal () const
+  {
+    switch (token_id)
+      {
+      case INT_LITERAL:
+      case FLOAT_LITERAL:
+      case CHAR_LITERAL:
+      case STRING_LITERAL:
+      case BYTE_CHAR_LITERAL:
+      case BYTE_STRING_LITERAL:
+	return true;
+      default:
+	return false;
+      }
+  }
+
+  /* Returns whether the token actually has a string (regardless of whether it
+   * should or not). */
+  bool has_str () const { return str != nullptr; }
+
+  // Returns whether the token should have a string.
+  bool should_have_str () const
+  {
+    return is_literal () || token_id == IDENTIFIER || token_id == LIFETIME;
+  }
+
+  // Returns whether the token is a pure decimal int literal
+  bool is_pure_decimal () const { return type_hint == CORETYPE_PURE_DECIMAL; }
+};
+} // namespace Rust
+
+#endif
diff --git a/gcc/rust/rust-buffered-queue.h b/gcc/rust/rust-buffered-queue.h
new file mode 100644
index 00000000000..afcc4670cac
--- /dev/null
+++ b/gcc/rust/rust-buffered-queue.h
@@ -0,0 +1,204 @@
+// 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_BUFFERED_QUEUE_H
+#define RUST_BUFFERED_QUEUE_H
+
+#include "rust-system.h"
+
+namespace Rust {
+/* Buffered queue implementation. Items are of type T, queue source is of type
+ * Source. Note that this is owning of the source. */
+template <typename T, typename Source> class buffered_queue
+{
+public:
+  // Construct empty queue from Source src.
+  buffered_queue (Source src) : source (src), start (0), end (0), buffer () {}
+
+  /* disable copying (since source is probably non-copyable)
+   * TODO is this actually a good idea? If source is non-copyable, it would
+   * just delete the copy constructor anyway.*/
+  buffered_queue (const buffered_queue &other) = delete;
+  buffered_queue &operator= (const buffered_queue &other) = delete;
+
+  // enable moving
+  buffered_queue (buffered_queue &&other) = default;
+  buffered_queue &operator= (buffered_queue &&other) = default;
+
+  // Returns token at position start + n (i.e. n tokens ahead).
+  T peek (int n)
+  {
+    // n should not be behind
+    rust_assert (n >= 0);
+
+    int num_queued_items = end - start;
+    int num_items_required = n + 1;
+
+    // if required items go past end of queue, add them to queue
+    if (num_items_required > num_queued_items)
+      {
+	int num_items_to_read = num_items_required - num_queued_items;
+
+	/* if queue length + extra items is larger than buffer size, expand
+	 * buffer */
+	if (end + num_items_to_read > (int) buffer.size ())
+	  {
+	    // Resize the buffer by 1.5x
+	    int new_size = (buffer.size () + num_items_to_read);
+	    new_size += (new_size >> 1);
+
+	    // old method:
+	    /*
+		  // create new queue buffer with new size
+		  std::vector<T> new_queue (new_size);
+		  std::copy (buffer.begin () + start, buffer.begin () + end,
+			     new_queue.begin ());
+		  start = 0;
+		  end = num_queued_items;
+		  // TODO: would move be better here? optimisation for move with
+		  // shared pointer?
+
+		  // swap member buffer and new queue buffer
+		  std::swap (buffer, new_queue);
+	    */
+
+	    // TODO: determine overhead of this approach vs copy. Should be
+	    // lower.
+	    std::vector<T> new_queue;
+	    new_queue.reserve (new_size);
+	    new_queue.insert (new_queue.begin (),
+			      std::make_move_iterator (buffer.begin () + start),
+			      std::make_move_iterator (buffer.begin () + end));
+	    start = 0;
+	    end = num_queued_items;
+	    // fill up rest of vector with junk so that indexing can work
+	    new_queue.insert (new_queue.begin () + end,
+			      new_size - new_queue.size (), T ());
+
+	    buffer = std::move (new_queue);
+	    /* this should be best method - std::move(range) would have
+	     * allocation problems; initial construction would require
+	     * reallocation upon resizing */
+
+	    // validate that buffer is large enough now
+	    rust_assert (end + num_items_to_read <= (int) buffer.size ());
+	  }
+
+	/* iterate through buffer and invoke operator () on source on values
+	 * past original end */
+	for (int i = 0; i < num_items_to_read; i++)
+	  buffer[end + i] = source.next ();
+
+	// move end based on additional items added
+	end += num_items_to_read;
+      }
+
+    rust_assert (0 <= start);
+    rust_assert (start <= end);
+    rust_assert (end <= (int) buffer.size ());
+
+    rust_assert (start + n < end);
+
+    // return value at start + n in buffer
+    return buffer[start + n];
+  }
+
+  /* TODO: add faster peek current token to remove overhead of conditional
+   * branches? */
+
+  // Advances start by n + 1.
+  void skip (int n)
+  {
+    // Call peek to ensure requested n is actually in queue.
+    peek (n);
+
+    // Clear queue values from start to n (inclusive).
+    for (int i = 0; i < (n + 1); i++)
+      buffer[start + i] = T ();
+
+    // Move start forward by n + 1.
+    start += (n + 1);
+
+    // Ensure start is not impossible somehow
+    rust_assert (0 <= start);
+    rust_assert (start <= end);
+
+    // Compact buffer if empty
+    if (start == end)
+      start = end = 0;
+  }
+
+  /* Inserts element at front of vector. Really dirty hack with terrible
+   * performance, only use when really needed. */
+  void insert_at_front (T elem_to_insert)
+  {
+    // TODO: test as this may not work properly
+
+    // Insert actual element in buffer at start.
+    buffer.insert (buffer.begin (), elem_to_insert);
+
+    /* Increase the end number since added element means all others have shifted
+     * one along */
+    end++;
+  }
+
+  // Insert at arbitrary position (attempt)
+  void insert (int index, T elem_to_insert)
+  {
+    // TODO: test as this may not work properly
+
+    // n should not be behind
+    rust_assert (index >= 0);
+
+    // call peek to ensure that the items behind this (at least) are in queue
+    if (index >= 1)
+      peek (index - 1);
+    else
+      peek (index);
+
+    buffer.insert (buffer.begin () + start + index, std::move (elem_to_insert));
+
+    end++;
+  }
+
+  // Replaces the current value in the buffer. Total HACK.
+  void replace_current_value (T replacement)
+  {
+    // call peek to ensure value exists
+    peek (0);
+
+    buffer[start] = std::move (replacement);
+
+    // don't move start or end
+  }
+
+private:
+  // Source of tokens for queue.
+  Source source;
+
+  // Begin of range in buffer, inclusive.
+  int start;
+  // End of range in buffer, exclusive.
+  int end;
+
+  // Queue buffer.
+  std::vector<T> buffer;
+};
+} // namespace Rust
+
+#endif
-- 
2.25.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [PATCH Rust front-end v2 10/37] gccrs: Add Parser for Rust front-end
  2022-08-24 11:59 Rust frontend patches v2 herron.philip
                   ` (8 preceding siblings ...)
  2022-08-24 11:59 ` [PATCH Rust front-end v2 09/37] gccrs: Add Lexer for Rust front-end herron.philip
@ 2022-08-24 11:59 ` herron.philip
  2022-08-24 11:59 ` [PATCH Rust front-end v2 11/37] gccrs: Add expansion pass for the " herron.philip
                   ` (27 subsequent siblings)
  37 siblings, 0 replies; 59+ messages in thread
From: herron.philip @ 2022-08-24 11:59 UTC (permalink / raw)
  To: gcc-patches; +Cc: gcc-rust, The Other, Philip Herron

From: The Other <simplytheother@gmail.com>

This is a Pratt style parser for Rust implementing all of the AST. The
rust-parser-impl.h is the implementation of the parser which is a template
so we can give it any ManagedTokenSource and avoid virtual calls. The down
side is it takes time to compile when used.

see: https://en.wikipedia.org/wiki/Operator-precedence_parser#Pratt_parsing

Co-authored-by: Philip Herron <philip.herron@embecosm.com>
Co-authored-by: Arthur Cohen <arthur.cohen@embecosm.com
---
 gcc/rust/parse/rust-cfg-parser.cc |   111 +
 gcc/rust/parse/rust-cfg-parser.h  |    54 +
 gcc/rust/parse/rust-parse-impl.h  | 14927 ++++++++++++++++++++++++++++
 gcc/rust/parse/rust-parse.cc      |   328 +
 gcc/rust/parse/rust-parse.h       |   732 ++
 5 files changed, 16152 insertions(+)
 create mode 100644 gcc/rust/parse/rust-cfg-parser.cc
 create mode 100644 gcc/rust/parse/rust-cfg-parser.h
 create mode 100644 gcc/rust/parse/rust-parse-impl.h
 create mode 100644 gcc/rust/parse/rust-parse.cc
 create mode 100644 gcc/rust/parse/rust-parse.h

diff --git a/gcc/rust/parse/rust-cfg-parser.cc b/gcc/rust/parse/rust-cfg-parser.cc
new file mode 100644
index 00000000000..00693c4cc75
--- /dev/null
+++ b/gcc/rust/parse/rust-cfg-parser.cc
@@ -0,0 +1,111 @@
+#include "rust-cfg-parser.h"
+#include "rust-lex.h"
+#include "rust-parse.h"
+#include "rust-session-manager.h"
+#include "selftest.h"
+
+namespace Rust {
+bool
+parse_cfg_option (std::string &input, std::string &key, std::string &value)
+{
+  key.clear ();
+  value.clear ();
+
+  auto lexer = Lexer (input);
+  auto parser = Parser<Lexer> (lexer);
+
+  auto token = parser.peek_current_token ();
+  if (token->get_id () != IDENTIFIER)
+    {
+      return false;
+    }
+
+  key = token->get_str ();
+
+  rust_assert (parser.skip_token (IDENTIFIER));
+  token = parser.peek_current_token ();
+
+  switch (token->get_id ())
+    {
+    case END_OF_FILE:
+      // we're done parsing, we had a valid key, return happily
+      return true;
+    case EQUAL:
+      // We have an equal sign: Skip the token and parse an identifier
+      {
+	rust_assert (parser.skip_token (EQUAL));
+
+	auto value_expr = parser.parse_literal_expr ();
+	// We had an equal sign but no value, error out
+	if (!value_expr)
+	  return false;
+
+	if (value_expr->get_lit_type () != AST::Literal::LitType::STRING)
+	  return false;
+
+	value = value_expr->get_literal ().as_string ();
+	return true;
+      }
+    default:
+      return false;
+    }
+}
+} // namespace Rust
+
+#if CHECKING_P
+
+namespace selftest {
+
+void
+rust_cfg_parser_test (void)
+{
+  std::string key;
+  std::string value;
+
+  auto input = std::string ("key_no_value");
+
+  ASSERT_TRUE (Rust::parse_cfg_option (input, key, value));
+  ASSERT_EQ (key, "key_no_value");
+  ASSERT_TRUE (value.empty ());
+
+  input = std::string ("k=\"v\"");
+
+  ASSERT_TRUE (Rust::parse_cfg_option (input, key, value));
+  ASSERT_EQ (key, "k");
+  ASSERT_EQ (value, "v");
+
+  // values should be between double quotes
+  input = std::string ("k=v");
+  ASSERT_FALSE (Rust::parse_cfg_option (input, key, value));
+
+  // No value is an error if there is an equal sign
+  input = std::string ("k=");
+  ASSERT_FALSE (Rust::parse_cfg_option (input, key, value));
+
+  // No key is an error
+  input = std::string ("=");
+  ASSERT_FALSE (Rust::parse_cfg_option (input, key, value));
+
+  input = std::string ("=value");
+  ASSERT_FALSE (Rust::parse_cfg_option (input, key, value));
+
+  // values that are not string literals are an error
+  input = std::string ("key=b\"a\"");
+  ASSERT_FALSE (Rust::parse_cfg_option (input, key, value));
+
+  input = std::string ("key='v'");
+  ASSERT_FALSE (Rust::parse_cfg_option (input, key, value));
+
+  input = std::string ("key=155");
+  ASSERT_FALSE (Rust::parse_cfg_option (input, key, value));
+
+  input = std::string ("key=3.14");
+  ASSERT_FALSE (Rust::parse_cfg_option (input, key, value));
+
+  // kebab case is not valid for an identifier
+  input = std::string ("key-no-value");
+  ASSERT_FALSE (Rust::parse_cfg_option (input, key, value));
+}
+} // namespace selftest
+
+#endif // CHECKING_P
diff --git a/gcc/rust/parse/rust-cfg-parser.h b/gcc/rust/parse/rust-cfg-parser.h
new file mode 100644
index 00000000000..895e058057b
--- /dev/null
+++ b/gcc/rust/parse/rust-cfg-parser.h
@@ -0,0 +1,54 @@
+/* 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_CFG_PARSER_H
+#define RUST_CFG_PARSER_H
+
+#include "config.h"
+#include "rust-system.h"
+#include "coretypes.h"
+
+namespace Rust {
+/**
+ * Parse a `key` or `key="value"` pair given to the `-frust-cfg` compiler
+ * option.
+ *
+ * The format is as follows:
+ *
+ * -frust-cfg=<input>
+ *
+ * cfg_input: identifier | identifier '=' '"' identifier '"'
+ *
+ * @param input User input given to the -frust-cfg option
+ * @param key String in which to store the parsed `key`.
+ * @param value String in which to store the parsed `value` if it exists
+ *
+ * @return false if the given input was invalid, true otherwise
+ */
+bool
+parse_cfg_option (std::string &input, std::string &key, std::string &value);
+} // namespace Rust
+
+#if CHECKING_P
+
+namespace selftest {
+extern void
+rust_cfg_parser_test (void);
+} // namespace selftest
+
+#endif // CHECKING_P
+
+#endif // RUST_CFG_PARSER_H
diff --git a/gcc/rust/parse/rust-parse-impl.h b/gcc/rust/parse/rust-parse-impl.h
new file mode 100644
index 00000000000..d925aca05e9
--- /dev/null
+++ b/gcc/rust/parse/rust-parse-impl.h
@@ -0,0 +1,14927 @@
+// 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/>.
+
+/* Template implementation for Rust::Parser. Previously in rust-parse.cc (before
+ * Parser was template). Separated from rust-parse.h for readability. */
+
+/* DO NOT INCLUDE ANYWHERE - this is automatically included with rust-parse.h
+ * This is also the reason why there are no include guards. */
+
+#define INCLUDE_ALGORITHM
+#include "rust-diagnostics.h"
+#include "rust-make-unique.h"
+
+namespace Rust {
+// Left binding powers of operations.
+enum binding_powers
+{
+  // Highest priority
+  LBP_HIGHEST = 100,
+
+  LBP_PATH = 95,
+
+  LBP_METHOD_CALL = 90,
+
+  LBP_FIELD_EXPR = 85,
+
+  LBP_FUNCTION_CALL = 80,
+  LBP_ARRAY_REF = LBP_FUNCTION_CALL,
+
+  LBP_QUESTION_MARK = 75, // unary postfix - counts as left
+
+  LBP_UNARY_PLUS = 70,		    // Used only when the null denotation is +
+  LBP_UNARY_MINUS = LBP_UNARY_PLUS, // Used only when the null denotation is -
+  LBP_UNARY_ASTERISK = LBP_UNARY_PLUS, // deref operator - unary prefix
+  LBP_UNARY_EXCLAM = LBP_UNARY_PLUS,
+  LBP_UNARY_AMP = LBP_UNARY_PLUS,
+  LBP_UNARY_AMP_MUT = LBP_UNARY_PLUS,
+
+  LBP_AS = 65,
+
+  LBP_MUL = 60,
+  LBP_DIV = LBP_MUL,
+  LBP_MOD = LBP_MUL,
+
+  LBP_PLUS = 55,
+  LBP_MINUS = LBP_PLUS,
+
+  LBP_L_SHIFT = 50,
+  LBP_R_SHIFT = LBP_L_SHIFT,
+
+  LBP_AMP = 45,
+
+  LBP_CARET = 40,
+
+  LBP_PIPE = 35,
+
+  LBP_EQUAL = 30,
+  LBP_NOT_EQUAL = LBP_EQUAL,
+  LBP_SMALLER_THAN = LBP_EQUAL,
+  LBP_SMALLER_EQUAL = LBP_EQUAL,
+  LBP_GREATER_THAN = LBP_EQUAL,
+  LBP_GREATER_EQUAL = LBP_EQUAL,
+
+  LBP_LOGICAL_AND = 25,
+
+  LBP_LOGICAL_OR = 20,
+
+  LBP_DOT_DOT = 15,
+  LBP_DOT_DOT_EQ = LBP_DOT_DOT,
+
+  // TODO: note all these assig operators are RIGHT associative!
+  LBP_ASSIG = 10,
+  LBP_PLUS_ASSIG = LBP_ASSIG,
+  LBP_MINUS_ASSIG = LBP_ASSIG,
+  LBP_MULT_ASSIG = LBP_ASSIG,
+  LBP_DIV_ASSIG = LBP_ASSIG,
+  LBP_MOD_ASSIG = LBP_ASSIG,
+  LBP_AMP_ASSIG = LBP_ASSIG,
+  LBP_PIPE_ASSIG = LBP_ASSIG,
+  LBP_CARET_ASSIG = LBP_ASSIG,
+  LBP_L_SHIFT_ASSIG = LBP_ASSIG,
+  LBP_R_SHIFT_ASSIG = LBP_ASSIG,
+
+  // return, break, and closures as lowest priority?
+  LBP_RETURN = 5,
+  LBP_BREAK = LBP_RETURN,
+  LBP_CLOSURE = LBP_RETURN, // unary prefix operators
+
+#if 0
+        // rust precedences
+        PREC_CLOSURE = -40,     // used for closures
+        PREC_JUMP = -30,        // used for break, continue, return, and yield
+        PREC_RANGE = -10,       // used for range (although weird comment in rustc about this)
+        PREC_BINOP = FROM_ASSOC_OP,
+        // used for binary operators mentioned below - also cast, colon (type), assign, assign_op
+        PREC_PREFIX = 50,       // used for box, address_of, let, unary (again, weird comment on let)
+        PREC_POSTFIX = 60,      // used for await, call, method call, field, index, try, inline asm, macro invocation
+        PREC_PAREN = 99,        // used for array, repeat, tuple, literal, path, paren, if, while, for, 'loop', match, block, try block, async, struct
+        PREC_FORCE_PAREN = 100,
+#endif
+
+  // lowest priority
+  LBP_LOWEST = 0
+};
+
+/* Returns whether the token can start a type (i.e. there is a valid type
+ * beginning with the token). */
+inline bool
+can_tok_start_type (TokenId id)
+{
+  switch (id)
+    {
+    case EXCLAM:
+    case LEFT_SQUARE:
+    case LEFT_ANGLE:
+    case UNDERSCORE:
+    case ASTERISK:
+    case AMP:
+    case LIFETIME:
+    case IDENTIFIER:
+    case SUPER:
+    case SELF:
+    case SELF_ALIAS:
+    case CRATE:
+    case DOLLAR_SIGN:
+    case SCOPE_RESOLUTION:
+    case LEFT_PAREN:
+    case FOR:
+    case ASYNC:
+    case CONST:
+    case UNSAFE:
+    case EXTERN_TOK:
+    case FN_TOK:
+    case IMPL:
+    case DYN:
+    case QUESTION_MARK:
+      return true;
+    default:
+      return false;
+    }
+}
+
+/* Returns whether the token id is (or is likely to be) a right angle bracket.
+ * i.e. '>', '>>', '>=' and '>>=' tokens. */
+inline bool
+is_right_angle_tok (TokenId id)
+{
+  switch (id)
+    {
+    case RIGHT_ANGLE:
+    case RIGHT_SHIFT:
+    case GREATER_OR_EQUAL:
+    case RIGHT_SHIFT_EQ:
+      return true;
+    default:
+      return false;
+    }
+}
+
+/* HACK-y special handling for skipping a right angle token at the end of
+ * generic arguments.
+ * Currently, this replaces the "current token" with one that is identical
+ * except has the leading '>' removed (e.g. '>>' becomes '>'). This is bad
+ * for several reasons - it modifies the token stream to something that
+ * actually doesn't make syntactic sense, it may not worked if the token
+ * has already been skipped, etc. It was done because it would not
+ * actually require inserting new items into the token stream (which I
+ * thought would take more work to not mess up) and because I wasn't sure
+ * if the "already seen right angle" flag in the parser would work
+ * correctly.
+ * Those two other approaches listed are in my opinion actually better
+ * long-term - insertion is probably best as it reflects syntactically
+ * what occurs. On the other hand, I need to do a code audit to make sure
+ * that insertion doesn't mess anything up. So that's a FIXME. */
+template <typename ManagedTokenSource>
+bool
+Parser<ManagedTokenSource>::skip_generics_right_angle ()
+{
+  /* OK, new great idea. Have a lexer method called
+   * "split_current_token(TokenType newLeft, TokenType newRight)", which is
+   * called here with whatever arguments are appropriate. That lexer method
+   * handles "replacing" the current token with the "newLeft" and "inserting"
+   * the next token with the "newRight" (and creating a location, etc. for it)
+   */
+
+  /* HACK: special handling for right shift '>>', greater or equal '>=', and
+   * right shift assig */
+  // '>>='
+  const_TokenPtr tok = lexer.peek_token ();
+  switch (tok->get_id ())
+    {
+    case RIGHT_ANGLE:
+      // this is good - skip token
+      lexer.skip_token ();
+      return true;
+      case RIGHT_SHIFT: {
+	// new implementation that should be better
+	lexer.split_current_token (RIGHT_ANGLE, RIGHT_ANGLE);
+	lexer.skip_token ();
+	return true;
+      }
+      case GREATER_OR_EQUAL: {
+	// new implementation that should be better
+	lexer.split_current_token (RIGHT_ANGLE, EQUAL);
+	lexer.skip_token ();
+	return true;
+      }
+      case RIGHT_SHIFT_EQ: {
+	// new implementation that should be better
+	lexer.split_current_token (RIGHT_ANGLE, GREATER_OR_EQUAL);
+	lexer.skip_token ();
+	return true;
+      }
+    default:
+      add_error (Error (tok->get_locus (),
+			"expected %<>%> at end of generic argument - found %qs",
+			tok->get_token_description ()));
+      return false;
+    }
+}
+
+/* Gets left binding power for specified token.
+ * Not suitable for use at the moment or possibly ever because binding power
+ * cannot be purely determined from operator token with Rust grammar - e.g.
+ * method call and field access have
+ * different left binding powers but the same operator token. */
+template <typename ManagedTokenSource>
+int
+Parser<ManagedTokenSource>::left_binding_power (const_TokenPtr token)
+{
+  // HACK: called with "peek_token()", so lookahead is "peek_token(1)"
+  switch (token->get_id ())
+    {
+      /* TODO: issue here - distinguish between method calls and field access
+       * somehow? Also would have to distinguish between paths and function
+       * calls (:: operator), maybe more stuff. */
+      /* Current plan for tackling LBP - don't do it based on token, use
+       * lookahead. Or alternatively, only use Pratt parsing for OperatorExpr
+       * and handle other expressions without it. rustc only considers
+       * arithmetic, logical/relational, 'as',
+       * '?=', ranges, colons, and assignment to have operator precedence and
+       * associativity rules applicable. It then has
+       * a separate "ExprPrecedence" that also includes binary operators. */
+
+      // TODO: handle operator overloading - have a function replace the
+      // operator?
+
+      /*case DOT:
+	  return LBP_DOT;*/
+
+    case SCOPE_RESOLUTION:
+      rust_debug (
+	"possible error - looked up LBP of scope resolution operator. should "
+	"be handled elsewhere.");
+      return LBP_PATH;
+
+    /* Resolved by lookahead HACK that should work with current code. If next
+     * token is identifier and token after that isn't parenthesised expression
+     * list, it is a field reference. */
+    case DOT:
+      if (lexer.peek_token (1)->get_id () == IDENTIFIER
+	  && lexer.peek_token (2)->get_id () != LEFT_PAREN)
+	{
+	  return LBP_FIELD_EXPR;
+	}
+      return LBP_METHOD_CALL;
+
+    case LEFT_PAREN:
+      return LBP_FUNCTION_CALL;
+
+    case LEFT_SQUARE:
+      return LBP_ARRAY_REF;
+
+    // postfix question mark (i.e. error propagation expression)
+    case QUESTION_MARK:
+      return LBP_QUESTION_MARK;
+
+    case AS:
+      return LBP_AS;
+
+    case ASTERISK:
+      return LBP_MUL;
+    case DIV:
+      return LBP_DIV;
+    case PERCENT:
+      return LBP_MOD;
+
+    case PLUS:
+      return LBP_PLUS;
+    case MINUS:
+      return LBP_MINUS;
+
+    case LEFT_SHIFT:
+      return LBP_L_SHIFT;
+    case RIGHT_SHIFT:
+      return LBP_R_SHIFT;
+
+    // binary & operator
+    case AMP:
+      return LBP_AMP;
+
+    // binary ^ operator
+    case CARET:
+      return LBP_CARET;
+
+    // binary | operator
+    case PIPE:
+      return LBP_PIPE;
+
+    case EQUAL_EQUAL:
+      return LBP_EQUAL;
+    case NOT_EQUAL:
+      return LBP_NOT_EQUAL;
+    case RIGHT_ANGLE:
+      return LBP_GREATER_THAN;
+    case GREATER_OR_EQUAL:
+      return LBP_GREATER_EQUAL;
+    case LEFT_ANGLE:
+      return LBP_SMALLER_THAN;
+    case LESS_OR_EQUAL:
+      return LBP_SMALLER_EQUAL;
+
+    case LOGICAL_AND:
+      return LBP_LOGICAL_AND;
+
+    case OR:
+      return LBP_LOGICAL_OR;
+
+    case DOT_DOT:
+      return LBP_DOT_DOT;
+
+    case DOT_DOT_EQ:
+      return LBP_DOT_DOT_EQ;
+
+    case EQUAL:
+      return LBP_ASSIG;
+    case PLUS_EQ:
+      return LBP_PLUS_ASSIG;
+    case MINUS_EQ:
+      return LBP_MINUS_ASSIG;
+    case ASTERISK_EQ:
+      return LBP_MULT_ASSIG;
+    case DIV_EQ:
+      return LBP_DIV_ASSIG;
+    case PERCENT_EQ:
+      return LBP_MOD_ASSIG;
+    case AMP_EQ:
+      return LBP_AMP_ASSIG;
+    case PIPE_EQ:
+      return LBP_PIPE_ASSIG;
+    case CARET_EQ:
+      return LBP_CARET_ASSIG;
+    case LEFT_SHIFT_EQ:
+      return LBP_L_SHIFT_ASSIG;
+    case RIGHT_SHIFT_EQ:
+      return LBP_R_SHIFT_ASSIG;
+
+    /* HACK: float literal due to lexer misidentifying a dot then an integer as
+     * a float */
+    case FLOAT_LITERAL:
+      return LBP_FIELD_EXPR;
+      // field expr is same as tuple expr in precedence, i imagine
+      // TODO: is this needed anymore? lexer shouldn't do that anymore
+
+    // anything that can't appear in an infix position is given lowest priority
+    default:
+      return LBP_LOWEST;
+    }
+}
+
+// Returns true when current token is EOF.
+template <typename ManagedTokenSource>
+bool
+Parser<ManagedTokenSource>::done_end_of_file ()
+{
+  return lexer.peek_token ()->get_id () == END_OF_FILE;
+}
+
+// Parses a sequence of items within a module or the implicit top-level module
+// in a crate
+template <typename ManagedTokenSource>
+std::vector<std::unique_ptr<AST::Item>>
+Parser<ManagedTokenSource>::parse_items ()
+{
+  std::vector<std::unique_ptr<AST::Item>> items;
+
+  const_TokenPtr t = lexer.peek_token ();
+  while (t->get_id () != END_OF_FILE)
+    {
+      std::unique_ptr<AST::Item> item = parse_item (false);
+      if (item == nullptr)
+	{
+	  Error error (lexer.peek_token ()->get_locus (),
+		       "failed to parse item in crate");
+	  add_error (std::move (error));
+
+	  // TODO: should all items be cleared?
+	  items = std::vector<std::unique_ptr<AST::Item>> ();
+	  break;
+	}
+
+      items.push_back (std::move (item));
+
+      t = lexer.peek_token ();
+    }
+
+  return items;
+}
+
+// Parses a crate (compilation unit) - entry point
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::Crate>
+Parser<ManagedTokenSource>::parse_crate ()
+{
+  // parse inner attributes
+  AST::AttrVec inner_attrs = parse_inner_attributes ();
+
+  // parse items
+  std::vector<std::unique_ptr<AST::Item>> items = parse_items ();
+
+  // emit all errors
+  for (const auto &error : error_table)
+    error.emit_error ();
+
+  return std::unique_ptr<AST::Crate> (
+    new AST::Crate (std::move (items), std::move (inner_attrs)));
+}
+
+// Parse a contiguous block of inner attributes.
+template <typename ManagedTokenSource>
+AST::AttrVec
+Parser<ManagedTokenSource>::parse_inner_attributes ()
+{
+  AST::AttrVec inner_attributes;
+
+  // only try to parse it if it starts with "#!" not only "#"
+  while ((lexer.peek_token ()->get_id () == HASH
+	  && lexer.peek_token (1)->get_id () == EXCLAM)
+	 || lexer.peek_token ()->get_id () == INNER_DOC_COMMENT)
+    {
+      AST::Attribute inner_attr = parse_inner_attribute ();
+
+      /* Ensure only valid inner attributes are added to the inner_attributes
+       * list */
+      if (!inner_attr.is_empty ())
+	{
+	  inner_attributes.push_back (std::move (inner_attr));
+	}
+      else
+	{
+	  /* If no more valid inner attributes, break out of loop (only
+	   * contiguous inner attributes parsed). */
+	  break;
+	}
+    }
+
+  inner_attributes.shrink_to_fit ();
+  return inner_attributes;
+}
+
+// Parse a inner or outer doc comment into an doc attribute
+template <typename ManagedTokenSource>
+AST::Attribute
+Parser<ManagedTokenSource>::parse_doc_comment ()
+{
+  const_TokenPtr token = lexer.peek_token ();
+  Location locus = token->get_locus ();
+  AST::SimplePathSegment segment ("doc", locus);
+  std::vector<AST::SimplePathSegment> segments;
+  segments.push_back (std::move (segment));
+  AST::SimplePath attr_path (std::move (segments), false, locus);
+  AST::LiteralExpr lit_expr (token->get_str (), AST::Literal::STRING,
+			     PrimitiveCoreType::CORETYPE_STR, {}, locus);
+  std::unique_ptr<AST::AttrInput> attr_input (
+    new AST::AttrInputLiteral (std::move (lit_expr)));
+  lexer.skip_token ();
+  return AST::Attribute (std::move (attr_path), std::move (attr_input), locus);
+}
+
+// Parse a single inner attribute.
+template <typename ManagedTokenSource>
+AST::Attribute
+Parser<ManagedTokenSource>::parse_inner_attribute ()
+{
+  if (lexer.peek_token ()->get_id () == INNER_DOC_COMMENT)
+    return parse_doc_comment ();
+
+  if (lexer.peek_token ()->get_id () != HASH)
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "BUG: token %<#%> is missing, but %<parse_inner_attribute%> "
+		   "was invoked");
+      add_error (std::move (error));
+
+      return AST::Attribute::create_empty ();
+    }
+  lexer.skip_token ();
+
+  if (lexer.peek_token ()->get_id () != EXCLAM)
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "expected %<!%> or %<[%> for inner attribute");
+      add_error (std::move (error));
+
+      return AST::Attribute::create_empty ();
+    }
+  lexer.skip_token ();
+
+  if (!skip_token (LEFT_SQUARE))
+    return AST::Attribute::create_empty ();
+
+  AST::Attribute actual_attribute = parse_attribute_body ();
+
+  if (!skip_token (RIGHT_SQUARE))
+    return AST::Attribute::create_empty ();
+
+  return actual_attribute;
+}
+
+// Parses the body of an attribute (inner or outer).
+template <typename ManagedTokenSource>
+AST::Attribute
+Parser<ManagedTokenSource>::parse_attribute_body ()
+{
+  Location locus = lexer.peek_token ()->get_locus ();
+
+  AST::SimplePath attr_path = parse_simple_path ();
+  // ensure path is valid to parse attribute input
+  if (attr_path.is_empty ())
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "empty simple path in attribute");
+      add_error (std::move (error));
+
+      // Skip past potential further info in attribute (i.e. attr_input)
+      skip_after_end_attribute ();
+      return AST::Attribute::create_empty ();
+    }
+
+  std::unique_ptr<AST::AttrInput> attr_input = parse_attr_input ();
+  // AttrInput is allowed to be null, so no checks here
+
+  return AST::Attribute (std::move (attr_path), std::move (attr_input), locus);
+}
+
+/* Determines whether token is a valid simple path segment. This does not
+ * include scope resolution operators. */
+inline bool
+is_simple_path_segment (TokenId id)
+{
+  switch (id)
+    {
+    case IDENTIFIER:
+    case SUPER:
+    case SELF:
+    case CRATE:
+      return true;
+    case DOLLAR_SIGN:
+      // assume that dollar sign leads to $crate
+      return true;
+    default:
+      return false;
+    }
+}
+
+// Parses a SimplePath AST node, if it exists. Does nothing otherwise.
+template <typename ManagedTokenSource>
+AST::SimplePath
+Parser<ManagedTokenSource>::parse_simple_path ()
+{
+  bool has_opening_scope_resolution = false;
+  Location locus = Linemap::unknown_location ();
+
+  // don't parse anything if not a path upfront
+  if (!is_simple_path_segment (lexer.peek_token ()->get_id ())
+      && !is_simple_path_segment (lexer.peek_token (1)->get_id ()))
+    return AST::SimplePath::create_empty ();
+
+  /* Checks for opening scope resolution (i.e. global scope fully-qualified
+   * path) */
+  if (lexer.peek_token ()->get_id () == SCOPE_RESOLUTION)
+    {
+      has_opening_scope_resolution = true;
+
+      locus = lexer.peek_token ()->get_locus ();
+
+      lexer.skip_token ();
+    }
+
+  // Parse single required simple path segment
+  AST::SimplePathSegment segment = parse_simple_path_segment ();
+
+  // get location if not gotten already
+  if (locus == Linemap::unknown_location ())
+    locus = segment.get_locus ();
+
+  std::vector<AST::SimplePathSegment> segments;
+
+  // Return empty vector if first, actually required segment is an error
+  if (segment.is_error ())
+    return AST::SimplePath::create_empty ();
+
+  segments.push_back (std::move (segment));
+
+  // Parse all other simple path segments
+  while (lexer.peek_token ()->get_id () == SCOPE_RESOLUTION)
+    {
+      // Skip scope resolution operator
+      lexer.skip_token ();
+
+      AST::SimplePathSegment new_segment = parse_simple_path_segment ();
+
+      // Return path as currently constructed if segment in error state.
+      if (new_segment.is_error ())
+	break;
+
+      segments.push_back (std::move (new_segment));
+    }
+
+  // DEBUG: check for any empty segments
+  for (const auto &seg : segments)
+    {
+      if (seg.is_error ())
+	{
+	  rust_debug (
+	    "when parsing simple path, somehow empty path segment was "
+	    "not filtered out. Path begins with '%s'",
+	    segments.at (0).as_string ().c_str ());
+	}
+    }
+
+  return AST::SimplePath (std::move (segments), has_opening_scope_resolution,
+			  locus);
+  /* TODO: now that is_simple_path_segment exists, could probably start
+   * actually making errors upon parse failure of segments and whatever */
+}
+
+/* Parses a single SimplePathSegment (does not handle the scope resolution
+ * operators) */
+template <typename ManagedTokenSource>
+AST::SimplePathSegment
+Parser<ManagedTokenSource>::parse_simple_path_segment ()
+{
+  const_TokenPtr t = lexer.peek_token ();
+  switch (t->get_id ())
+    {
+    case IDENTIFIER:
+      lexer.skip_token ();
+
+      return AST::SimplePathSegment (t->get_str (), t->get_locus ());
+    case SUPER:
+      lexer.skip_token ();
+
+      return AST::SimplePathSegment ("super", t->get_locus ());
+    case SELF:
+      lexer.skip_token ();
+
+      return AST::SimplePathSegment ("self", t->get_locus ());
+    case CRATE:
+      lexer.skip_token ();
+
+      return AST::SimplePathSegment ("crate", t->get_locus ());
+    case DOLLAR_SIGN:
+      if (lexer.peek_token (1)->get_id () == CRATE)
+	{
+	  lexer.skip_token (1);
+
+	  return AST::SimplePathSegment ("$crate", t->get_locus ());
+	}
+      gcc_fallthrough ();
+    default:
+      // do nothing but inactivates warning from gcc when compiling
+      /* could put the rust_error_at thing here but fallthrough (from failing
+       * $crate condition) isn't completely obvious if it is. */
+
+      // test prevent error
+      return AST::SimplePathSegment::create_error ();
+    }
+  gcc_unreachable ();
+  /*rust_error_at(
+    t->get_locus(), "invalid token '%s' in simple path segment",
+    t->get_token_description());*/
+  // this is not necessarily an error, e.g. end of path
+  // return AST::SimplePathSegment::create_error();
+}
+
+// Parses a PathIdentSegment - an identifier segment of a non-SimplePath path.
+template <typename ManagedTokenSource>
+AST::PathIdentSegment
+Parser<ManagedTokenSource>::parse_path_ident_segment ()
+{
+  const_TokenPtr t = lexer.peek_token ();
+  switch (t->get_id ())
+    {
+    case IDENTIFIER:
+      lexer.skip_token ();
+
+      return AST::PathIdentSegment (t->get_str (), t->get_locus ());
+    case SUPER:
+      lexer.skip_token ();
+
+      return AST::PathIdentSegment ("super", t->get_locus ());
+    case SELF:
+      lexer.skip_token ();
+
+      return AST::PathIdentSegment ("self", t->get_locus ());
+    case SELF_ALIAS:
+      lexer.skip_token ();
+
+      return AST::PathIdentSegment ("Self", t->get_locus ());
+    case CRATE:
+      lexer.skip_token ();
+
+      return AST::PathIdentSegment ("crate", t->get_locus ());
+    case DOLLAR_SIGN:
+      if (lexer.peek_token (1)->get_id () == CRATE)
+	{
+	  lexer.skip_token (1);
+
+	  return AST::PathIdentSegment ("$crate", t->get_locus ());
+	}
+      gcc_fallthrough ();
+    default:
+      /* do nothing but inactivates warning from gcc when compiling
+       * could put the error_at thing here but fallthrough (from failing $crate
+       * condition) isn't completely obvious if it is. */
+
+      // test prevent error
+      return AST::PathIdentSegment::create_error ();
+    }
+  gcc_unreachable ();
+  // not necessarily an error
+}
+
+// Parses an AttrInput AST node (polymorphic, as AttrInput is abstract)
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::AttrInput>
+Parser<ManagedTokenSource>::parse_attr_input ()
+{
+  const_TokenPtr t = lexer.peek_token ();
+  switch (t->get_id ())
+    {
+    case LEFT_PAREN:
+    case LEFT_SQUARE:
+      case LEFT_CURLY: {
+	// must be a delimited token tree, so parse that
+	std::unique_ptr<AST::AttrInput> input_tree (
+	  new AST::DelimTokenTree (parse_delim_token_tree ()));
+
+	// TODO: potential checks on DelimTokenTree before returning
+
+	return input_tree;
+      }
+      case EQUAL: {
+	// = LiteralExpr
+	lexer.skip_token ();
+
+	t = lexer.peek_token ();
+
+	/* Ensure token is a "literal expression" (literally only a literal
+	 * token of any type) */
+	if (!t->is_literal ())
+	  {
+	    Error error (
+	      t->get_locus (),
+	      "unknown token %qs in attribute body - literal expected",
+	      t->get_token_description ());
+	    add_error (std::move (error));
+
+	    skip_after_end_attribute ();
+	    return nullptr;
+	  }
+
+	AST::Literal::LitType lit_type = AST::Literal::STRING;
+	// Crappy mapping of token type to literal type
+	switch (t->get_id ())
+	  {
+	  case INT_LITERAL:
+	    lit_type = AST::Literal::INT;
+	    break;
+	  case FLOAT_LITERAL:
+	    lit_type = AST::Literal::FLOAT;
+	    break;
+	  case CHAR_LITERAL:
+	    lit_type = AST::Literal::CHAR;
+	    break;
+	  case BYTE_CHAR_LITERAL:
+	    lit_type = AST::Literal::BYTE;
+	    break;
+	  case BYTE_STRING_LITERAL:
+	    lit_type = AST::Literal::BYTE_STRING;
+	    break;
+	  case STRING_LITERAL:
+	  default:
+	    lit_type = AST::Literal::STRING;
+	    break; // TODO: raw string? don't eliminate it from lexer?
+	  }
+
+	// create actual LiteralExpr
+	AST::LiteralExpr lit_expr (t->get_str (), lit_type, t->get_type_hint (),
+				   {}, t->get_locus ());
+	lexer.skip_token ();
+
+	std::unique_ptr<AST::AttrInput> attr_input_lit (
+	  new AST::AttrInputLiteral (std::move (lit_expr)));
+
+	// do checks or whatever? none required, really
+
+	// FIXME: shouldn't a skip token be required here?
+
+	return attr_input_lit;
+      }
+      break;
+    case RIGHT_SQUARE:
+      // means AttrInput is missing, which is allowed
+      return nullptr;
+    default:
+      add_error (
+	Error (t->get_locus (),
+	       "unknown token %qs in attribute body - attribute input or "
+	       "none expected",
+	       t->get_token_description ()));
+
+      skip_after_end_attribute ();
+      return nullptr;
+    }
+  gcc_unreachable ();
+  // TODO: find out how to stop gcc error on "no return value"
+}
+
+/* Returns true if the token id matches the delimiter type. Note that this only
+ * operates for END delimiter tokens. */
+inline bool
+token_id_matches_delims (TokenId token_id, AST::DelimType delim_type)
+{
+  return ((token_id == RIGHT_PAREN && delim_type == AST::PARENS)
+	  || (token_id == RIGHT_SQUARE && delim_type == AST::SQUARE)
+	  || (token_id == RIGHT_CURLY && delim_type == AST::CURLY));
+}
+
+/* Returns true if the likely result of parsing the next few tokens is a path.
+ * Not guaranteed, though, especially in the case of syntax errors. */
+inline bool
+is_likely_path_next (TokenId next_token_id)
+{
+  switch (next_token_id)
+    {
+    case IDENTIFIER:
+    case SUPER:
+    case SELF:
+    case SELF_ALIAS:
+    case CRATE:
+    // maybe - maybe do extra check. But then requires another TokenId.
+    case DOLLAR_SIGN:
+    case SCOPE_RESOLUTION:
+      return true;
+    default:
+      return false;
+    }
+}
+
+// Parses a delimited token tree
+template <typename ManagedTokenSource>
+AST::DelimTokenTree
+Parser<ManagedTokenSource>::parse_delim_token_tree ()
+{
+  const_TokenPtr t = lexer.peek_token ();
+  lexer.skip_token ();
+  Location initial_loc = t->get_locus ();
+
+  // save delim type to ensure it is reused later
+  AST::DelimType delim_type = AST::PARENS;
+
+  // Map tokens to DelimType
+  switch (t->get_id ())
+    {
+    case LEFT_PAREN:
+      delim_type = AST::PARENS;
+      break;
+    case LEFT_SQUARE:
+      delim_type = AST::SQUARE;
+      break;
+    case LEFT_CURLY:
+      delim_type = AST::CURLY;
+      break;
+    default:
+      add_error (Error (t->get_locus (),
+			"unexpected token %qs - expecting delimiters (for a "
+			"delimited token tree)",
+			t->get_token_description ()));
+
+      return AST::DelimTokenTree::create_empty ();
+    }
+
+  // parse actual token tree vector - 0 or more
+  std::vector<std::unique_ptr<AST::TokenTree>> token_trees_in_tree;
+  auto delim_open
+    = std::unique_ptr<AST::Token> (new AST::Token (std::move (t)));
+  token_trees_in_tree.push_back (std::move (delim_open));
+
+  // repeat loop until finding the matching delimiter
+  t = lexer.peek_token ();
+  while (!token_id_matches_delims (t->get_id (), delim_type)
+	 && t->get_id () != END_OF_FILE)
+    {
+      std::unique_ptr<AST::TokenTree> tok_tree = parse_token_tree ();
+
+      if (tok_tree == nullptr)
+	{
+	  // TODO: is this error handling appropriate?
+	  Error error (
+	    t->get_locus (),
+	    "failed to parse token tree in delimited token tree - found %qs",
+	    t->get_token_description ());
+	  add_error (std::move (error));
+
+	  return AST::DelimTokenTree::create_empty ();
+	}
+
+      token_trees_in_tree.push_back (std::move (tok_tree));
+
+      // lexer.skip_token();
+      t = lexer.peek_token ();
+    }
+  auto delim_close
+    = std::unique_ptr<AST::Token> (new AST::Token (std::move (t)));
+  token_trees_in_tree.push_back (std::move (delim_close));
+
+  AST::DelimTokenTree token_tree (delim_type, std::move (token_trees_in_tree),
+				  initial_loc);
+
+  // parse end delimiters
+  t = lexer.peek_token ();
+
+  if (token_id_matches_delims (t->get_id (), delim_type))
+    {
+      // tokens match opening delimiter, so skip.
+      lexer.skip_token ();
+
+      // DEBUG
+      rust_debug ("finished parsing new delim token tree - peeked token is now "
+		  "'%s' while t is '%s'",
+		  lexer.peek_token ()->get_token_description (),
+		  t->get_token_description ());
+
+      return token_tree;
+    }
+  else
+    {
+      // tokens don't match opening delimiters, so produce error
+      Error error (t->get_locus (),
+		   "unexpected token %qs - expecting closing delimiter %qs "
+		   "(for a delimited token tree)",
+		   t->get_token_description (),
+		   (delim_type == AST::PARENS
+		      ? ")"
+		      : (delim_type == AST::SQUARE ? "]" : "}")));
+      add_error (std::move (error));
+
+      /* return empty token tree despite possibly parsing valid token tree -
+       * TODO is this a good idea? */
+      return AST::DelimTokenTree::create_empty ();
+    }
+}
+
+/* Parses a TokenTree syntactical production. This is either a delimited token
+ * tree or a non-delimiter token. */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::TokenTree>
+Parser<ManagedTokenSource>::parse_token_tree ()
+{
+  const_TokenPtr t = lexer.peek_token ();
+
+  switch (t->get_id ())
+    {
+    case LEFT_PAREN:
+    case LEFT_SQUARE:
+    case LEFT_CURLY:
+      // Parse delimited token tree
+      // TODO: use move rather than copy constructor
+      return std::unique_ptr<AST::DelimTokenTree> (
+	new AST::DelimTokenTree (parse_delim_token_tree ()));
+    case RIGHT_PAREN:
+    case RIGHT_SQUARE:
+    case RIGHT_CURLY:
+      // error - should not be called when this a token
+      add_error (
+	Error (t->get_locus (),
+	       "unexpected closing delimiter %qs - token tree requires "
+	       "either paired delimiters or non-delimiter tokens",
+	       t->get_token_description ()));
+
+      lexer.skip_token ();
+      return nullptr;
+    default:
+      // parse token itself as TokenTree
+      lexer.skip_token ();
+      return std::unique_ptr<AST::Token> (new AST::Token (std::move (t)));
+    }
+}
+
+// Parses a single item
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::Item>
+Parser<ManagedTokenSource>::parse_item (bool called_from_statement)
+{
+  // has a "called_from_statement" parameter for better error message handling
+
+  // parse outer attributes for item
+  AST::AttrVec outer_attrs = parse_outer_attributes ();
+
+  // TODO: decide how to deal with VisItem vs MacroItem dichotomy
+  /* best current solution: catch all keywords that would imply a VisItem in a
+   * switch and have MacroItem as a last resort */
+
+  const_TokenPtr t = lexer.peek_token ();
+
+  switch (t->get_id ())
+    {
+    case END_OF_FILE:
+      // not necessarily an error, unless we just read outer
+      // attributes which needs to be attached
+      if (!outer_attrs.empty ())
+	{
+	  Rust::AST::Attribute attr = outer_attrs.back ();
+	  Error error (attr.get_locus (),
+		       "expected item after outer attribute or doc comment");
+	  add_error (std::move (error));
+	}
+      return nullptr;
+    case PUB:
+    case MOD:
+    case EXTERN_TOK:
+    case USE:
+    case FN_TOK:
+    case TYPE:
+    case STRUCT_TOK:
+    case ENUM_TOK:
+    case CONST:
+    case STATIC_TOK:
+    case TRAIT:
+    case IMPL:
+    /* TODO: implement union keyword but not really because of
+     * context-dependence crappy hack way to parse a union written below to
+     * separate it from the good code. */
+    // case UNION:
+    case UNSAFE: // maybe - unsafe traits are a thing
+      // if any of these (should be all possible VisItem prefixes), parse a
+      // VisItem
+      return parse_vis_item (std::move (outer_attrs));
+      break;
+    case SUPER:
+    case SELF:
+    case CRATE:
+    case DOLLAR_SIGN:
+      // almost certainly macro invocation semi
+      return parse_macro_item (std::move (outer_attrs));
+      break;
+    // crappy hack to do union "keyword"
+    case IDENTIFIER:
+      // TODO: ensure std::string and literal comparison works
+      if (t->get_str () == "union"
+	  && lexer.peek_token (1)->get_id () == IDENTIFIER)
+	{
+	  return parse_vis_item (std::move (outer_attrs));
+	  // or should this go straight to parsing union?
+	}
+      else if (t->get_str () == "macro_rules")
+	{
+	  // macro_rules! macro item
+	  return parse_macro_item (std::move (outer_attrs));
+	}
+      else if (lexer.peek_token (1)->get_id () == SCOPE_RESOLUTION
+	       || lexer.peek_token (1)->get_id () == EXCLAM)
+	{
+	  /* path (probably) or macro invocation, so probably a macro invocation
+	   * semi */
+	  return parse_macro_item (std::move (outer_attrs));
+	}
+      gcc_fallthrough ();
+    default:
+      // otherwise unrecognised
+      // return parse_macro_item(std::move(outer_attrs));
+      add_error (Error (t->get_locus (),
+			"unrecognised token %qs for start of %s",
+			t->get_token_description (),
+			called_from_statement ? "statement" : "item"));
+
+      // skip somewhere?
+      return nullptr;
+      break;
+    }
+}
+
+// Parses a contiguous block of outer attributes.
+template <typename ManagedTokenSource>
+AST::AttrVec
+Parser<ManagedTokenSource>::parse_outer_attributes ()
+{
+  AST::AttrVec outer_attributes;
+
+  while (lexer.peek_token ()->get_id ()
+	   == HASH /* Can also be #!, which catches errors.  */
+	 || lexer.peek_token ()->get_id () == OUTER_DOC_COMMENT
+	 || lexer.peek_token ()->get_id ()
+	      == INNER_DOC_COMMENT) /* For error handling.  */
+    {
+      AST::Attribute outer_attr = parse_outer_attribute ();
+
+      /* Ensure only valid outer attributes are added to the outer_attributes
+       * list */
+      if (!outer_attr.is_empty ())
+	{
+	  outer_attributes.push_back (std::move (outer_attr));
+	}
+      else
+	{
+	  /* If no more valid outer attributes, break out of loop (only
+	   * contiguous outer attributes parsed). */
+	  break;
+	}
+    }
+
+  outer_attributes.shrink_to_fit ();
+  return outer_attributes;
+
+  /* TODO: this shares basically all code with parse_inner_attributes except
+   * function call - find way of making it more modular? function pointer? */
+}
+
+// Parse a single outer attribute.
+template <typename ManagedTokenSource>
+AST::Attribute
+Parser<ManagedTokenSource>::parse_outer_attribute ()
+{
+  if (lexer.peek_token ()->get_id () == OUTER_DOC_COMMENT)
+    return parse_doc_comment ();
+
+  if (lexer.peek_token ()->get_id () == INNER_DOC_COMMENT)
+    {
+      Error error (
+	lexer.peek_token ()->get_locus (),
+	"inner doc (%<//!%> or %</*!%>) only allowed at start of item "
+	"and before any outer attribute or doc (%<#[%>, %<///%> or %</**%>)");
+      add_error (std::move (error));
+      lexer.skip_token ();
+      return AST::Attribute::create_empty ();
+    }
+
+  /* OuterAttribute -> '#' '[' Attr ']' */
+
+  if (lexer.peek_token ()->get_id () != HASH)
+    return AST::Attribute::create_empty ();
+
+  lexer.skip_token ();
+
+  TokenId id = lexer.peek_token ()->get_id ();
+  if (id != LEFT_SQUARE)
+    {
+      if (id == EXCLAM)
+	{
+	  // this is inner attribute syntax, so throw error
+	  // inner attributes were either already parsed or not allowed here.
+	  Error error (
+	    lexer.peek_token ()->get_locus (),
+	    "token %<!%> found, indicating inner attribute definition. Inner "
+	    "attributes are not possible at this location");
+	  add_error (std::move (error));
+	}
+      return AST::Attribute::create_empty ();
+    }
+
+  lexer.skip_token ();
+
+  AST::Attribute actual_attribute = parse_attribute_body ();
+
+  if (lexer.peek_token ()->get_id () != RIGHT_SQUARE)
+    return AST::Attribute::create_empty ();
+
+  lexer.skip_token ();
+
+  return actual_attribute;
+}
+
+// Parses a VisItem (item that can have non-default visibility).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::VisItem>
+Parser<ManagedTokenSource>::parse_vis_item (AST::AttrVec outer_attrs)
+{
+  // parse visibility, which may or may not exist
+  AST::Visibility vis = parse_visibility ();
+
+  // select VisItem to create depending on keyword
+  const_TokenPtr t = lexer.peek_token ();
+
+  switch (t->get_id ())
+    {
+    case MOD:
+      return parse_module (std::move (vis), std::move (outer_attrs));
+    case EXTERN_TOK:
+      // lookahead to resolve syntactical production
+      t = lexer.peek_token (1);
+
+      switch (t->get_id ())
+	{
+	case CRATE:
+	  return parse_extern_crate (std::move (vis), std::move (outer_attrs));
+	case FN_TOK: // extern function
+	  return parse_function (std::move (vis), std::move (outer_attrs));
+	case LEFT_CURLY: // extern block
+	  return parse_extern_block (std::move (vis), std::move (outer_attrs));
+	case STRING_LITERAL: // for specifying extern ABI
+	  // could be extern block or extern function, so more lookahead
+	  t = lexer.peek_token (2);
+
+	  switch (t->get_id ())
+	    {
+	    case FN_TOK:
+	      return parse_function (std::move (vis), std::move (outer_attrs));
+	    case LEFT_CURLY:
+	      return parse_extern_block (std::move (vis),
+					 std::move (outer_attrs));
+	    default:
+	      add_error (
+		Error (t->get_locus (),
+		       "unexpected token %qs in some sort of extern production",
+		       t->get_token_description ()));
+
+	      lexer.skip_token (2); // TODO: is this right thing to do?
+	      return nullptr;
+	    }
+	default:
+	  add_error (
+	    Error (t->get_locus (),
+		   "unexpected token %qs in some sort of extern production",
+		   t->get_token_description ()));
+
+	  lexer.skip_token (1); // TODO: is this right thing to do?
+	  return nullptr;
+	}
+    case USE:
+      return parse_use_decl (std::move (vis), std::move (outer_attrs));
+    case FN_TOK:
+      return parse_function (std::move (vis), std::move (outer_attrs));
+    case TYPE:
+      return parse_type_alias (std::move (vis), std::move (outer_attrs));
+    case STRUCT_TOK:
+      return parse_struct (std::move (vis), std::move (outer_attrs));
+    case ENUM_TOK:
+      return parse_enum (std::move (vis), std::move (outer_attrs));
+    // TODO: implement union keyword but not really because of
+    // context-dependence case UNION: crappy hack to do union "keyword"
+    case IDENTIFIER:
+      if (t->get_str () == "union"
+	  && lexer.peek_token (1)->get_id () == IDENTIFIER)
+	{
+	  return parse_union (std::move (vis), std::move (outer_attrs));
+	  // or should item switch go straight to parsing union?
+	}
+      else
+	{
+	  break;
+	}
+    case CONST:
+      // lookahead to resolve syntactical production
+      t = lexer.peek_token (1);
+
+      switch (t->get_id ())
+	{
+	case IDENTIFIER:
+	case UNDERSCORE:
+	  return parse_const_item (std::move (vis), std::move (outer_attrs));
+	case UNSAFE:
+	case EXTERN_TOK:
+	case FN_TOK:
+	  return parse_function (std::move (vis), std::move (outer_attrs));
+	default:
+	  add_error (
+	    Error (t->get_locus (),
+		   "unexpected token %qs in some sort of const production",
+		   t->get_token_description ()));
+
+	  lexer.skip_token (1); // TODO: is this right thing to do?
+	  return nullptr;
+	}
+    case STATIC_TOK:
+      return parse_static_item (std::move (vis), std::move (outer_attrs));
+    case TRAIT:
+      return parse_trait (std::move (vis), std::move (outer_attrs));
+    case IMPL:
+      return parse_impl (std::move (vis), std::move (outer_attrs));
+    case UNSAFE: // unsafe traits, unsafe functions, unsafe impls (trait impls),
+      // lookahead to resolve syntactical production
+      t = lexer.peek_token (1);
+
+      switch (t->get_id ())
+	{
+	case TRAIT:
+	  return parse_trait (std::move (vis), std::move (outer_attrs));
+	case EXTERN_TOK:
+	case FN_TOK:
+	  return parse_function (std::move (vis), std::move (outer_attrs));
+	case IMPL:
+	  return parse_impl (std::move (vis), std::move (outer_attrs));
+	default:
+	  add_error (
+	    Error (t->get_locus (),
+		   "unexpected token %qs in some sort of unsafe production",
+		   t->get_token_description ()));
+
+	  lexer.skip_token (1); // TODO: is this right thing to do?
+	  return nullptr;
+	}
+    default:
+      // otherwise vis item clearly doesn't exist, which is not an error
+      // has a catch-all post-switch return to allow other breaks to occur
+      break;
+    }
+  return nullptr;
+}
+
+// Parses a MacroItem (either a MacroInvocationSemi or MacroRulesDefinition).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::MacroItem>
+Parser<ManagedTokenSource>::parse_macro_item (AST::AttrVec outer_attrs)
+{
+  const_TokenPtr t = lexer.peek_token ();
+
+  /* dodgy way of detecting macro due to weird context-dependence thing.
+   * probably can be improved */
+  // TODO: ensure that string compare works properly
+  if (t->get_id () == IDENTIFIER && t->get_str () == "macro_rules")
+    {
+      return parse_macro_rules_def (std::move (outer_attrs));
+    }
+  else
+    {
+      // DEBUG: TODO: remove
+      rust_debug (
+	"DEBUG - parse_macro_item called and token is not macro_rules");
+      if (t->get_id () == IDENTIFIER)
+	{
+	  rust_debug ("just add to last error: token is not macro_rules and is "
+		      "instead '%s'",
+		      t->get_str ().c_str ());
+	}
+      else
+	{
+	  rust_debug ("just add to last error: token is not macro_rules and is "
+		      "not an identifier either - it is '%s'",
+		      t->get_token_description ());
+	}
+
+      return parse_macro_invocation_semi (std::move (outer_attrs));
+    }
+}
+
+// Parses a macro rules definition syntax extension whatever thing.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::MacroRulesDefinition>
+Parser<ManagedTokenSource>::parse_macro_rules_def (AST::AttrVec outer_attrs)
+{
+  // ensure that first token is identifier saying "macro_rules"
+  const_TokenPtr t = lexer.peek_token ();
+  if (t->get_id () != IDENTIFIER || t->get_str () != "macro_rules")
+    {
+      Error error (
+	t->get_locus (),
+	"macro rules definition does not start with %<macro_rules%>");
+      add_error (std::move (error));
+
+      // skip after somewhere?
+      return nullptr;
+    }
+  lexer.skip_token ();
+  Location macro_locus = t->get_locus ();
+
+  if (!skip_token (EXCLAM))
+    {
+      // skip after somewhere?
+      return nullptr;
+    }
+
+  // parse macro name
+  const_TokenPtr ident_tok = expect_token (IDENTIFIER);
+  if (ident_tok == nullptr)
+    {
+      return nullptr;
+    }
+  Identifier rule_name = ident_tok->get_str ();
+
+  // DEBUG
+  rust_debug ("in macro rules def, about to parse parens.");
+
+  // save delim type to ensure it is reused later
+  AST::DelimType delim_type = AST::PARENS;
+
+  // Map tokens to DelimType
+  t = lexer.peek_token ();
+  switch (t->get_id ())
+    {
+    case LEFT_PAREN:
+      delim_type = AST::PARENS;
+      break;
+    case LEFT_SQUARE:
+      delim_type = AST::SQUARE;
+      break;
+    case LEFT_CURLY:
+      delim_type = AST::CURLY;
+      break;
+    default:
+      add_error (Error (t->get_locus (),
+			"unexpected token %qs - expecting delimiters (for a "
+			"macro rules definition)",
+			t->get_token_description ()));
+
+      return nullptr;
+    }
+  lexer.skip_token ();
+
+  // parse actual macro rules
+  std::vector<AST::MacroRule> macro_rules;
+
+  // must be at least one macro rule, so parse it
+  AST::MacroRule initial_rule = parse_macro_rule ();
+  if (initial_rule.is_error ())
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "required first macro rule in macro rules definition "
+		   "could not be parsed");
+      add_error (std::move (error));
+
+      // skip after somewhere?
+      return nullptr;
+    }
+  macro_rules.push_back (std::move (initial_rule));
+
+  // DEBUG
+  rust_debug ("successfully pushed back initial macro rule");
+
+  t = lexer.peek_token ();
+  // parse macro rules
+  while (t->get_id () == SEMICOLON)
+    {
+      // skip semicolon
+      lexer.skip_token ();
+
+      // don't parse if end of macro rules
+      if (token_id_matches_delims (lexer.peek_token ()->get_id (), delim_type))
+	{
+	  // DEBUG
+	  rust_debug (
+	    "broke out of parsing macro rules loop due to finding delim");
+
+	  break;
+	}
+
+      // try to parse next rule
+      AST::MacroRule rule = parse_macro_rule ();
+      if (rule.is_error ())
+	{
+	  Error error (lexer.peek_token ()->get_locus (),
+		       "failed to parse macro rule in macro rules definition");
+	  add_error (std::move (error));
+
+	  return nullptr;
+	}
+
+      macro_rules.push_back (std::move (rule));
+
+      // DEBUG
+      rust_debug ("successfully pushed back another macro rule");
+
+      t = lexer.peek_token ();
+    }
+
+  // parse end delimiters
+  t = lexer.peek_token ();
+  if (token_id_matches_delims (t->get_id (), delim_type))
+    {
+      // tokens match opening delimiter, so skip.
+      lexer.skip_token ();
+
+      if (delim_type != AST::CURLY)
+	{
+	  // skip semicolon at end of non-curly macro definitions
+	  if (!skip_token (SEMICOLON))
+	    {
+	      // as this is the end, allow recovery (probably) - may change
+	      return std::unique_ptr<AST::MacroRulesDefinition> (
+		new AST::MacroRulesDefinition (
+		  std::move (rule_name), delim_type, std::move (macro_rules),
+		  std::move (outer_attrs), macro_locus));
+	    }
+	}
+
+      return std::unique_ptr<AST::MacroRulesDefinition> (
+	new AST::MacroRulesDefinition (std::move (rule_name), delim_type,
+				       std::move (macro_rules),
+				       std::move (outer_attrs), macro_locus));
+    }
+  else
+    {
+      // tokens don't match opening delimiters, so produce error
+      Error error (t->get_locus (),
+		   "unexpected token %qs - expecting closing delimiter %qs "
+		   "(for a macro rules definition)",
+		   t->get_token_description (),
+		   (delim_type == AST::PARENS
+		      ? ")"
+		      : (delim_type == AST::SQUARE ? "]" : "}")));
+      add_error (std::move (error));
+
+      /* return empty macro definiton despite possibly parsing mostly valid one
+       * - TODO is this a good idea? */
+      return nullptr;
+    }
+}
+
+// Parses a semi-coloned (except for full block) macro invocation item.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::MacroInvocation>
+Parser<ManagedTokenSource>::parse_macro_invocation_semi (
+  AST::AttrVec outer_attrs)
+{
+  Location macro_locus = lexer.peek_token ()->get_locus ();
+  AST::SimplePath path = parse_simple_path ();
+
+  if (!skip_token (EXCLAM))
+    {
+      // skip after somewhere?
+      return nullptr;
+    }
+
+  // save delim type to ensure it is reused later
+  AST::DelimType delim_type = AST::PARENS;
+
+  // Map tokens to DelimType
+  const_TokenPtr t = lexer.peek_token ();
+  switch (t->get_id ())
+    {
+    case LEFT_PAREN:
+      delim_type = AST::PARENS;
+      break;
+    case LEFT_SQUARE:
+      delim_type = AST::SQUARE;
+      break;
+    case LEFT_CURLY:
+      delim_type = AST::CURLY;
+      break;
+    default:
+      add_error (Error (t->get_locus (),
+			"unexpected token %qs - expecting delimiters (for a "
+			"macro invocation semi body)",
+			t->get_token_description ()));
+
+      return nullptr;
+    }
+  Location tok_tree_locus = t->get_locus ();
+  lexer.skip_token ();
+
+  // parse actual token trees
+  std::vector<std::unique_ptr<AST::TokenTree>> token_trees;
+  auto delim_open
+    = std::unique_ptr<AST::Token> (new AST::Token (std::move (t)));
+  token_trees.push_back (std::move (delim_open));
+
+  t = lexer.peek_token ();
+  // parse token trees until the initial delimiter token is found again
+  while (!token_id_matches_delims (t->get_id (), delim_type))
+    {
+      std::unique_ptr<AST::TokenTree> tree = parse_token_tree ();
+
+      if (tree == nullptr)
+	{
+	  Error error (t->get_locus (),
+		       "failed to parse token tree for macro invocation semi "
+		       "- found %qs",
+		       t->get_token_description ());
+	  add_error (std::move (error));
+
+	  return nullptr;
+	}
+
+      token_trees.push_back (std::move (tree));
+
+      t = lexer.peek_token ();
+    }
+  auto delim_close
+    = std::unique_ptr<AST::Token> (new AST::Token (std::move (t)));
+  token_trees.push_back (std::move (delim_close));
+
+  AST::DelimTokenTree delim_tok_tree (delim_type, std::move (token_trees),
+				      tok_tree_locus);
+  AST::MacroInvocData invoc_data (std::move (path), std::move (delim_tok_tree));
+
+  // parse end delimiters
+  t = lexer.peek_token ();
+  if (token_id_matches_delims (t->get_id (), delim_type))
+    {
+      // tokens match opening delimiter, so skip.
+      lexer.skip_token ();
+
+      if (delim_type != AST::CURLY)
+	{
+	  // skip semicolon at end of non-curly macro invocation semis
+	  if (!skip_token (SEMICOLON))
+	    {
+	      // as this is the end, allow recovery (probably) - may change
+
+	      return std::unique_ptr<AST::MacroInvocation> (
+		new AST::MacroInvocation (std::move (invoc_data),
+					  std::move (outer_attrs), macro_locus,
+					  true));
+	    }
+	}
+
+      // DEBUG:
+      rust_debug ("skipped token is '%s', next token (current peek) is '%s'",
+		  t->get_token_description (),
+		  lexer.peek_token ()->get_token_description ());
+
+      return std::unique_ptr<AST::MacroInvocation> (
+	new AST::MacroInvocation (std::move (invoc_data),
+				  std::move (outer_attrs), macro_locus, true));
+    }
+  else
+    {
+      // tokens don't match opening delimiters, so produce error
+      Error error (t->get_locus (),
+		   "unexpected token %qs - expecting closing delimiter %qs "
+		   "(for a macro invocation semi)",
+		   t->get_token_description (),
+		   (delim_type == AST::PARENS
+		      ? ")"
+		      : (delim_type == AST::SQUARE ? "]" : "}")));
+      add_error (std::move (error));
+
+      /* return empty macro invocation despite possibly parsing mostly valid one
+       * - TODO is this a good idea? */
+      return nullptr;
+    }
+}
+
+// Parses a non-semicoloned macro invocation (i.e. as pattern or expression).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::MacroInvocation>
+Parser<ManagedTokenSource>::parse_macro_invocation (AST::AttrVec outer_attrs)
+{
+  // parse macro path
+  AST::SimplePath macro_path = parse_simple_path ();
+  if (macro_path.is_empty ())
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "failed to parse macro invocation path");
+      add_error (std::move (error));
+
+      // skip?
+      return nullptr;
+    }
+
+  if (!skip_token (EXCLAM))
+    {
+      // skip after somewhere?
+      return nullptr;
+    }
+
+  // parse internal delim token tree
+  AST::DelimTokenTree delim_tok_tree = parse_delim_token_tree ();
+
+  Location macro_locus = macro_path.get_locus ();
+
+  return std::unique_ptr<AST::MacroInvocation> (
+    new AST::MacroInvocation (AST::MacroInvocData (std::move (macro_path),
+						   std::move (delim_tok_tree)),
+			      std::move (outer_attrs), macro_locus));
+}
+
+// Parses a macro rule definition - does not parse semicolons.
+template <typename ManagedTokenSource>
+AST::MacroRule
+Parser<ManagedTokenSource>::parse_macro_rule ()
+{
+  Location locus = lexer.peek_token ()->get_locus ();
+
+  // parse macro matcher
+  AST::MacroMatcher matcher = parse_macro_matcher ();
+
+  if (matcher.is_error ())
+    return AST::MacroRule::create_error (locus);
+
+  if (!skip_token (MATCH_ARROW))
+    {
+      // skip after somewhere?
+      return AST::MacroRule::create_error (locus);
+    }
+
+  // parse transcriber (this is just a delim token tree)
+  Location token_tree_loc = lexer.peek_token ()->get_locus ();
+  AST::MacroTranscriber transcriber (parse_delim_token_tree (), token_tree_loc);
+
+  return AST::MacroRule (std::move (matcher), std::move (transcriber), locus);
+}
+
+// Parses a macro matcher (part of a macro rule definition).
+template <typename ManagedTokenSource>
+AST::MacroMatcher
+Parser<ManagedTokenSource>::parse_macro_matcher ()
+{
+  // save delim type to ensure it is reused later
+  AST::DelimType delim_type = AST::PARENS;
+
+  // DEBUG
+  rust_debug ("begun parsing macro matcher");
+
+  // Map tokens to DelimType
+  const_TokenPtr t = lexer.peek_token ();
+  Location locus = t->get_locus ();
+  switch (t->get_id ())
+    {
+    case LEFT_PAREN:
+      delim_type = AST::PARENS;
+      break;
+    case LEFT_SQUARE:
+      delim_type = AST::SQUARE;
+      break;
+    case LEFT_CURLY:
+      delim_type = AST::CURLY;
+      break;
+    default:
+      add_error (Error (
+	t->get_locus (),
+	"unexpected token %qs - expecting delimiters (for a macro matcher)",
+	t->get_token_description ()));
+
+      return AST::MacroMatcher::create_error (t->get_locus ());
+    }
+  lexer.skip_token ();
+
+  // parse actual macro matches
+  std::vector<std::unique_ptr<AST::MacroMatch>> matches;
+  // Set of possible preceding macro matches to make sure follow-set
+  // restrictions are respected.
+  // TODO: Consider using std::reference_wrapper instead of raw pointers?
+  std::vector<const AST::MacroMatch *> last_matches;
+
+  t = lexer.peek_token ();
+  // parse token trees until the initial delimiter token is found again
+  while (!token_id_matches_delims (t->get_id (), delim_type))
+    {
+      std::unique_ptr<AST::MacroMatch> match = parse_macro_match ();
+
+      if (match == nullptr)
+	{
+	  Error error (
+	    t->get_locus (),
+	    "failed to parse macro match for macro matcher - found %qs",
+	    t->get_token_description ());
+	  add_error (std::move (error));
+
+	  return AST::MacroMatcher::create_error (t->get_locus ());
+	}
+
+      if (matches.size () > 0)
+	{
+	  const auto *last_match = matches.back ().get ();
+
+	  // We want to check if we are dealing with a zeroable repetition
+	  bool zeroable = false;
+	  if (last_match->get_macro_match_type ()
+	      == AST::MacroMatch::MacroMatchType::Repetition)
+	    {
+	      auto repetition
+		= static_cast<const AST::MacroMatchRepetition *> (last_match);
+
+	      if (repetition->get_op ()
+		  != AST::MacroMatchRepetition::MacroRepOp::ONE_OR_MORE)
+		zeroable = true;
+	    }
+
+	  if (!zeroable)
+	    last_matches.clear ();
+
+	  last_matches.emplace_back (last_match);
+
+	  for (auto last : last_matches)
+	    if (!is_match_compatible (*last, *match))
+	      return AST::MacroMatcher::create_error (
+		match->get_match_locus ());
+	}
+
+      matches.push_back (std::move (match));
+
+      // DEBUG
+      rust_debug ("pushed back a match in macro matcher");
+
+      t = lexer.peek_token ();
+    }
+
+  // parse end delimiters
+  t = lexer.peek_token ();
+  if (token_id_matches_delims (t->get_id (), delim_type))
+    {
+      // tokens match opening delimiter, so skip.
+      lexer.skip_token ();
+
+      return AST::MacroMatcher (delim_type, std::move (matches), locus);
+    }
+  else
+    {
+      // tokens don't match opening delimiters, so produce error
+      Error error (t->get_locus (),
+		   "unexpected token %qs - expecting closing delimiter %qs "
+		   "(for a macro matcher)",
+		   t->get_token_description (),
+		   (delim_type == AST::PARENS
+		      ? ")"
+		      : (delim_type == AST::SQUARE ? "]" : "}")));
+      add_error (std::move (error));
+
+      /* return error macro matcher despite possibly parsing mostly correct one?
+       * TODO is this the best idea? */
+      return AST::MacroMatcher::create_error (t->get_locus ());
+    }
+}
+
+// Parses a macro match (syntax match inside a matcher in a macro rule).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::MacroMatch>
+Parser<ManagedTokenSource>::parse_macro_match ()
+{
+  // branch based on token available
+  const_TokenPtr t = lexer.peek_token ();
+  switch (t->get_id ())
+    {
+    case LEFT_PAREN:
+    case LEFT_SQUARE:
+      case LEFT_CURLY: {
+	// must be macro matcher as delimited
+	AST::MacroMatcher matcher = parse_macro_matcher ();
+	if (matcher.is_error ())
+	  {
+	    Error error (lexer.peek_token ()->get_locus (),
+			 "failed to parse macro matcher in macro match");
+	    add_error (std::move (error));
+
+	    return nullptr;
+	  }
+	return std::unique_ptr<AST::MacroMatcher> (
+	  new AST::MacroMatcher (std::move (matcher)));
+      }
+      case DOLLAR_SIGN: {
+	// have to do more lookahead to determine if fragment or repetition
+	const_TokenPtr t2 = lexer.peek_token (1);
+	switch (t2->get_id ())
+	  {
+	  case ABSTRACT:
+	  case AS:
+	  case ASYNC:
+	  case BECOME:
+	  case BOX:
+	  case BREAK:
+	  case CONST:
+	  case CONTINUE:
+	  case CRATE:
+	  case DO:
+	  case DYN:
+	  case ELSE:
+	  case ENUM_TOK:
+	  case EXTERN_TOK:
+	  case FALSE_LITERAL:
+	  case FINAL_TOK:
+	  case FN_TOK:
+	  case FOR:
+	  case IF:
+	  case IMPL:
+	  case IN:
+	  case LET:
+	  case LOOP:
+	  case MACRO:
+	  case MATCH_TOK:
+	  case MOD:
+	  case MOVE:
+	  case MUT:
+	  case OVERRIDE_TOK:
+	  case PRIV:
+	  case PUB:
+	  case REF:
+	  case RETURN_TOK:
+	  case SELF_ALIAS:
+	  case SELF:
+	  case STATIC_TOK:
+	  case STRUCT_TOK:
+	  case SUPER:
+	  case TRAIT:
+	  case TRUE_LITERAL:
+	  case TRY:
+	  case TYPE:
+	  case TYPEOF:
+	  case UNSAFE:
+	  case UNSIZED:
+	  case USE:
+	  case VIRTUAL:
+	  case WHERE:
+	  case WHILE:
+	  case YIELD:
+	  case IDENTIFIER:
+	    // macro fragment
+	    return parse_macro_match_fragment ();
+	  case LEFT_PAREN:
+	    // macro repetition
+	    return parse_macro_match_repetition ();
+	  default:
+	    // error: unrecognised
+	    add_error (
+	      Error (t2->get_locus (),
+		     "unrecognised token combination %<$%s%> at start of "
+		     "macro match - did you mean %<$identifier%> or %<$(%>?",
+		     t2->get_token_description ()));
+
+	    // skip somewhere?
+	    return nullptr;
+	  }
+      }
+    case RIGHT_PAREN:
+    case RIGHT_SQUARE:
+    case RIGHT_CURLY:
+      // not allowed
+      add_error (Error (
+	t->get_locus (),
+	"closing delimiters like %qs are not allowed at the start of a macro "
+	"match",
+	t->get_token_description ()));
+
+      // skip somewhere?
+      return nullptr;
+    default:
+      // just the token
+      lexer.skip_token ();
+      return std::unique_ptr<AST::Token> (new AST::Token (std::move (t)));
+    }
+}
+
+// Parses a fragment macro match.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::MacroMatchFragment>
+Parser<ManagedTokenSource>::parse_macro_match_fragment ()
+{
+  Location fragment_locus = lexer.peek_token ()->get_locus ();
+  skip_token (DOLLAR_SIGN);
+
+  Identifier ident = "";
+  auto identifier = lexer.peek_token ();
+  if (identifier->has_str ())
+    ident = identifier->get_str ();
+  else
+    ident = std::string (token_id_to_str (identifier->get_id ()));
+
+  if (ident.empty ())
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "missing identifier in macro match fragment");
+      add_error (std::move (error));
+
+      return nullptr;
+    }
+  skip_token (identifier->get_id ());
+
+  if (!skip_token (COLON))
+    {
+      // skip after somewhere?
+      return nullptr;
+    }
+
+  // get MacroFragSpec for macro
+  const_TokenPtr t = expect_token (IDENTIFIER);
+  if (t == nullptr)
+    return nullptr;
+
+  AST::MacroFragSpec frag
+    = AST::MacroFragSpec::get_frag_spec_from_str (t->get_str ());
+  if (frag.is_error ())
+    {
+      Error error (t->get_locus (),
+		   "invalid fragment specifier %qs in fragment macro match",
+		   t->get_str ().c_str ());
+      add_error (std::move (error));
+
+      return nullptr;
+    }
+
+  return std::unique_ptr<AST::MacroMatchFragment> (
+    new AST::MacroMatchFragment (std::move (ident), frag, fragment_locus));
+}
+
+// Parses a repetition macro match.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::MacroMatchRepetition>
+Parser<ManagedTokenSource>::parse_macro_match_repetition ()
+{
+  skip_token (DOLLAR_SIGN);
+  skip_token (LEFT_PAREN);
+
+  std::vector<std::unique_ptr<AST::MacroMatch>> matches;
+
+  // parse required first macro match
+  std::unique_ptr<AST::MacroMatch> initial_match = parse_macro_match ();
+  if (initial_match == nullptr)
+    {
+      Error error (
+	lexer.peek_token ()->get_locus (),
+	"could not parse required first macro match in macro match repetition");
+      add_error (std::move (error));
+
+      // skip after somewhere?
+      return nullptr;
+    }
+  matches.push_back (std::move (initial_match));
+
+  // parse optional later macro matches
+  const_TokenPtr t = lexer.peek_token ();
+  while (t->get_id () != RIGHT_PAREN)
+    {
+      std::unique_ptr<AST::MacroMatch> match = parse_macro_match ();
+
+      if (match == nullptr)
+	{
+	  Error error (lexer.peek_token ()->get_locus (),
+		       "failed to parse macro match in macro match repetition");
+	  add_error (std::move (error));
+
+	  return nullptr;
+	}
+
+      matches.push_back (std::move (match));
+
+      t = lexer.peek_token ();
+    }
+
+  if (!skip_token (RIGHT_PAREN))
+    {
+      // skip after somewhere?
+      return nullptr;
+    }
+
+  t = lexer.peek_token ();
+  // see if separator token exists
+  std::unique_ptr<AST::Token> separator = nullptr;
+  switch (t->get_id ())
+    {
+    // repetition operators
+    case ASTERISK:
+    case PLUS:
+    case QUESTION_MARK:
+    // delimiters
+    case LEFT_PAREN:
+    case LEFT_CURLY:
+    case LEFT_SQUARE:
+    case RIGHT_PAREN:
+    case RIGHT_CURLY:
+    case RIGHT_SQUARE:
+      // separator does not exist, so still null and don't skip token
+      break;
+    default:
+      // separator does exist
+      separator = std::unique_ptr<AST::Token> (new AST::Token (std::move (t)));
+      lexer.skip_token ();
+      break;
+    }
+
+  // parse repetition operator
+  t = lexer.peek_token ();
+  AST::MacroMatchRepetition::MacroRepOp op = AST::MacroMatchRepetition::NONE;
+  switch (t->get_id ())
+    {
+    case ASTERISK:
+      op = AST::MacroMatchRepetition::ANY;
+      lexer.skip_token ();
+      break;
+    case PLUS:
+      op = AST::MacroMatchRepetition::ONE_OR_MORE;
+      lexer.skip_token ();
+      break;
+    case QUESTION_MARK:
+      op = AST::MacroMatchRepetition::ZERO_OR_ONE;
+      lexer.skip_token ();
+      break;
+    default:
+      add_error (
+	Error (t->get_locus (),
+	       "expected macro repetition operator (%<*%>, %<+%>, or %<?%>) in "
+	       "macro match - found %qs",
+	       t->get_token_description ()));
+
+      // skip after somewhere?
+      return nullptr;
+    }
+
+  return std::unique_ptr<AST::MacroMatchRepetition> (
+    new AST::MacroMatchRepetition (std::move (matches), op,
+				   std::move (separator), t->get_locus ()));
+}
+
+/* Parses a visibility syntactical production (i.e. creating a non-default
+ * visibility) */
+template <typename ManagedTokenSource>
+AST::Visibility
+Parser<ManagedTokenSource>::parse_visibility ()
+{
+  // check for no visibility
+  if (lexer.peek_token ()->get_id () != PUB)
+    {
+      return AST::Visibility::create_private ();
+    }
+
+  lexer.skip_token ();
+
+  // create simple pub visibility if no parentheses
+  if (lexer.peek_token ()->get_id () != LEFT_PAREN)
+    {
+      return AST::Visibility::create_public ();
+      // or whatever
+    }
+
+  lexer.skip_token ();
+
+  const_TokenPtr t = lexer.peek_token ();
+  auto path_loc = t->get_locus ();
+
+  switch (t->get_id ())
+    {
+    case CRATE:
+      lexer.skip_token ();
+
+      skip_token (RIGHT_PAREN);
+
+      return AST::Visibility::create_crate (path_loc);
+    case SELF:
+      lexer.skip_token ();
+
+      skip_token (RIGHT_PAREN);
+
+      return AST::Visibility::create_self (path_loc);
+    case SUPER:
+      lexer.skip_token ();
+
+      skip_token (RIGHT_PAREN);
+
+      return AST::Visibility::create_super (path_loc);
+      case IN: {
+	lexer.skip_token ();
+
+	// parse the "in" path as well
+	AST::SimplePath path = parse_simple_path ();
+	if (path.is_empty ())
+	  {
+	    Error error (lexer.peek_token ()->get_locus (),
+			 "missing path in pub(in path) visibility");
+	    add_error (std::move (error));
+
+	    // skip after somewhere?
+	    return AST::Visibility::create_error ();
+	  }
+
+	skip_token (RIGHT_PAREN);
+
+	return AST::Visibility::create_in_path (std::move (path));
+      }
+    default:
+      add_error (Error (t->get_locus (), "unexpected token %qs in visibility",
+			t->get_token_description ()));
+
+      lexer.skip_token ();
+      return AST::Visibility::create_error ();
+    }
+}
+
+// Parses a module - either a bodied module or a module defined in another file.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::Module>
+Parser<ManagedTokenSource>::parse_module (AST::Visibility vis,
+					  AST::AttrVec outer_attrs)
+{
+  Location locus = lexer.peek_token ()->get_locus ();
+  skip_token (MOD);
+
+  const_TokenPtr module_name = expect_token (IDENTIFIER);
+  if (module_name == nullptr)
+    {
+      return nullptr;
+    }
+  Identifier name = module_name->get_str ();
+
+  const_TokenPtr t = lexer.peek_token ();
+
+  switch (t->get_id ())
+    {
+    case SEMICOLON:
+      lexer.skip_token ();
+
+      // Construct an external module
+      return std::unique_ptr<AST::Module> (
+	new AST::Module (std::move (name), std::move (vis),
+			 std::move (outer_attrs), locus, lexer.get_filename (),
+			 inline_module_stack));
+      case LEFT_CURLY: {
+	lexer.skip_token ();
+
+	// parse inner attributes
+	AST::AttrVec inner_attrs = parse_inner_attributes ();
+
+	std::string module_path_name
+	  = extract_module_path (inner_attrs, outer_attrs, name);
+	InlineModuleStackScope scope (*this, std::move (module_path_name));
+
+	// parse items
+	std::vector<std::unique_ptr<AST::Item>> items;
+	const_TokenPtr tok = lexer.peek_token ();
+	while (tok->get_id () != RIGHT_CURLY)
+	  {
+	    std::unique_ptr<AST::Item> item = parse_item (false);
+	    if (item == nullptr)
+	      {
+		Error error (tok->get_locus (),
+			     "failed to parse item in module");
+		add_error (std::move (error));
+
+		return nullptr;
+	      }
+
+	    items.push_back (std::move (item));
+
+	    tok = lexer.peek_token ();
+	  }
+
+	if (!skip_token (RIGHT_CURLY))
+	  {
+	    // skip somewhere?
+	    return nullptr;
+	  }
+
+	return std::unique_ptr<AST::Module> (
+	  new AST::Module (std::move (name), locus, std::move (items),
+			   std::move (vis), std::move (inner_attrs),
+			   std::move (outer_attrs))); // module name?
+      }
+    default:
+      add_error (
+	Error (t->get_locus (),
+	       "unexpected token %qs in module declaration/definition item",
+	       t->get_token_description ()));
+
+      lexer.skip_token ();
+      return nullptr;
+    }
+}
+
+// Parses an extern crate declaration (dependency on external crate)
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ExternCrate>
+Parser<ManagedTokenSource>::parse_extern_crate (AST::Visibility vis,
+						AST::AttrVec outer_attrs)
+{
+  Location locus = lexer.peek_token ()->get_locus ();
+  if (!skip_token (EXTERN_TOK))
+    {
+      skip_after_semicolon ();
+      return nullptr;
+    }
+
+  if (!skip_token (CRATE))
+    {
+      skip_after_semicolon ();
+      return nullptr;
+    }
+
+  /* parse crate reference name - this has its own syntactical rule in reference
+   * but seems to not be used elsewhere, so i'm putting it here */
+  const_TokenPtr crate_name_tok = lexer.peek_token ();
+  std::string crate_name;
+
+  switch (crate_name_tok->get_id ())
+    {
+    case IDENTIFIER:
+      crate_name = crate_name_tok->get_str ();
+      lexer.skip_token ();
+      break;
+    case SELF:
+      crate_name = "self";
+      lexer.skip_token ();
+      break;
+    default:
+      add_error (
+	Error (crate_name_tok->get_locus (),
+	       "expecting crate name (identifier or %<self%>), found %qs",
+	       crate_name_tok->get_token_description ()));
+
+      skip_after_semicolon ();
+      return nullptr;
+    }
+
+  // don't parse as clause if it doesn't exist
+  if (lexer.peek_token ()->get_id () == SEMICOLON)
+    {
+      lexer.skip_token ();
+
+      return std::unique_ptr<AST::ExternCrate> (
+	new AST::ExternCrate (std::move (crate_name), std::move (vis),
+			      std::move (outer_attrs), locus));
+    }
+
+  /* parse as clause - this also has its own syntactical rule in reference and
+   * also seems to not be used elsewhere, so including here again. */
+  if (!skip_token (AS))
+    {
+      skip_after_semicolon ();
+      return nullptr;
+    }
+
+  const_TokenPtr as_name_tok = lexer.peek_token ();
+  std::string as_name;
+
+  switch (as_name_tok->get_id ())
+    {
+    case IDENTIFIER:
+      as_name = as_name_tok->get_str ();
+      lexer.skip_token ();
+      break;
+    case UNDERSCORE:
+      as_name = "_";
+      lexer.skip_token ();
+      break;
+    default:
+      add_error (
+	Error (as_name_tok->get_locus (),
+	       "expecting as clause name (identifier or %<_%>), found %qs",
+	       as_name_tok->get_token_description ()));
+
+      skip_after_semicolon ();
+      return nullptr;
+    }
+
+  if (!skip_token (SEMICOLON))
+    {
+      skip_after_semicolon ();
+      return nullptr;
+    }
+
+  return std::unique_ptr<AST::ExternCrate> (
+    new AST::ExternCrate (std::move (crate_name), std::move (vis),
+			  std::move (outer_attrs), locus, std::move (as_name)));
+}
+
+// Parses a use declaration.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::UseDeclaration>
+Parser<ManagedTokenSource>::parse_use_decl (AST::Visibility vis,
+					    AST::AttrVec outer_attrs)
+{
+  Location locus = lexer.peek_token ()->get_locus ();
+  if (!skip_token (USE))
+    {
+      skip_after_semicolon ();
+      return nullptr;
+    }
+
+  // parse use tree, which is required
+  std::unique_ptr<AST::UseTree> use_tree = parse_use_tree ();
+  if (use_tree == nullptr)
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "could not parse use tree in use declaration");
+      add_error (std::move (error));
+
+      skip_after_semicolon ();
+      return nullptr;
+    }
+
+  if (!skip_token (SEMICOLON))
+    {
+      skip_after_semicolon ();
+      return nullptr;
+    }
+
+  return std::unique_ptr<AST::UseDeclaration> (
+    new AST::UseDeclaration (std::move (use_tree), std::move (vis),
+			     std::move (outer_attrs), locus));
+}
+
+// Parses a use tree (which can be recursive and is actually a base class).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::UseTree>
+Parser<ManagedTokenSource>::parse_use_tree ()
+{
+  /* potential syntax definitions in attempt to get algorithm:
+   *  Glob:
+   *      <- SimplePath :: *
+   *      <- :: *
+   *      <- *
+   *  Nested tree thing:
+   *      <- SimplePath :: { COMPLICATED_INNER_TREE_THING }
+   *      <- :: COMPLICATED_INNER_TREE_THING }
+   *      <- { COMPLICATED_INNER_TREE_THING }
+   *  Rebind thing:
+   *      <- SimplePath as IDENTIFIER
+   *      <- SimplePath as _
+   *      <- SimplePath
+   */
+
+  /* current plan of attack: try to parse SimplePath first - if fails, one of
+   * top two then try parse :: - if fails, one of top two. Next is deciding
+   * character for top two. */
+
+  /* Thus, parsing smaller parts of use tree may require feeding into function
+   * via parameters (or could handle all in this single function because other
+   * use tree types aren't recognised as separate in the spec) */
+
+  // TODO: I think this function is too complex, probably should split it
+
+  Location locus = lexer.peek_token ()->get_locus ();
+
+  // bool has_path = false;
+  AST::SimplePath path = parse_simple_path ();
+
+  if (path.is_empty ())
+    {
+      // has no path, so must be glob or nested tree UseTree type
+
+      bool is_global = false;
+
+      // check for global scope resolution operator
+      if (lexer.peek_token ()->get_id () == SCOPE_RESOLUTION)
+	{
+	  lexer.skip_token ();
+	  is_global = true;
+	}
+
+      const_TokenPtr t = lexer.peek_token ();
+      switch (t->get_id ())
+	{
+	case ASTERISK:
+	  // glob UseTree type
+	  lexer.skip_token ();
+
+	  if (is_global)
+	    return std::unique_ptr<AST::UseTreeGlob> (
+	      new AST::UseTreeGlob (AST::UseTreeGlob::GLOBAL,
+				    AST::SimplePath::create_empty (), locus));
+	  else
+	    return std::unique_ptr<AST::UseTreeGlob> (
+	      new AST::UseTreeGlob (AST::UseTreeGlob::NO_PATH,
+				    AST::SimplePath::create_empty (), locus));
+	  case LEFT_CURLY: {
+	    // nested tree UseTree type
+	    lexer.skip_token ();
+
+	    std::vector<std::unique_ptr<AST::UseTree>> use_trees;
+
+	    const_TokenPtr t = lexer.peek_token ();
+	    while (t->get_id () != RIGHT_CURLY)
+	      {
+		std::unique_ptr<AST::UseTree> use_tree = parse_use_tree ();
+		if (use_tree == nullptr)
+		  {
+		    break;
+		  }
+
+		use_trees.push_back (std::move (use_tree));
+
+		if (lexer.peek_token ()->get_id () != COMMA)
+		  break;
+
+		lexer.skip_token ();
+		t = lexer.peek_token ();
+	      }
+
+	    // skip end curly delimiter
+	    if (!skip_token (RIGHT_CURLY))
+	      {
+		// skip after somewhere?
+		return nullptr;
+	      }
+
+	    if (is_global)
+	      return std::unique_ptr<AST::UseTreeList> (
+		new AST::UseTreeList (AST::UseTreeList::GLOBAL,
+				      AST::SimplePath::create_empty (),
+				      std::move (use_trees), locus));
+	    else
+	      return std::unique_ptr<AST::UseTreeList> (
+		new AST::UseTreeList (AST::UseTreeList::NO_PATH,
+				      AST::SimplePath::create_empty (),
+				      std::move (use_trees), locus));
+	  }
+	case AS:
+	  // this is not allowed
+	  add_error (Error (
+	    t->get_locus (),
+	    "use declaration with rebind %<as%> requires a valid simple path - "
+	    "none found"));
+
+	  skip_after_semicolon ();
+	  return nullptr;
+	default:
+	  add_error (Error (t->get_locus (),
+			    "unexpected token %qs in use tree with "
+			    "no valid simple path (i.e. list"
+			    " or glob use tree)",
+			    t->get_token_description ()));
+
+	  skip_after_semicolon ();
+	  return nullptr;
+	}
+    }
+  else
+    {
+      /* Due to aforementioned implementation issues, the trailing :: token is
+       * consumed by the path, so it can not be used as a disambiguator.
+       * NOPE, not true anymore - TODO what are the consequences of this? */
+
+      const_TokenPtr t = lexer.peek_token ();
+      switch (t->get_id ())
+	{
+	case ASTERISK:
+	  // glob UseTree type
+	  lexer.skip_token ();
+
+	  return std::unique_ptr<AST::UseTreeGlob> (
+	    new AST::UseTreeGlob (AST::UseTreeGlob::PATH_PREFIXED,
+				  std::move (path), locus));
+	  case LEFT_CURLY: {
+	    // nested tree UseTree type
+	    lexer.skip_token ();
+
+	    std::vector<std::unique_ptr<AST::UseTree>> use_trees;
+
+	    // TODO: think of better control structure
+	    const_TokenPtr t = lexer.peek_token ();
+	    while (t->get_id () != RIGHT_CURLY)
+	      {
+		std::unique_ptr<AST::UseTree> use_tree = parse_use_tree ();
+		if (use_tree == nullptr)
+		  {
+		    break;
+		  }
+
+		use_trees.push_back (std::move (use_tree));
+
+		if (lexer.peek_token ()->get_id () != COMMA)
+		  break;
+
+		lexer.skip_token ();
+		t = lexer.peek_token ();
+	      }
+
+	    // skip end curly delimiter
+	    if (!skip_token (RIGHT_CURLY))
+	      {
+		// skip after somewhere?
+		return nullptr;
+	      }
+
+	    return std::unique_ptr<AST::UseTreeList> (
+	      new AST::UseTreeList (AST::UseTreeList::PATH_PREFIXED,
+				    std::move (path), std::move (use_trees),
+				    locus));
+	  }
+	  case AS: {
+	    // rebind UseTree type
+	    lexer.skip_token ();
+
+	    const_TokenPtr t = lexer.peek_token ();
+	    switch (t->get_id ())
+	      {
+	      case IDENTIFIER:
+		// skip lexer token
+		lexer.skip_token ();
+
+		return std::unique_ptr<AST::UseTreeRebind> (
+		  new AST::UseTreeRebind (AST::UseTreeRebind::IDENTIFIER,
+					  std::move (path), locus,
+					  t->get_str ()));
+	      case UNDERSCORE:
+		// skip lexer token
+		lexer.skip_token ();
+
+		return std::unique_ptr<AST::UseTreeRebind> (
+		  new AST::UseTreeRebind (AST::UseTreeRebind::WILDCARD,
+					  std::move (path), locus, "_"));
+	      default:
+		add_error (Error (
+		  t->get_locus (),
+		  "unexpected token %qs in use tree with as clause - expected "
+		  "identifier or %<_%>",
+		  t->get_token_description ()));
+
+		skip_after_semicolon ();
+		return nullptr;
+	      }
+	  }
+	case SEMICOLON:
+	  // rebind UseTree type without rebinding - path only
+
+	  // don't skip semicolon - handled in parse_use_tree
+	  // lexer.skip_token();
+
+	  return std::unique_ptr<AST::UseTreeRebind> (
+	    new AST::UseTreeRebind (AST::UseTreeRebind::NONE, std::move (path),
+				    locus));
+	case COMMA:
+	case RIGHT_CURLY:
+	  // this may occur in recursive calls - assume it is ok and ignore it
+	  return std::unique_ptr<AST::UseTreeRebind> (
+	    new AST::UseTreeRebind (AST::UseTreeRebind::NONE, std::move (path),
+				    locus));
+	default:
+	  add_error (Error (t->get_locus (),
+			    "unexpected token %qs in use tree with valid path",
+			    t->get_token_description ()));
+
+	  // skip_after_semicolon();
+	  return nullptr;
+	}
+    }
+}
+
+// Parses a function (not a method).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::Function>
+Parser<ManagedTokenSource>::parse_function (AST::Visibility vis,
+					    AST::AttrVec outer_attrs)
+{
+  Location locus = lexer.peek_token ()->get_locus ();
+  // Get qualifiers for function if they exist
+  AST::FunctionQualifiers qualifiers = parse_function_qualifiers ();
+
+  skip_token (FN_TOK);
+
+  // Save function name token
+  const_TokenPtr function_name_tok = expect_token (IDENTIFIER);
+  if (function_name_tok == nullptr)
+    {
+      skip_after_next_block ();
+      return nullptr;
+    }
+  Identifier function_name = function_name_tok->get_str ();
+
+  // parse generic params - if exist
+  std::vector<std::unique_ptr<AST::GenericParam>> generic_params
+    = parse_generic_params_in_angles ();
+
+  if (!skip_token (LEFT_PAREN))
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "function declaration missing opening parentheses before "
+		   "parameter list");
+      add_error (std::move (error));
+
+      skip_after_next_block ();
+      return nullptr;
+    }
+
+  // parse function parameters (only if next token isn't right paren)
+  std::vector<AST::FunctionParam> function_params;
+  if (lexer.peek_token ()->get_id () != RIGHT_PAREN)
+    function_params
+      = parse_function_params ([] (TokenId id) { return id == RIGHT_PAREN; });
+
+  if (!skip_token (RIGHT_PAREN))
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "function declaration missing closing parentheses after "
+		   "parameter list");
+      add_error (std::move (error));
+
+      skip_after_next_block ();
+      return nullptr;
+    }
+
+  // parse function return type - if exists
+  std::unique_ptr<AST::Type> return_type = parse_function_return_type ();
+
+  // parse where clause - if exists
+  AST::WhereClause where_clause = parse_where_clause ();
+
+  // parse block expression
+  std::unique_ptr<AST::BlockExpr> block_expr = parse_block_expr ();
+
+  return std::unique_ptr<AST::Function> (
+    new AST::Function (std::move (function_name), std::move (qualifiers),
+		       std::move (generic_params), std::move (function_params),
+		       std::move (return_type), std::move (where_clause),
+		       std::move (block_expr), std::move (vis),
+		       std::move (outer_attrs), locus));
+}
+
+// Parses function or method qualifiers (i.e. const, unsafe, and extern).
+template <typename ManagedTokenSource>
+AST::FunctionQualifiers
+Parser<ManagedTokenSource>::parse_function_qualifiers ()
+{
+  AsyncConstStatus const_status = NONE;
+  bool has_unsafe = false;
+  bool has_extern = false;
+  std::string abi;
+
+  // Check in order of const, unsafe, then extern
+  const_TokenPtr t = lexer.peek_token ();
+  Location locus = t->get_locus ();
+  switch (t->get_id ())
+    {
+    case CONST:
+      lexer.skip_token ();
+      const_status = CONST_FN;
+      break;
+    case ASYNC:
+      lexer.skip_token ();
+      const_status = ASYNC_FN;
+      break;
+    default:
+      // const status is still none
+      break;
+    }
+
+  if (lexer.peek_token ()->get_id () == UNSAFE)
+    {
+      lexer.skip_token ();
+      has_unsafe = true;
+    }
+
+  if (lexer.peek_token ()->get_id () == EXTERN_TOK)
+    {
+      lexer.skip_token ();
+      has_extern = true;
+
+      // detect optional abi name
+      const_TokenPtr next_tok = lexer.peek_token ();
+      if (next_tok->get_id () == STRING_LITERAL)
+	{
+	  lexer.skip_token ();
+	  abi = next_tok->get_str ();
+	}
+    }
+
+  return AST::FunctionQualifiers (locus, const_status, has_unsafe, has_extern,
+				  std::move (abi));
+}
+
+// Parses generic (lifetime or type) params inside angle brackets (optional).
+template <typename ManagedTokenSource>
+std::vector<std::unique_ptr<AST::GenericParam>>
+Parser<ManagedTokenSource>::parse_generic_params_in_angles ()
+{
+  if (lexer.peek_token ()->get_id () != LEFT_ANGLE)
+    {
+      // seems to be no generic params, so exit with empty vector
+      return std::vector<std::unique_ptr<AST::GenericParam>> ();
+    }
+  lexer.skip_token ();
+
+  // DEBUG:
+  rust_debug ("skipped left angle in generic param");
+
+  std::vector<std::unique_ptr<AST::GenericParam>> generic_params
+    = parse_generic_params (is_right_angle_tok);
+
+  // DEBUG:
+  rust_debug ("finished parsing actual generic params (i.e. inside angles)");
+
+  if (!skip_generics_right_angle ())
+    {
+      // DEBUG
+      rust_debug ("failed to skip generics right angle - returning empty "
+		  "generic params");
+
+      return std::vector<std::unique_ptr<AST::GenericParam>> ();
+    }
+
+  return generic_params;
+}
+
+template <typename ManagedTokenSource>
+template <typename EndTokenPred>
+std::unique_ptr<AST::GenericParam>
+Parser<ManagedTokenSource>::parse_generic_param (EndTokenPred is_end_token)
+{
+  auto token = lexer.peek_token ();
+  auto outer_attrs = parse_outer_attribute ();
+  std::unique_ptr<AST::GenericParam> param;
+
+  switch (token->get_id ())
+    {
+      case LIFETIME: {
+	auto lifetime = parse_lifetime ();
+	if (lifetime.is_error ())
+	  {
+	    rust_error_at (
+	      token->get_locus (),
+	      "failed to parse lifetime in generic parameter list");
+	    return nullptr;
+	  }
+
+	std::vector<AST::Lifetime> lifetime_bounds;
+	if (lexer.peek_token ()->get_id () == COLON)
+	  {
+	    lexer.skip_token ();
+	    // parse required bounds
+	    lifetime_bounds
+	      = parse_lifetime_bounds ([is_end_token] (TokenId id) {
+		  return is_end_token (id) || id == COMMA;
+		});
+	  }
+
+	param = std::unique_ptr<AST::LifetimeParam> (new AST::LifetimeParam (
+	  std::move (lifetime), std::move (lifetime_bounds),
+	  std::move (outer_attrs), token->get_locus ()));
+	break;
+      }
+      case IDENTIFIER: {
+	auto type_ident = token->get_str ();
+	lexer.skip_token ();
+
+	std::vector<std::unique_ptr<AST::TypeParamBound>> type_param_bounds;
+	if (lexer.peek_token ()->get_id () == COLON)
+	  {
+	    lexer.skip_token ();
+
+	    // parse optional type param bounds
+	    type_param_bounds = parse_type_param_bounds ();
+	  }
+
+	std::unique_ptr<AST::Type> type = nullptr;
+	if (lexer.peek_token ()->get_id () == EQUAL)
+	  {
+	    lexer.skip_token ();
+
+	    // parse required type
+	    type = parse_type ();
+	    if (!type)
+	      {
+		rust_error_at (
+		  lexer.peek_token ()->get_locus (),
+		  "failed to parse type in type param in generic params");
+		return nullptr;
+	      }
+	  }
+
+	param = std::unique_ptr<AST::TypeParam> (
+	  new AST::TypeParam (std::move (type_ident), token->get_locus (),
+			      std::move (type_param_bounds), std::move (type),
+			      std::move (outer_attrs)));
+	break;
+      }
+      case CONST: {
+	lexer.skip_token ();
+
+	auto name_token = expect_token (IDENTIFIER);
+
+	if (!name_token || !expect_token (COLON))
+	  return nullptr;
+
+	auto type = parse_type ();
+	if (!type)
+	  return nullptr;
+
+	// optional default value
+	auto default_expr = AST::GenericArg::create_error ();
+	if (lexer.peek_token ()->get_id () == EQUAL)
+	  {
+	    lexer.skip_token ();
+	    auto tok = lexer.peek_token ();
+	    default_expr = parse_generic_arg ();
+
+	    if (default_expr.is_error ())
+	      rust_error_at (tok->get_locus (),
+			     "invalid token for start of default value for "
+			     "const generic parameter: expected %<block%>, "
+			     "%<identifier%> or %<literal%>, got %qs",
+			     token_id_to_str (tok->get_id ()));
+
+	    // At this point, we *know* that we are parsing a const
+	    // expression
+	    if (default_expr.get_kind () == AST::GenericArg::Kind::Either)
+	      default_expr = default_expr.disambiguate_to_const ();
+	  }
+
+	param = std::unique_ptr<AST::ConstGenericParam> (
+	  new AST::ConstGenericParam (name_token->get_str (), std::move (type),
+				      default_expr, std::move (outer_attrs),
+				      token->get_locus ()));
+
+	break;
+      }
+    default:
+      // FIXME: Can we clean this last call with a method call?
+      rust_error_at (token->get_locus (),
+		     "unexpected token when parsing generic parameters: %qs",
+		     token->get_str ().c_str ());
+      return nullptr;
+    }
+
+  return param;
+}
+
+/* Parse generic (lifetime or type) params NOT INSIDE ANGLE BRACKETS!!! Almost
+ * always parse_generic_params_in_angles is what is wanted. */
+template <typename ManagedTokenSource>
+template <typename EndTokenPred>
+std::vector<std::unique_ptr<AST::GenericParam>>
+Parser<ManagedTokenSource>::parse_generic_params (EndTokenPred is_end_token)
+{
+  std::vector<std::unique_ptr<AST::GenericParam>> generic_params;
+
+  /* can't parse lifetime and type params separately due to lookahead issues
+   * thus, parse them all here */
+
+  /* HACK: used to retain attribute data if a lifetime param is tentatively
+   * parsed but it turns out to be type param */
+  AST::Attribute parsed_outer_attr = AST::Attribute::create_empty ();
+
+  // Did we parse a generic type param yet
+  auto type_seen = false;
+  // Did the user write a lifetime parameter after a type one
+  auto order_error = false;
+
+  // parse lifetime params
+  while (!is_end_token (lexer.peek_token ()->get_id ()))
+    {
+      auto param = parse_generic_param (is_end_token);
+      if (param)
+	{
+	  // TODO: Handle `Const` here as well if necessary
+	  if (param->get_kind () == AST::GenericParam::Kind::Type)
+	    type_seen = true;
+	  else if (param->get_kind () == AST::GenericParam::Kind::Lifetime
+		   && type_seen)
+	    order_error = true;
+
+	  generic_params.emplace_back (std::move (param));
+	  maybe_skip_token (COMMA);
+	}
+    }
+
+  // FIXME: Add reordering hint
+  if (order_error)
+    rust_error_at (generic_params.front ()->get_locus (),
+		   "invalid order for generic parameters: lifetimes should "
+		   "always come before types");
+
+  generic_params.shrink_to_fit ();
+  return generic_params;
+}
+
+/* Parses lifetime generic parameters (pointers). Will also consume any
+ * trailing comma. No extra checks for end token. */
+template <typename ManagedTokenSource>
+std::vector<std::unique_ptr<AST::LifetimeParam>>
+Parser<ManagedTokenSource>::parse_lifetime_params ()
+{
+  std::vector<std::unique_ptr<AST::LifetimeParam>> lifetime_params;
+
+  while (lexer.peek_token ()->get_id () != END_OF_FILE)
+    {
+      AST::LifetimeParam lifetime_param = parse_lifetime_param ();
+
+      if (lifetime_param.is_error ())
+	{
+	  // can't treat as error as only way to get out with trailing comma
+	  break;
+	}
+
+      lifetime_params.push_back (std::unique_ptr<AST::LifetimeParam> (
+	new AST::LifetimeParam (std::move (lifetime_param))));
+
+      if (lexer.peek_token ()->get_id () != COMMA)
+	break;
+
+      // skip commas, including trailing commas
+      lexer.skip_token ();
+    }
+
+  lifetime_params.shrink_to_fit ();
+
+  return lifetime_params;
+}
+
+/* Parses lifetime generic parameters (pointers). Will also consume any
+ * trailing comma. Has extra is_end_token predicate checking. */
+template <typename ManagedTokenSource>
+template <typename EndTokenPred>
+std::vector<std::unique_ptr<AST::LifetimeParam>>
+Parser<ManagedTokenSource>::parse_lifetime_params (EndTokenPred is_end_token)
+{
+  std::vector<std::unique_ptr<AST::LifetimeParam>> lifetime_params;
+
+  // if end_token is not specified, it defaults to EOF, so should work fine
+  while (!is_end_token (lexer.peek_token ()->get_id ()))
+    {
+      AST::LifetimeParam lifetime_param = parse_lifetime_param ();
+
+      if (lifetime_param.is_error ())
+	{
+	  /* TODO: is it worth throwing away all lifetime params just because
+	   * one failed? */
+	  Error error (lexer.peek_token ()->get_locus (),
+		       "failed to parse lifetime param in lifetime params");
+	  add_error (std::move (error));
+
+	  return {};
+	}
+
+      lifetime_params.push_back (std::unique_ptr<AST::LifetimeParam> (
+	new AST::LifetimeParam (std::move (lifetime_param))));
+
+      if (lexer.peek_token ()->get_id () != COMMA)
+	break;
+
+      // skip commas, including trailing commas
+      lexer.skip_token ();
+    }
+
+  lifetime_params.shrink_to_fit ();
+
+  return lifetime_params;
+}
+
+/* Parses lifetime generic parameters (objects). Will also consume any
+ * trailing comma. No extra checks for end token.
+ * TODO: is this best solution? implements most of the same algorithm. */
+template <typename ManagedTokenSource>
+std::vector<AST::LifetimeParam>
+Parser<ManagedTokenSource>::parse_lifetime_params_objs ()
+{
+  std::vector<AST::LifetimeParam> lifetime_params;
+
+  // bad control structure as end token cannot be guaranteed
+  while (true)
+    {
+      AST::LifetimeParam lifetime_param = parse_lifetime_param ();
+
+      if (lifetime_param.is_error ())
+	{
+	  // not an error as only way to exit if trailing comma
+	  break;
+	}
+
+      lifetime_params.push_back (std::move (lifetime_param));
+
+      if (lexer.peek_token ()->get_id () != COMMA)
+	break;
+
+      // skip commas, including trailing commas
+      lexer.skip_token ();
+    }
+
+  lifetime_params.shrink_to_fit ();
+
+  return lifetime_params;
+}
+
+/* Parses lifetime generic parameters (objects). Will also consume any
+ * trailing comma. Has extra is_end_token predicate checking.
+ * TODO: is this best solution? implements most of the same algorithm. */
+template <typename ManagedTokenSource>
+template <typename EndTokenPred>
+std::vector<AST::LifetimeParam>
+Parser<ManagedTokenSource>::parse_lifetime_params_objs (
+  EndTokenPred is_end_token)
+{
+  std::vector<AST::LifetimeParam> lifetime_params;
+
+  while (!is_end_token (lexer.peek_token ()->get_id ()))
+    {
+      AST::LifetimeParam lifetime_param = parse_lifetime_param ();
+
+      if (lifetime_param.is_error ())
+	{
+	  /* TODO: is it worth throwing away all lifetime params just because
+	   * one failed? */
+	  Error error (lexer.peek_token ()->get_locus (),
+		       "failed to parse lifetime param in lifetime params");
+	  add_error (std::move (error));
+
+	  return {};
+	}
+
+      lifetime_params.push_back (std::move (lifetime_param));
+
+      if (lexer.peek_token ()->get_id () != COMMA)
+	break;
+
+      // skip commas, including trailing commas
+      lexer.skip_token ();
+    }
+
+  lifetime_params.shrink_to_fit ();
+
+  return lifetime_params;
+}
+
+/* Parses a sequence of a certain grammar rule in object form (not pointer or
+ * smart pointer), delimited by commas and ending when 'is_end_token' is
+ * satisfied (templated). Will also consume any trailing comma.
+ * FIXME: this cannot be used due to member function pointer problems (i.e.
+ * parsing_function cannot be specified properly) */
+template <typename ManagedTokenSource>
+template <typename ParseFunction, typename EndTokenPred>
+auto
+Parser<ManagedTokenSource>::parse_non_ptr_sequence (
+  ParseFunction parsing_function, EndTokenPred is_end_token,
+  std::string error_msg) -> std::vector<decltype (parsing_function ())>
+{
+  std::vector<decltype (parsing_function ())> params;
+
+  while (!is_end_token (lexer.peek_token ()->get_id ()))
+    {
+      auto param = parsing_function ();
+
+      if (param.is_error ())
+	{
+	  // TODO: is it worth throwing away all params just because one
+	  // failed?
+	  Error error (lexer.peek_token ()->get_locus (),
+		       std::move (error_msg));
+	  add_error (std::move (error));
+
+	  return {};
+	}
+
+      params.push_back (std::move (param));
+
+      if (lexer.peek_token ()->get_id () != COMMA)
+	break;
+
+      // skip commas, including trailing commas
+      lexer.skip_token ();
+    }
+
+  params.shrink_to_fit ();
+
+  return params;
+}
+
+/* Parses a single lifetime generic parameter (not including comma). */
+template <typename ManagedTokenSource>
+AST::LifetimeParam
+Parser<ManagedTokenSource>::parse_lifetime_param ()
+{
+  // parse outer attribute, which is optional and may not exist
+  AST::Attribute outer_attr = parse_outer_attribute ();
+
+  // save lifetime token - required
+  const_TokenPtr lifetime_tok = lexer.peek_token ();
+  if (lifetime_tok->get_id () != LIFETIME)
+    {
+      // if lifetime is missing, must not be a lifetime param, so return null
+      return AST::LifetimeParam::create_error ();
+    }
+  lexer.skip_token ();
+  /* TODO: does this always create a named lifetime? or can a different type
+   * be made? */
+  AST::Lifetime lifetime (AST::Lifetime::NAMED, lifetime_tok->get_str (),
+			  lifetime_tok->get_locus ());
+
+  // parse lifetime bounds, if it exists
+  std::vector<AST::Lifetime> lifetime_bounds;
+  if (lexer.peek_token ()->get_id () == COLON)
+    {
+      // parse lifetime bounds
+      lifetime_bounds = parse_lifetime_bounds ();
+      // TODO: have end token passed in?
+    }
+
+  return AST::LifetimeParam (std::move (lifetime), std::move (lifetime_bounds),
+			     std::move (outer_attr),
+			     lifetime_tok->get_locus ());
+}
+
+// Parses type generic parameters. Will also consume any trailing comma.
+template <typename ManagedTokenSource>
+std::vector<std::unique_ptr<AST::TypeParam>>
+Parser<ManagedTokenSource>::parse_type_params ()
+{
+  std::vector<std::unique_ptr<AST::TypeParam>> type_params;
+
+  // infinite loop with break on failure as no info on ending token
+  while (true)
+    {
+      std::unique_ptr<AST::TypeParam> type_param = parse_type_param ();
+
+      if (type_param == nullptr)
+	{
+	  // break if fails to parse
+	  break;
+	}
+
+      type_params.push_back (std::move (type_param));
+
+      if (lexer.peek_token ()->get_id () != COMMA)
+	break;
+
+      // skip commas, including trailing commas
+      lexer.skip_token ();
+    }
+
+  type_params.shrink_to_fit ();
+  return type_params;
+}
+
+// Parses type generic parameters. Will also consume any trailing comma.
+template <typename ManagedTokenSource>
+template <typename EndTokenPred>
+std::vector<std::unique_ptr<AST::TypeParam>>
+Parser<ManagedTokenSource>::parse_type_params (EndTokenPred is_end_token)
+{
+  std::vector<std::unique_ptr<AST::TypeParam>> type_params;
+
+  while (!is_end_token (lexer.peek_token ()->get_id ()))
+    {
+      std::unique_ptr<AST::TypeParam> type_param = parse_type_param ();
+
+      if (type_param == nullptr)
+	{
+	  Error error (lexer.peek_token ()->get_locus (),
+		       "failed to parse type param in type params");
+	  add_error (std::move (error));
+
+	  return {};
+	}
+
+      type_params.push_back (std::move (type_param));
+
+      if (lexer.peek_token ()->get_id () != COMMA)
+	break;
+
+      // skip commas, including trailing commas
+      lexer.skip_token ();
+    }
+
+  type_params.shrink_to_fit ();
+  return type_params;
+  /* TODO: this shares most code with parse_lifetime_params - good place to
+   * use template (i.e. parse_non_ptr_sequence if doable) */
+}
+
+/* Parses a single type (generic) parameter, not including commas. May change
+ * to return value. */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::TypeParam>
+Parser<ManagedTokenSource>::parse_type_param ()
+{
+  // parse outer attribute, which is optional and may not exist
+  AST::Attribute outer_attr = parse_outer_attribute ();
+
+  const_TokenPtr identifier_tok = lexer.peek_token ();
+  if (identifier_tok->get_id () != IDENTIFIER)
+    {
+      // return null as type param can't exist without this required
+      // identifier
+      return nullptr;
+    }
+  // TODO: create identifier from identifier token
+  Identifier ident = identifier_tok->get_str ();
+  lexer.skip_token ();
+
+  // parse type param bounds (if they exist)
+  std::vector<std::unique_ptr<AST::TypeParamBound>> type_param_bounds;
+  if (lexer.peek_token ()->get_id () == COLON)
+    {
+      lexer.skip_token ();
+
+      // parse type param bounds, which may or may not exist
+      type_param_bounds = parse_type_param_bounds ();
+    }
+
+  // parse type (if it exists)
+  std::unique_ptr<AST::Type> type = nullptr;
+  if (lexer.peek_token ()->get_id () == EQUAL)
+    {
+      lexer.skip_token ();
+
+      // parse type (now required)
+      type = parse_type ();
+      if (type == nullptr)
+	{
+	  Error error (lexer.peek_token ()->get_locus (),
+		       "failed to parse type in type param");
+	  add_error (std::move (error));
+
+	  return nullptr;
+	}
+    }
+
+  return std::unique_ptr<AST::TypeParam> (
+    new AST::TypeParam (std::move (ident), identifier_tok->get_locus (),
+			std::move (type_param_bounds), std::move (type),
+			std::move (outer_attr)));
+}
+
+/* Parses regular (i.e. non-generic) parameters in functions or methods. Also
+ * has end token handling. */
+template <typename ManagedTokenSource>
+template <typename EndTokenPred>
+std::vector<AST::FunctionParam>
+Parser<ManagedTokenSource>::parse_function_params (EndTokenPred is_end_token)
+{
+  std::vector<AST::FunctionParam> params;
+
+  if (is_end_token (lexer.peek_token ()->get_id ()))
+    return params;
+
+  AST::FunctionParam initial_param = parse_function_param ();
+
+  // Return empty parameter list if no parameter there
+  if (initial_param.is_error ())
+    {
+      // TODO: is this an error?
+      return params;
+    }
+
+  params.push_back (std::move (initial_param));
+
+  // maybe think of a better control structure here - do-while with an initial
+  // error state? basically, loop through parameter list until can't find any
+  // more params
+  const_TokenPtr t = lexer.peek_token ();
+  while (t->get_id () == COMMA)
+    {
+      // skip comma if applies
+      lexer.skip_token ();
+
+      // TODO: strictly speaking, shouldn't there be no trailing comma?
+      if (is_end_token (lexer.peek_token ()->get_id ()))
+	break;
+
+      // now, as right paren would break, function param is required
+      AST::FunctionParam param = parse_function_param ();
+      if (param.is_error ())
+	{
+	  Error error (lexer.peek_token ()->get_locus (),
+		       "failed to parse function param (in function params)");
+	  add_error (std::move (error));
+
+	  // skip somewhere?
+	  return std::vector<AST::FunctionParam> ();
+	}
+
+      params.push_back (std::move (param));
+
+      t = lexer.peek_token ();
+    }
+
+  params.shrink_to_fit ();
+  return params;
+}
+
+/* Parses a single regular (i.e. non-generic) parameter in a function or
+ * method, i.e. the "name: type" bit. Also handles it not existing. */
+template <typename ManagedTokenSource>
+AST::FunctionParam
+Parser<ManagedTokenSource>::parse_function_param ()
+{
+  // parse outer attributes if they exist
+  AST::AttrVec outer_attrs = parse_outer_attributes ();
+
+  // TODO: should saved location be at start of outer attributes or pattern?
+  Location locus = lexer.peek_token ()->get_locus ();
+  std::unique_ptr<AST::Pattern> param_pattern = parse_pattern ();
+
+  // create error function param if it doesn't exist
+  if (param_pattern == nullptr)
+    {
+      // skip after something
+      return AST::FunctionParam::create_error ();
+    }
+
+  if (!skip_token (COLON))
+    {
+      // skip after something
+      return AST::FunctionParam::create_error ();
+    }
+
+  std::unique_ptr<AST::Type> param_type = parse_type ();
+  if (param_type == nullptr)
+    {
+      // skip?
+      return AST::FunctionParam::create_error ();
+    }
+
+  return AST::FunctionParam (std::move (param_pattern), std::move (param_type),
+			     std::move (outer_attrs), locus);
+}
+
+/* Parses a function or method return type syntactical construction. Also
+ * handles a function return type not existing. */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::Type>
+Parser<ManagedTokenSource>::parse_function_return_type ()
+{
+  if (lexer.peek_token ()->get_id () != RETURN_TYPE)
+    return nullptr;
+
+  // skip return type, as it now obviously exists
+  lexer.skip_token ();
+
+  std::unique_ptr<AST::Type> type = parse_type ();
+
+  return type;
+}
+
+/* Parses a "where clause" (in a function, struct, method, etc.). Also handles
+ * a where clause not existing, in which it will return
+ * WhereClause::create_empty(), which can be checked via
+ * WhereClause::is_empty(). */
+template <typename ManagedTokenSource>
+AST::WhereClause
+Parser<ManagedTokenSource>::parse_where_clause ()
+{
+  const_TokenPtr where_tok = lexer.peek_token ();
+  if (where_tok->get_id () != WHERE)
+    {
+      // where clause doesn't exist, so create empty one
+      return AST::WhereClause::create_empty ();
+    }
+
+  lexer.skip_token ();
+
+  /* parse where clause items - this is not a separate rule in the reference
+   * so won't be here */
+  std::vector<std::unique_ptr<AST::WhereClauseItem>> where_clause_items;
+
+  /* HACK: where clauses end with a right curly or semicolon or equals in all
+   * uses currently */
+  const_TokenPtr t = lexer.peek_token ();
+  while (t->get_id () != LEFT_CURLY && t->get_id () != SEMICOLON
+	 && t->get_id () != EQUAL)
+    {
+      std::unique_ptr<AST::WhereClauseItem> where_clause_item
+	= parse_where_clause_item ();
+
+      if (where_clause_item == nullptr)
+	{
+	  Error error (t->get_locus (), "failed to parse where clause item");
+	  add_error (std::move (error));
+
+	  return AST::WhereClause::create_empty ();
+	}
+
+      where_clause_items.push_back (std::move (where_clause_item));
+
+      // also skip comma if it exists
+      if (lexer.peek_token ()->get_id () != COMMA)
+	break;
+
+      lexer.skip_token ();
+      t = lexer.peek_token ();
+    }
+
+  where_clause_items.shrink_to_fit ();
+  return AST::WhereClause (std::move (where_clause_items));
+}
+
+/* Parses a where clause item (lifetime or type bound). Does not parse any
+ * commas. */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::WhereClauseItem>
+Parser<ManagedTokenSource>::parse_where_clause_item ()
+{
+  // shitty cheat way of determining lifetime or type bound - test for
+  // lifetime
+  const_TokenPtr t = lexer.peek_token ();
+
+  if (t->get_id () == LIFETIME)
+    return parse_lifetime_where_clause_item ();
+  else
+    return parse_type_bound_where_clause_item ();
+}
+
+// Parses a lifetime where clause item.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::LifetimeWhereClauseItem>
+Parser<ManagedTokenSource>::parse_lifetime_where_clause_item ()
+{
+  AST::Lifetime lifetime = parse_lifetime ();
+  if (lifetime.is_error ())
+    {
+      // TODO: error here?
+      return nullptr;
+    }
+
+  if (!skip_token (COLON))
+    {
+      // TODO: skip after somewhere
+      return nullptr;
+    }
+
+  std::vector<AST::Lifetime> lifetime_bounds = parse_lifetime_bounds ();
+  // TODO: have end token passed in?
+
+  Location locus = lifetime.get_locus ();
+
+  return std::unique_ptr<AST::LifetimeWhereClauseItem> (
+    new AST::LifetimeWhereClauseItem (std::move (lifetime),
+				      std::move (lifetime_bounds), locus));
+}
+
+// Parses a type bound where clause item.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::TypeBoundWhereClauseItem>
+Parser<ManagedTokenSource>::parse_type_bound_where_clause_item ()
+{
+  // parse for lifetimes, if it exists
+  std::vector<AST::LifetimeParam> for_lifetimes;
+  if (lexer.peek_token ()->get_id () == FOR)
+    for_lifetimes = parse_for_lifetimes ();
+
+  std::unique_ptr<AST::Type> type = parse_type ();
+  if (type == nullptr)
+    {
+      return nullptr;
+    }
+
+  if (!skip_token (COLON))
+    {
+      // TODO: skip after somewhere
+      return nullptr;
+    }
+
+  // parse type param bounds if they exist
+  std::vector<std::unique_ptr<AST::TypeParamBound>> type_param_bounds
+    = parse_type_param_bounds ();
+
+  Location locus = lexer.peek_token ()->get_locus ();
+
+  return std::unique_ptr<AST::TypeBoundWhereClauseItem> (
+    new AST::TypeBoundWhereClauseItem (std::move (for_lifetimes),
+				       std::move (type),
+				       std::move (type_param_bounds), locus));
+}
+
+// Parses a for lifetimes clause, including the for keyword and angle
+// brackets.
+template <typename ManagedTokenSource>
+std::vector<AST::LifetimeParam>
+Parser<ManagedTokenSource>::parse_for_lifetimes ()
+{
+  std::vector<AST::LifetimeParam> params;
+
+  if (!skip_token (FOR))
+    {
+      // skip after somewhere?
+      return params;
+    }
+
+  if (!skip_token (LEFT_ANGLE))
+    {
+      // skip after somewhere?
+      return params;
+    }
+
+  /* cannot specify end token due to parsing problems with '>' tokens being
+   * nested */
+  params = parse_lifetime_params_objs (is_right_angle_tok);
+
+  if (!skip_generics_right_angle ())
+    {
+      // DEBUG
+      rust_debug ("failed to skip generics right angle after (supposedly) "
+		  "finished parsing where clause items");
+      // ok, well this gets called.
+
+      // skip after somewhere?
+      return params;
+    }
+
+  return params;
+}
+
+// Parses type parameter bounds in where clause or generic arguments.
+template <typename ManagedTokenSource>
+std::vector<std::unique_ptr<AST::TypeParamBound>>
+Parser<ManagedTokenSource>::parse_type_param_bounds ()
+{
+  std::vector<std::unique_ptr<AST::TypeParamBound>> type_param_bounds;
+
+  std::unique_ptr<AST::TypeParamBound> initial_bound
+    = parse_type_param_bound ();
+
+  // quick exit if null
+  if (initial_bound == nullptr)
+    {
+      /* error? type param bounds must have at least one term, but are bounds
+       * optional? */
+      return type_param_bounds;
+    }
+  type_param_bounds.push_back (std::move (initial_bound));
+
+  while (lexer.peek_token ()->get_id () == PLUS)
+    {
+      lexer.skip_token ();
+
+      std::unique_ptr<AST::TypeParamBound> bound = parse_type_param_bound ();
+      if (bound == nullptr)
+	{
+	  /* not an error: bound is allowed to be null as trailing plus is
+	   * allowed */
+	  return type_param_bounds;
+	}
+
+      type_param_bounds.push_back (std::move (bound));
+    }
+
+  type_param_bounds.shrink_to_fit ();
+  return type_param_bounds;
+}
+
+/* Parses type parameter bounds in where clause or generic arguments, with end
+ * token handling. */
+template <typename ManagedTokenSource>
+template <typename EndTokenPred>
+std::vector<std::unique_ptr<AST::TypeParamBound>>
+Parser<ManagedTokenSource>::parse_type_param_bounds (EndTokenPred is_end_token)
+{
+  std::vector<std::unique_ptr<AST::TypeParamBound>> type_param_bounds;
+
+  std::unique_ptr<AST::TypeParamBound> initial_bound
+    = parse_type_param_bound ();
+
+  // quick exit if null
+  if (initial_bound == nullptr)
+    {
+      /* error? type param bounds must have at least one term, but are bounds
+       * optional? */
+      return type_param_bounds;
+    }
+  type_param_bounds.push_back (std::move (initial_bound));
+
+  while (lexer.peek_token ()->get_id () == PLUS)
+    {
+      lexer.skip_token ();
+
+      // break if end token character
+      if (is_end_token (lexer.peek_token ()->get_id ()))
+	break;
+
+      std::unique_ptr<AST::TypeParamBound> bound = parse_type_param_bound ();
+      if (bound == nullptr)
+	{
+	  // TODO how wise is it to ditch all bounds if only one failed?
+	  Error error (lexer.peek_token ()->get_locus (),
+		       "failed to parse type param bound in type param bounds");
+	  add_error (std::move (error));
+
+	  return {};
+	}
+
+      type_param_bounds.push_back (std::move (bound));
+    }
+
+  type_param_bounds.shrink_to_fit ();
+  return type_param_bounds;
+}
+
+/* Parses a single type parameter bound in a where clause or generic argument.
+ * Does not parse the '+' between arguments. */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::TypeParamBound>
+Parser<ManagedTokenSource>::parse_type_param_bound ()
+{
+  // shitty cheat way of determining lifetime or trait bound - test for
+  // lifetime
+  const_TokenPtr t = lexer.peek_token ();
+  switch (t->get_id ())
+    {
+    case LIFETIME:
+      return std::unique_ptr<AST::Lifetime> (
+	new AST::Lifetime (parse_lifetime ()));
+    case LEFT_PAREN:
+    case QUESTION_MARK:
+    case FOR:
+    case IDENTIFIER:
+    case SUPER:
+    case SELF:
+    case SELF_ALIAS:
+    case CRATE:
+    case DOLLAR_SIGN:
+      return parse_trait_bound ();
+    default:
+      // don't error - assume this is fine TODO
+      return nullptr;
+    }
+}
+
+// Parses a trait bound type param bound.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::TraitBound>
+Parser<ManagedTokenSource>::parse_trait_bound ()
+{
+  bool has_parens = false;
+  bool has_question_mark = false;
+
+  Location locus = lexer.peek_token ()->get_locus ();
+
+  // handle trait bound being in parentheses
+  if (lexer.peek_token ()->get_id () == LEFT_PAREN)
+    {
+      has_parens = true;
+      lexer.skip_token ();
+    }
+
+  // handle having question mark (optional)
+  if (lexer.peek_token ()->get_id () == QUESTION_MARK)
+    {
+      has_question_mark = true;
+      lexer.skip_token ();
+    }
+
+  /* parse for lifetimes, if it exists (although empty for lifetimes is ok to
+   * handle this) */
+  std::vector<AST::LifetimeParam> for_lifetimes;
+  if (lexer.peek_token ()->get_id () == FOR)
+    for_lifetimes = parse_for_lifetimes ();
+
+  // handle TypePath
+  AST::TypePath type_path = parse_type_path ();
+
+  // handle closing parentheses
+  if (has_parens)
+    {
+      if (!skip_token (RIGHT_PAREN))
+	{
+	  return nullptr;
+	}
+    }
+
+  return std::unique_ptr<AST::TraitBound> (
+    new AST::TraitBound (std::move (type_path), locus, has_parens,
+			 has_question_mark, std::move (for_lifetimes)));
+}
+
+// Parses lifetime bounds.
+template <typename ManagedTokenSource>
+std::vector<AST::Lifetime>
+Parser<ManagedTokenSource>::parse_lifetime_bounds ()
+{
+  std::vector<AST::Lifetime> lifetime_bounds;
+
+  while (true)
+    {
+      AST::Lifetime lifetime = parse_lifetime ();
+
+      // quick exit for parsing failure
+      if (lifetime.is_error ())
+	break;
+
+      lifetime_bounds.push_back (std::move (lifetime));
+
+      /* plus is maybe not allowed at end - spec defines it weirdly, so
+       * assuming allowed at end */
+      if (lexer.peek_token ()->get_id () != PLUS)
+	break;
+
+      lexer.skip_token ();
+    }
+
+  lifetime_bounds.shrink_to_fit ();
+  return lifetime_bounds;
+}
+
+// Parses lifetime bounds, with added check for ending token.
+template <typename ManagedTokenSource>
+template <typename EndTokenPred>
+std::vector<AST::Lifetime>
+Parser<ManagedTokenSource>::parse_lifetime_bounds (EndTokenPred is_end_token)
+{
+  std::vector<AST::Lifetime> lifetime_bounds;
+
+  while (!is_end_token (lexer.peek_token ()->get_id ()))
+    {
+      AST::Lifetime lifetime = parse_lifetime ();
+
+      if (lifetime.is_error ())
+	{
+	  /* TODO: is it worth throwing away all lifetime bound info just
+	   * because one failed? */
+	  Error error (lexer.peek_token ()->get_locus (),
+		       "failed to parse lifetime in lifetime bounds");
+	  add_error (std::move (error));
+
+	  return {};
+	}
+
+      lifetime_bounds.push_back (std::move (lifetime));
+
+      /* plus is maybe not allowed at end - spec defines it weirdly, so
+       * assuming allowed at end */
+      if (lexer.peek_token ()->get_id () != PLUS)
+	break;
+
+      lexer.skip_token ();
+    }
+
+  lifetime_bounds.shrink_to_fit ();
+  return lifetime_bounds;
+}
+
+/* Parses a lifetime token (named, 'static, or '_). Also handles lifetime not
+ * existing. */
+template <typename ManagedTokenSource>
+AST::Lifetime
+Parser<ManagedTokenSource>::parse_lifetime ()
+{
+  const_TokenPtr lifetime_tok = lexer.peek_token ();
+  Location locus = lifetime_tok->get_locus ();
+  // create error lifetime if doesn't exist
+  if (lifetime_tok->get_id () != LIFETIME)
+    {
+      return AST::Lifetime::error ();
+    }
+  lexer.skip_token ();
+
+  std::string lifetime_ident = lifetime_tok->get_str ();
+
+  if (lifetime_ident == "'static")
+    {
+      return AST::Lifetime (AST::Lifetime::STATIC, "", locus);
+    }
+  else if (lifetime_ident == "'_")
+    {
+      return AST::Lifetime (AST::Lifetime::WILDCARD, "", locus);
+    }
+  else
+    {
+      return AST::Lifetime (AST::Lifetime::NAMED, std::move (lifetime_ident),
+			    locus);
+    }
+}
+
+// Parses a "type alias" (typedef) item.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::TypeAlias>
+Parser<ManagedTokenSource>::parse_type_alias (AST::Visibility vis,
+					      AST::AttrVec outer_attrs)
+{
+  Location locus = lexer.peek_token ()->get_locus ();
+  skip_token (TYPE);
+
+  // TODO: use this token for identifier when finished that
+  const_TokenPtr alias_name_tok = expect_token (IDENTIFIER);
+  if (alias_name_tok == nullptr)
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "could not parse identifier in type alias");
+      add_error (std::move (error));
+
+      skip_after_semicolon ();
+      return nullptr;
+    }
+  Identifier alias_name = alias_name_tok->get_str ();
+
+  // parse generic params, which may not exist
+  std::vector<std::unique_ptr<AST::GenericParam>> generic_params
+    = parse_generic_params_in_angles ();
+
+  // parse where clause, which may not exist
+  AST::WhereClause where_clause = parse_where_clause ();
+
+  if (!skip_token (EQUAL))
+    {
+      skip_after_semicolon ();
+      return nullptr;
+    }
+
+  std::unique_ptr<AST::Type> type_to_alias = parse_type ();
+
+  if (!skip_token (SEMICOLON))
+    {
+      // should be skipping past this, not the next line
+      return nullptr;
+    }
+
+  return std::unique_ptr<AST::TypeAlias> (
+    new AST::TypeAlias (std::move (alias_name), std::move (generic_params),
+			std::move (where_clause), std::move (type_to_alias),
+			std::move (vis), std::move (outer_attrs), locus));
+}
+
+// Parse a struct item AST node.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::Struct>
+Parser<ManagedTokenSource>::parse_struct (AST::Visibility vis,
+					  AST::AttrVec outer_attrs)
+{
+  /* TODO: determine best way to parse the proper struct vs tuple struct -
+   * share most of initial constructs so lookahead might be impossible, and if
+   * not probably too expensive. Best way is probably unified parsing for the
+   * initial parts and then pass them in as params to more derived functions.
+   * Alternatively, just parse everything in this one function - do this if
+   * function not too long. */
+
+  /* Proper struct <- 'struct' IDENTIFIER generic_params? where_clause? ( '{'
+   * struct_fields? '}' | ';' ) */
+  /* Tuple struct <- 'struct' IDENTIFIER generic_params? '(' tuple_fields? ')'
+   * where_clause? ';' */
+  Location locus = lexer.peek_token ()->get_locus ();
+  skip_token (STRUCT_TOK);
+
+  // parse struct name
+  const_TokenPtr name_tok = expect_token (IDENTIFIER);
+  if (name_tok == nullptr)
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "could not parse struct or tuple struct identifier");
+      add_error (std::move (error));
+
+      // skip after somewhere?
+      return nullptr;
+    }
+  Identifier struct_name = name_tok->get_str ();
+
+  // parse generic params, which may or may not exist
+  std::vector<std::unique_ptr<AST::GenericParam>> generic_params
+    = parse_generic_params_in_angles ();
+
+  // branch on next token - determines whether proper struct or tuple struct
+  if (lexer.peek_token ()->get_id () == LEFT_PAREN)
+    {
+      // tuple struct
+
+      // skip left parenthesis
+      lexer.skip_token ();
+
+      // parse tuple fields
+      std::vector<AST::TupleField> tuple_fields;
+      // Might be empty tuple for unit tuple struct.
+      if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
+	tuple_fields = std::vector<AST::TupleField> ();
+      else
+	tuple_fields = parse_tuple_fields ();
+
+      // tuple parameters must have closing parenthesis
+      if (!skip_token (RIGHT_PAREN))
+	{
+	  skip_after_semicolon ();
+	  return nullptr;
+	}
+
+      // parse where clause, which is optional
+      AST::WhereClause where_clause = parse_where_clause ();
+
+      if (!skip_token (SEMICOLON))
+	{
+	  // can't skip after semicolon because it's meant to be here
+	  return nullptr;
+	}
+
+      return std::unique_ptr<AST::TupleStruct> (
+	new AST::TupleStruct (std::move (tuple_fields), std::move (struct_name),
+			      std::move (generic_params),
+			      std::move (where_clause), std::move (vis),
+			      std::move (outer_attrs), locus));
+    }
+
+  // assume it is a proper struct being parsed and continue outside of switch
+  // - label only here to suppress warning
+
+  // parse where clause, which is optional
+  AST::WhereClause where_clause = parse_where_clause ();
+
+  // branch on next token - determines whether struct is a unit struct
+  const_TokenPtr t = lexer.peek_token ();
+  switch (t->get_id ())
+    {
+      case LEFT_CURLY: {
+	// struct with body
+
+	// skip curly bracket
+	lexer.skip_token ();
+
+	// parse struct fields, if any
+	std::vector<AST::StructField> struct_fields
+	  = parse_struct_fields ([] (TokenId id) { return id == RIGHT_CURLY; });
+
+	if (!skip_token (RIGHT_CURLY))
+	  {
+	    // skip somewhere?
+	    return nullptr;
+	  }
+
+	return std::unique_ptr<AST::StructStruct> (new AST::StructStruct (
+	  std::move (struct_fields), std::move (struct_name),
+	  std::move (generic_params), std::move (where_clause), false,
+	  std::move (vis), std::move (outer_attrs), locus));
+      }
+    case SEMICOLON:
+      // unit struct declaration
+
+      lexer.skip_token ();
+
+      return std::unique_ptr<AST::StructStruct> (
+	new AST::StructStruct (std::move (struct_name),
+			       std::move (generic_params),
+			       std::move (where_clause), std::move (vis),
+			       std::move (outer_attrs), locus));
+    default:
+      add_error (Error (t->get_locus (),
+			"unexpected token %qs in struct declaration",
+			t->get_token_description ()));
+
+      // skip somewhere?
+      return nullptr;
+    }
+}
+
+// Parses struct fields in struct declarations.
+template <typename ManagedTokenSource>
+std::vector<AST::StructField>
+Parser<ManagedTokenSource>::parse_struct_fields ()
+{
+  std::vector<AST::StructField> fields;
+
+  AST::StructField initial_field = parse_struct_field ();
+
+  // Return empty field list if no field there
+  if (initial_field.is_error ())
+    return fields;
+
+  fields.push_back (std::move (initial_field));
+
+  while (lexer.peek_token ()->get_id () == COMMA)
+    {
+      lexer.skip_token ();
+
+      AST::StructField field = parse_struct_field ();
+
+      if (field.is_error ())
+	{
+	  // would occur with trailing comma, so allowed
+	  break;
+	}
+
+      fields.push_back (std::move (field));
+    }
+
+  fields.shrink_to_fit ();
+  return fields;
+  // TODO: template if possible (parse_non_ptr_seq)
+}
+
+// Parses struct fields in struct declarations.
+template <typename ManagedTokenSource>
+template <typename EndTokenPred>
+std::vector<AST::StructField>
+Parser<ManagedTokenSource>::parse_struct_fields (EndTokenPred is_end_tok)
+{
+  std::vector<AST::StructField> fields;
+
+  AST::StructField initial_field = parse_struct_field ();
+
+  // Return empty field list if no field there
+  if (initial_field.is_error ())
+    return fields;
+
+  fields.push_back (std::move (initial_field));
+
+  while (lexer.peek_token ()->get_id () == COMMA)
+    {
+      lexer.skip_token ();
+
+      if (is_end_tok (lexer.peek_token ()->get_id ()))
+	break;
+
+      AST::StructField field = parse_struct_field ();
+      if (field.is_error ())
+	{
+	  /* TODO: should every field be ditched just because one couldn't be
+	   * parsed? */
+	  Error error (lexer.peek_token ()->get_locus (),
+		       "failed to parse struct field in struct fields");
+	  add_error (std::move (error));
+
+	  return {};
+	}
+
+      fields.push_back (std::move (field));
+    }
+
+  fields.shrink_to_fit ();
+  return fields;
+  // TODO: template if possible (parse_non_ptr_seq)
+}
+
+// Parses a single struct field (in a struct definition). Does not parse
+// commas.
+template <typename ManagedTokenSource>
+AST::StructField
+Parser<ManagedTokenSource>::parse_struct_field ()
+{
+  // parse outer attributes, if they exist
+  AST::AttrVec outer_attrs = parse_outer_attributes ();
+
+  // parse visibility, if it exists
+  AST::Visibility vis = parse_visibility ();
+
+  Location locus = lexer.peek_token ()->get_locus ();
+
+  // parse field name
+  const_TokenPtr field_name_tok = lexer.peek_token ();
+  if (field_name_tok->get_id () != IDENTIFIER)
+    {
+      // if not identifier, assumes there is no struct field and exits - not
+      // necessarily error
+      return AST::StructField::create_error ();
+    }
+  Identifier field_name = field_name_tok->get_str ();
+  lexer.skip_token ();
+
+  if (!skip_token (COLON))
+    {
+      // skip after somewhere?
+      return AST::StructField::create_error ();
+    }
+
+  // parse field type - this is required
+  std::unique_ptr<AST::Type> field_type = parse_type ();
+  if (field_type == nullptr)
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "could not parse type in struct field definition");
+      add_error (std::move (error));
+
+      // skip after somewhere
+      return AST::StructField::create_error ();
+    }
+
+  return AST::StructField (std::move (field_name), std::move (field_type),
+			   std::move (vis), locus, std::move (outer_attrs));
+}
+
+// Parses tuple fields in tuple/tuple struct declarations.
+template <typename ManagedTokenSource>
+std::vector<AST::TupleField>
+Parser<ManagedTokenSource>::parse_tuple_fields ()
+{
+  std::vector<AST::TupleField> fields;
+
+  AST::TupleField initial_field = parse_tuple_field ();
+
+  // Return empty field list if no field there
+  if (initial_field.is_error ())
+    {
+      return fields;
+    }
+
+  fields.push_back (std::move (initial_field));
+
+  // maybe think of a better control structure here - do-while with an initial
+  // error state? basically, loop through field list until can't find any more
+  // params HACK: all current syntax uses of tuple fields have them ending
+  // with a right paren token
+  const_TokenPtr t = lexer.peek_token ();
+  while (t->get_id () == COMMA)
+    {
+      // skip comma if applies - e.g. trailing comma
+      lexer.skip_token ();
+
+      // break out due to right paren if it exists
+      if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
+	{
+	  break;
+	}
+
+      AST::TupleField field = parse_tuple_field ();
+      if (field.is_error ())
+	{
+	  Error error (lexer.peek_token ()->get_locus (),
+		       "failed to parse tuple field in tuple fields");
+	  add_error (std::move (error));
+
+	  return std::vector<AST::TupleField> ();
+	}
+
+      fields.push_back (std::move (field));
+
+      t = lexer.peek_token ();
+    }
+
+  fields.shrink_to_fit ();
+  return fields;
+
+  // TODO: this shares basically all code with function params and struct
+  // fields
+  // - templates?
+}
+
+/* Parses a single tuple struct field in a tuple struct definition. Does not
+ * parse commas. */
+template <typename ManagedTokenSource>
+AST::TupleField
+Parser<ManagedTokenSource>::parse_tuple_field ()
+{
+  // parse outer attributes if they exist
+  AST::AttrVec outer_attrs = parse_outer_attributes ();
+
+  // parse visibility if it exists
+  AST::Visibility vis = parse_visibility ();
+
+  Location locus = lexer.peek_token ()->get_locus ();
+
+  // parse type, which is required
+  std::unique_ptr<AST::Type> field_type = parse_type ();
+  if (field_type == nullptr)
+    {
+      // error if null
+      Error error (lexer.peek_token ()->get_locus (),
+		   "could not parse type in tuple struct field");
+      add_error (std::move (error));
+
+      // skip after something
+      return AST::TupleField::create_error ();
+    }
+
+  return AST::TupleField (std::move (field_type), std::move (vis), locus,
+			  std::move (outer_attrs));
+}
+
+// Parses a Rust "enum" tagged union item definition.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::Enum>
+Parser<ManagedTokenSource>::parse_enum (AST::Visibility vis,
+					AST::AttrVec outer_attrs)
+{
+  Location locus = lexer.peek_token ()->get_locus ();
+  skip_token (ENUM_TOK);
+
+  // parse enum name
+  const_TokenPtr enum_name_tok = expect_token (IDENTIFIER);
+  if (enum_name_tok == nullptr)
+    return nullptr;
+
+  Identifier enum_name = enum_name_tok->get_str ();
+
+  // parse generic params (of enum container, not enum variants) if they exist
+  std::vector<std::unique_ptr<AST::GenericParam>> generic_params
+    = parse_generic_params_in_angles ();
+
+  // parse where clause if it exists
+  AST::WhereClause where_clause = parse_where_clause ();
+
+  if (!skip_token (LEFT_CURLY))
+    {
+      skip_after_end_block ();
+      return nullptr;
+    }
+
+  // parse actual enum variant definitions
+  std::vector<std::unique_ptr<AST::EnumItem>> enum_items
+    = parse_enum_items ([] (TokenId id) { return id == RIGHT_CURLY; });
+
+  if (!skip_token (RIGHT_CURLY))
+    {
+      skip_after_end_block ();
+      return nullptr;
+    }
+
+  return std::unique_ptr<AST::Enum> (
+    new AST::Enum (std::move (enum_name), std::move (vis),
+		   std::move (generic_params), std::move (where_clause),
+		   std::move (enum_items), std::move (outer_attrs), locus));
+}
+
+// Parses the enum variants inside an enum definiton.
+template <typename ManagedTokenSource>
+std::vector<std::unique_ptr<AST::EnumItem>>
+Parser<ManagedTokenSource>::parse_enum_items ()
+{
+  std::vector<std::unique_ptr<AST::EnumItem>> items;
+
+  std::unique_ptr<AST::EnumItem> initial_item = parse_enum_item ();
+
+  // Return empty item list if no field there
+  if (initial_item == nullptr)
+    return items;
+
+  items.push_back (std::move (initial_item));
+
+  while (lexer.peek_token ()->get_id () == COMMA)
+    {
+      lexer.skip_token ();
+
+      std::unique_ptr<AST::EnumItem> item = parse_enum_item ();
+      if (item == nullptr)
+	{
+	  // this would occur with a trailing comma, which is allowed
+	  break;
+	}
+
+      items.push_back (std::move (item));
+    }
+
+  items.shrink_to_fit ();
+  return items;
+
+  /* TODO: use template if doable (parse_non_ptr_sequence) */
+}
+
+// Parses the enum variants inside an enum definiton.
+template <typename ManagedTokenSource>
+template <typename EndTokenPred>
+std::vector<std::unique_ptr<AST::EnumItem>>
+Parser<ManagedTokenSource>::parse_enum_items (EndTokenPred is_end_tok)
+{
+  std::vector<std::unique_ptr<AST::EnumItem>> items;
+
+  std::unique_ptr<AST::EnumItem> initial_item = parse_enum_item ();
+
+  // Return empty item list if no field there
+  if (initial_item == nullptr)
+    return items;
+
+  items.push_back (std::move (initial_item));
+
+  while (lexer.peek_token ()->get_id () == COMMA)
+    {
+      lexer.skip_token ();
+
+      if (is_end_tok (lexer.peek_token ()->get_id ()))
+	break;
+
+      std::unique_ptr<AST::EnumItem> item = parse_enum_item ();
+      if (item == nullptr)
+	{
+	  /* TODO should this ignore all successfully parsed enum items just
+	   * because one failed? */
+	  Error error (lexer.peek_token ()->get_locus (),
+		       "failed to parse enum item in enum items");
+	  add_error (std::move (error));
+
+	  return {};
+	}
+
+      items.push_back (std::move (item));
+    }
+
+  items.shrink_to_fit ();
+  return items;
+
+  /* TODO: use template if doable (parse_non_ptr_sequence) */
+}
+
+/* Parses a single enum variant item in an enum definition. Does not parse
+ * commas. */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::EnumItem>
+Parser<ManagedTokenSource>::parse_enum_item ()
+{
+  // parse outer attributes if they exist
+  AST::AttrVec outer_attrs = parse_outer_attributes ();
+
+  // parse visibility, which may or may not exist
+  AST::Visibility vis = parse_visibility ();
+
+  // parse name for enum item, which is required
+  const_TokenPtr item_name_tok = lexer.peek_token ();
+  if (item_name_tok->get_id () != IDENTIFIER)
+    {
+      // this may not be an error but it means there is no enum item here
+      return nullptr;
+    }
+  lexer.skip_token ();
+  Identifier item_name = item_name_tok->get_str ();
+
+  // branch based on next token
+  const_TokenPtr t = lexer.peek_token ();
+  switch (t->get_id ())
+    {
+      case LEFT_PAREN: {
+	// tuple enum item
+	lexer.skip_token ();
+
+	std::vector<AST::TupleField> tuple_fields;
+	// Might be empty tuple for unit tuple enum variant.
+	if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
+	  tuple_fields = std::vector<AST::TupleField> ();
+	else
+	  tuple_fields = parse_tuple_fields ();
+
+	if (!skip_token (RIGHT_PAREN))
+	  {
+	    // skip after somewhere
+	    return nullptr;
+	  }
+
+	return std::unique_ptr<AST::EnumItemTuple> (new AST::EnumItemTuple (
+	  std::move (item_name), std::move (vis), std::move (tuple_fields),
+	  std::move (outer_attrs), item_name_tok->get_locus ()));
+      }
+      case LEFT_CURLY: {
+	// struct enum item
+	lexer.skip_token ();
+
+	std::vector<AST::StructField> struct_fields
+	  = parse_struct_fields ([] (TokenId id) { return id == RIGHT_CURLY; });
+
+	if (!skip_token (RIGHT_CURLY))
+	  {
+	    // skip after somewhere
+	    return nullptr;
+	  }
+
+	return std::unique_ptr<AST::EnumItemStruct> (new AST::EnumItemStruct (
+	  std::move (item_name), std::move (vis), std::move (struct_fields),
+	  std::move (outer_attrs), item_name_tok->get_locus ()));
+      }
+      case EQUAL: {
+	// discriminant enum item
+	lexer.skip_token ();
+
+	std::unique_ptr<AST::Expr> discriminant_expr = parse_expr ();
+
+	return std::unique_ptr<AST::EnumItemDiscriminant> (
+	  new AST::EnumItemDiscriminant (std::move (item_name), std::move (vis),
+					 std::move (discriminant_expr),
+					 std::move (outer_attrs),
+					 item_name_tok->get_locus ()));
+      }
+    default:
+      // regular enum with just an identifier
+      return std::unique_ptr<AST::EnumItem> (
+	new AST::EnumItem (std::move (item_name), std::move (vis),
+			   std::move (outer_attrs),
+			   item_name_tok->get_locus ()));
+    }
+}
+
+// Parses a C-style (and C-compat) untagged union declaration.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::Union>
+Parser<ManagedTokenSource>::parse_union (AST::Visibility vis,
+					 AST::AttrVec outer_attrs)
+{
+  /* hack - "weak keyword" by finding identifier called "union" (lookahead in
+   * item switch) */
+  const_TokenPtr union_keyword = expect_token (IDENTIFIER);
+  rust_assert (union_keyword->get_str () == "union");
+  Location locus = union_keyword->get_locus ();
+
+  // parse actual union name
+  const_TokenPtr union_name_tok = expect_token (IDENTIFIER);
+  if (union_name_tok == nullptr)
+    {
+      skip_after_next_block ();
+      return nullptr;
+    }
+  Identifier union_name = union_name_tok->get_str ();
+
+  // parse optional generic parameters
+  std::vector<std::unique_ptr<AST::GenericParam>> generic_params
+    = parse_generic_params_in_angles ();
+
+  // parse optional where clause
+  AST::WhereClause where_clause = parse_where_clause ();
+
+  if (!skip_token (LEFT_CURLY))
+    {
+      skip_after_end_block ();
+      return nullptr;
+    }
+
+  /* parse union inner items as "struct fields" because hey, syntax reuse.
+   * Spec said so. */
+  std::vector<AST::StructField> union_fields
+    = parse_struct_fields ([] (TokenId id) { return id == RIGHT_CURLY; });
+
+  if (!skip_token (RIGHT_CURLY))
+    {
+      // skip after somewhere
+      return nullptr;
+    }
+
+  return std::unique_ptr<AST::Union> (
+    new AST::Union (std::move (union_name), std::move (vis),
+		    std::move (generic_params), std::move (where_clause),
+		    std::move (union_fields), std::move (outer_attrs), locus));
+}
+
+/* Parses a "constant item" (compile-time constant to maybe "inline"
+ * throughout the program - like constexpr). */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ConstantItem>
+Parser<ManagedTokenSource>::parse_const_item (AST::Visibility vis,
+					      AST::AttrVec outer_attrs)
+{
+  Location locus = lexer.peek_token ()->get_locus ();
+  skip_token (CONST);
+
+  /* get constant identifier - this is either a proper identifier or the _
+   * wildcard */
+  const_TokenPtr ident_tok = lexer.peek_token ();
+  // make default identifier the underscore wildcard one
+  std::string ident ("_");
+  switch (ident_tok->get_id ())
+    {
+    case IDENTIFIER:
+      ident = ident_tok->get_str ();
+      lexer.skip_token ();
+      break;
+    case UNDERSCORE:
+      // do nothing - identifier is already "_"
+      lexer.skip_token ();
+      break;
+    default:
+      add_error (
+	Error (ident_tok->get_locus (),
+	       "expected item name (identifier or %<_%>) in constant item "
+	       "declaration - found %qs",
+	       ident_tok->get_token_description ()));
+
+      skip_after_semicolon ();
+      return nullptr;
+    }
+
+  if (!skip_token (COLON))
+    {
+      skip_after_semicolon ();
+      return nullptr;
+    }
+
+  // parse constant type (required)
+  std::unique_ptr<AST::Type> type = parse_type ();
+
+  if (!skip_token (EQUAL))
+    {
+      skip_after_semicolon ();
+      return nullptr;
+    }
+
+  // parse constant expression (required)
+  std::unique_ptr<AST::Expr> expr = parse_expr ();
+
+  if (!skip_token (SEMICOLON))
+    {
+      // skip somewhere?
+      return nullptr;
+    }
+
+  return std::unique_ptr<AST::ConstantItem> (
+    new AST::ConstantItem (std::move (ident), std::move (vis), std::move (type),
+			   std::move (expr), std::move (outer_attrs), locus));
+}
+
+// Parses a "static item" (static storage item, with 'static lifetime).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::StaticItem>
+Parser<ManagedTokenSource>::parse_static_item (AST::Visibility vis,
+					       AST::AttrVec outer_attrs)
+{
+  Location locus = lexer.peek_token ()->get_locus ();
+  skip_token (STATIC_TOK);
+
+  // determine whether static item is mutable
+  bool is_mut = false;
+  if (lexer.peek_token ()->get_id () == MUT)
+    {
+      is_mut = true;
+      lexer.skip_token ();
+    }
+
+  const_TokenPtr ident_tok = expect_token (IDENTIFIER);
+  if (ident_tok == nullptr)
+    return nullptr;
+
+  Identifier ident = ident_tok->get_str ();
+
+  if (!skip_token (COLON))
+    {
+      skip_after_semicolon ();
+      return nullptr;
+    }
+
+  // parse static item type (required)
+  std::unique_ptr<AST::Type> type = parse_type ();
+
+  if (!skip_token (EQUAL))
+    {
+      skip_after_semicolon ();
+      return nullptr;
+    }
+
+  // parse static item expression (required)
+  std::unique_ptr<AST::Expr> expr = parse_expr ();
+
+  if (!skip_token (SEMICOLON))
+    {
+      // skip after somewhere
+      return nullptr;
+    }
+
+  return std::unique_ptr<AST::StaticItem> (
+    new AST::StaticItem (std::move (ident), is_mut, std::move (type),
+			 std::move (expr), std::move (vis),
+			 std::move (outer_attrs), locus));
+}
+
+// Parses a trait definition item, including unsafe ones.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::Trait>
+Parser<ManagedTokenSource>::parse_trait (AST::Visibility vis,
+					 AST::AttrVec outer_attrs)
+{
+  Location locus = lexer.peek_token ()->get_locus ();
+  bool is_unsafe = false;
+  if (lexer.peek_token ()->get_id () == UNSAFE)
+    {
+      is_unsafe = true;
+      lexer.skip_token ();
+    }
+
+  skip_token (TRAIT);
+
+  // parse trait name
+  const_TokenPtr ident_tok = expect_token (IDENTIFIER);
+  if (ident_tok == nullptr)
+    return nullptr;
+
+  Identifier ident = ident_tok->get_str ();
+
+  // parse generic parameters (if they exist)
+  std::vector<std::unique_ptr<AST::GenericParam>> generic_params
+    = parse_generic_params_in_angles ();
+
+  // create placeholder type param bounds in case they don't exist
+  std::vector<std::unique_ptr<AST::TypeParamBound>> type_param_bounds;
+
+  // parse type param bounds (if they exist)
+  if (lexer.peek_token ()->get_id () == COLON)
+    {
+      lexer.skip_token ();
+
+      type_param_bounds = parse_type_param_bounds (
+	[] (TokenId id) { return id == WHERE || id == LEFT_CURLY; });
+      // type_param_bounds = parse_type_param_bounds ();
+    }
+
+  // parse where clause (if it exists)
+  AST::WhereClause where_clause = parse_where_clause ();
+
+  if (!skip_token (LEFT_CURLY))
+    {
+      skip_after_end_block ();
+      return nullptr;
+    }
+
+  // parse inner attrs (if they exist)
+  AST::AttrVec inner_attrs = parse_inner_attributes ();
+
+  // parse trait items
+  std::vector<std::unique_ptr<AST::TraitItem>> trait_items;
+
+  const_TokenPtr t = lexer.peek_token ();
+  while (t->get_id () != RIGHT_CURLY)
+    {
+      std::unique_ptr<AST::TraitItem> trait_item = parse_trait_item ();
+
+      if (trait_item == nullptr)
+	{
+	  Error error (lexer.peek_token ()->get_locus (),
+		       "failed to parse trait item in trait");
+	  add_error (std::move (error));
+
+	  return nullptr;
+	}
+      trait_items.push_back (std::move (trait_item));
+
+      t = lexer.peek_token ();
+    }
+
+  if (!skip_token (RIGHT_CURLY))
+    {
+      // skip after something
+      return nullptr;
+    }
+
+  trait_items.shrink_to_fit ();
+  return std::unique_ptr<AST::Trait> (
+    new AST::Trait (std::move (ident), is_unsafe, std::move (generic_params),
+		    std::move (type_param_bounds), std::move (where_clause),
+		    std::move (trait_items), std::move (vis),
+		    std::move (outer_attrs), std::move (inner_attrs), locus));
+}
+
+// Parses a trait item used inside traits (not trait, the Item).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::TraitItem>
+Parser<ManagedTokenSource>::parse_trait_item ()
+{
+  // parse outer attributes (if they exist)
+  AST::AttrVec outer_attrs = parse_outer_attributes ();
+
+  // lookahead to determine what type of trait item to parse
+  const_TokenPtr tok = lexer.peek_token ();
+  switch (tok->get_id ())
+    {
+    case TYPE:
+      return parse_trait_type (std::move (outer_attrs));
+    case CONST:
+      // disambiguate with function qualifier
+      if (lexer.peek_token (1)->get_id () == IDENTIFIER)
+	{
+	  return parse_trait_const (std::move (outer_attrs));
+	}
+      // else, fallthrough to function
+      // TODO: find out how to disable gcc "implicit fallthrough" error
+      gcc_fallthrough ();
+    case UNSAFE:
+    case EXTERN_TOK:
+      case FN_TOK: {
+	/* function and method can't be disambiguated by lookahead alone
+	 * (without a lot of work and waste), so either make a
+	 * "parse_trait_function_or_method" or parse here mostly and pass in
+	 * most parameters (or if short enough, parse whole thing here). */
+	// parse function and method here
+
+	// parse function or method qualifiers
+	AST::FunctionQualifiers qualifiers = parse_function_qualifiers ();
+
+	skip_token (FN_TOK);
+
+	// parse function or method name
+	const_TokenPtr ident_tok = expect_token (IDENTIFIER);
+	if (ident_tok == nullptr)
+	  return nullptr;
+
+	Identifier ident = ident_tok->get_str ();
+
+	// parse generic params
+	std::vector<std::unique_ptr<AST::GenericParam>> generic_params
+	  = parse_generic_params_in_angles ();
+
+	if (!skip_token (LEFT_PAREN))
+	  {
+	    // skip after somewhere?
+	    return nullptr;
+	  }
+
+	/* now for function vs method disambiguation - method has opening
+	 * "self" param */
+	AST::SelfParam self_param = parse_self_param ();
+	/* FIXME: ensure that self param doesn't accidently consume tokens for
+	 * a function */
+	bool is_method = false;
+	if (!self_param.is_error ())
+	  {
+	    is_method = true;
+
+	    /* skip comma so function and method regular params can be parsed
+	     * in same way */
+	    if (lexer.peek_token ()->get_id () == COMMA)
+	      lexer.skip_token ();
+	  }
+
+	// parse trait function params
+	std::vector<AST::FunctionParam> function_params
+	  = parse_function_params (
+	    [] (TokenId id) { return id == RIGHT_PAREN; });
+
+	if (!skip_token (RIGHT_PAREN))
+	  {
+	    // skip after somewhere?
+	    return nullptr;
+	  }
+
+	// parse return type (optional)
+	std::unique_ptr<AST::Type> return_type = parse_function_return_type ();
+
+	// parse where clause (optional)
+	AST::WhereClause where_clause = parse_where_clause ();
+
+	// parse semicolon or function definition (in block)
+	const_TokenPtr t = lexer.peek_token ();
+	std::unique_ptr<AST::BlockExpr> definition = nullptr;
+	switch (t->get_id ())
+	  {
+	  case SEMICOLON:
+	    lexer.skip_token ();
+	    // definition is already nullptr, so don't need to change it
+	    break;
+	  case LEFT_CURLY:
+	    definition = parse_block_expr ();
+	    /* FIXME: are these outer attributes meant to be passed into the
+	     * block? */
+	    break;
+	  default:
+	    add_error (
+	      Error (t->get_locus (),
+		     "expected %<;%> or definiton at the end of trait %s "
+		     "definition - found %qs instead",
+		     is_method ? "method" : "function",
+		     t->get_token_description ()));
+
+	    // skip?
+	    return nullptr;
+	  }
+
+	// do actual if instead of ternary for return value optimisation
+	if (is_method)
+	  {
+	    AST::TraitMethodDecl method_decl (std::move (ident),
+					      std::move (qualifiers),
+					      std::move (generic_params),
+					      std::move (self_param),
+					      std::move (function_params),
+					      std::move (return_type),
+					      std::move (where_clause));
+
+	    // TODO: does this (method_decl) need move?
+	    return std::unique_ptr<AST::TraitItemMethod> (
+	      new AST::TraitItemMethod (std::move (method_decl),
+					std::move (definition),
+					std::move (outer_attrs),
+					tok->get_locus ()));
+	  }
+	else
+	  {
+	    AST::TraitFunctionDecl function_decl (std::move (ident),
+						  std::move (qualifiers),
+						  std::move (generic_params),
+						  std::move (function_params),
+						  std::move (return_type),
+						  std::move (where_clause));
+
+	    return std::unique_ptr<AST::TraitItemFunc> (new AST::TraitItemFunc (
+	      std::move (function_decl), std::move (definition),
+	      std::move (outer_attrs), tok->get_locus ()));
+	  }
+      }
+      default: {
+	// TODO: try and parse macro invocation semi - if fails, maybe error.
+	std::unique_ptr<AST::TraitItem> macro_invoc
+	  = parse_macro_invocation_semi (outer_attrs);
+
+	if (macro_invoc == nullptr)
+	  {
+	    // TODO: error?
+	    return nullptr;
+	  }
+	else
+	  {
+	    return macro_invoc;
+	  }
+	/* FIXME: macro invocations can only start with certain tokens. be
+	 * more picky with these? */
+      }
+    }
+}
+
+// Parse a typedef trait item.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::TraitItemType>
+Parser<ManagedTokenSource>::parse_trait_type (AST::AttrVec outer_attrs)
+{
+  Location locus = lexer.peek_token ()->get_locus ();
+  skip_token (TYPE);
+
+  const_TokenPtr ident_tok = expect_token (IDENTIFIER);
+  if (ident_tok == nullptr)
+    return nullptr;
+
+  Identifier ident = ident_tok->get_str ();
+
+  std::vector<std::unique_ptr<AST::TypeParamBound>> bounds;
+
+  // parse optional colon
+  if (lexer.peek_token ()->get_id () == COLON)
+    {
+      lexer.skip_token ();
+
+      // parse optional type param bounds
+      bounds
+	= parse_type_param_bounds ([] (TokenId id) { return id == SEMICOLON; });
+      // bounds = parse_type_param_bounds ();
+    }
+
+  if (!skip_token (SEMICOLON))
+    {
+      // skip?
+      return nullptr;
+    }
+
+  return std::unique_ptr<AST::TraitItemType> (
+    new AST::TraitItemType (std::move (ident), std::move (bounds),
+			    std::move (outer_attrs), locus));
+}
+
+// Parses a constant trait item.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::TraitItemConst>
+Parser<ManagedTokenSource>::parse_trait_const (AST::AttrVec outer_attrs)
+{
+  Location locus = lexer.peek_token ()->get_locus ();
+  skip_token (CONST);
+
+  // parse constant item name
+  const_TokenPtr ident_tok = expect_token (IDENTIFIER);
+  if (ident_tok == nullptr)
+    return nullptr;
+
+  Identifier ident = ident_tok->get_str ();
+
+  if (!skip_token (COLON))
+    {
+      skip_after_semicolon ();
+      return nullptr;
+    }
+
+  // parse constant trait item type
+  std::unique_ptr<AST::Type> type = parse_type ();
+
+  // parse constant trait body expression, if it exists
+  std::unique_ptr<AST::Expr> const_body = nullptr;
+  if (lexer.peek_token ()->get_id () == EQUAL)
+    {
+      lexer.skip_token ();
+
+      // expression must exist, so parse it
+      const_body = parse_expr ();
+    }
+
+  if (!skip_token (SEMICOLON))
+    {
+      // skip after something?
+      return nullptr;
+    }
+
+  return std::unique_ptr<AST::TraitItemConst> (
+    new AST::TraitItemConst (std::move (ident), std::move (type),
+			     std::move (const_body), std::move (outer_attrs),
+			     locus));
+}
+
+/* Parses a struct "impl" item (both inherent impl and trait impl can be
+ * parsed here), */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::Impl>
+Parser<ManagedTokenSource>::parse_impl (AST::Visibility vis,
+					AST::AttrVec outer_attrs)
+{
+  /* Note that only trait impls are allowed to be unsafe. So if unsafe, it
+   * must be a trait impl. However, this isn't enough for full disambiguation,
+   * so don't branch here. */
+  Location locus = lexer.peek_token ()->get_locus ();
+  bool is_unsafe = false;
+  if (lexer.peek_token ()->get_id () == UNSAFE)
+    {
+      lexer.skip_token ();
+      is_unsafe = true;
+    }
+
+  if (!skip_token (IMPL))
+    {
+      skip_after_next_block ();
+      return nullptr;
+    }
+
+  // parse generic params (shared by trait and inherent impls)
+  std::vector<std::unique_ptr<AST::GenericParam>> generic_params
+    = parse_generic_params_in_angles ();
+
+  // Again, trait impl-only feature, but optional one, so can be used for
+  // branching yet.
+  bool has_exclam = false;
+  if (lexer.peek_token ()->get_id () == EXCLAM)
+    {
+      lexer.skip_token ();
+      has_exclam = true;
+    }
+
+  /* FIXME: code that doesn't look shit for TypePath. Also, make sure this
+   * doesn't parse too much and not work. */
+  AST::TypePath type_path = parse_type_path ();
+  if (type_path.is_error () || lexer.peek_token ()->get_id () != FOR)
+    {
+      /* cannot parse type path (or not for token next, at least), so must be
+       * inherent impl */
+
+      // hacky conversion of TypePath stack object to Type pointer
+      std::unique_ptr<AST::Type> type = nullptr;
+      if (!type_path.is_error ())
+	type = std::unique_ptr<AST::TypePath> (
+	  new AST::TypePath (std::move (type_path)));
+      else
+	type = parse_type ();
+
+      // Type is required, so error if null
+      if (type == nullptr)
+	{
+	  Error error (lexer.peek_token ()->get_locus (),
+		       "could not parse type in inherent impl");
+	  add_error (std::move (error));
+
+	  skip_after_next_block ();
+	  return nullptr;
+	}
+
+      // parse optional where clause
+      AST::WhereClause where_clause = parse_where_clause ();
+
+      if (!skip_token (LEFT_CURLY))
+	{
+	  // TODO: does this still skip properly?
+	  skip_after_end_block ();
+	  return nullptr;
+	}
+
+      // parse inner attributes (optional)
+      AST::AttrVec inner_attrs = parse_inner_attributes ();
+
+      // parse inherent impl items
+      std::vector<std::unique_ptr<AST::InherentImplItem>> impl_items;
+
+      const_TokenPtr t = lexer.peek_token ();
+      while (t->get_id () != RIGHT_CURLY)
+	{
+	  std::unique_ptr<AST::InherentImplItem> impl_item
+	    = parse_inherent_impl_item ();
+
+	  if (impl_item == nullptr)
+	    {
+	      Error error (
+		lexer.peek_token ()->get_locus (),
+		"failed to parse inherent impl item in inherent impl");
+	      add_error (std::move (error));
+
+	      return nullptr;
+	    }
+
+	  impl_items.push_back (std::move (impl_item));
+
+	  t = lexer.peek_token ();
+	}
+
+      if (!skip_token (RIGHT_CURLY))
+	{
+	  // skip somewhere
+	  return nullptr;
+	}
+
+      // DEBUG
+      rust_debug ("successfully parsed inherent impl");
+
+      impl_items.shrink_to_fit ();
+
+      return std::unique_ptr<AST::InherentImpl> (new AST::InherentImpl (
+	std::move (impl_items), std::move (generic_params), std::move (type),
+	std::move (where_clause), std::move (vis), std::move (inner_attrs),
+	std::move (outer_attrs), locus));
+    }
+  else
+    {
+      // type path must both be valid and next token is for, so trait impl
+      if (!skip_token (FOR))
+	{
+	  skip_after_next_block ();
+	  return nullptr;
+	}
+
+      // parse type
+      std::unique_ptr<AST::Type> type = parse_type ();
+      // ensure type is included as it is required
+      if (type == nullptr)
+	{
+	  Error error (lexer.peek_token ()->get_locus (),
+		       "could not parse type in trait impl");
+	  add_error (std::move (error));
+
+	  skip_after_next_block ();
+	  return nullptr;
+	}
+
+      // parse optional where clause
+      AST::WhereClause where_clause = parse_where_clause ();
+
+      if (!skip_token (LEFT_CURLY))
+	{
+	  // TODO: does this still skip properly?
+	  skip_after_end_block ();
+	  return nullptr;
+	}
+
+      // parse inner attributes (optional)
+      AST::AttrVec inner_attrs = parse_inner_attributes ();
+
+      // parse trait impl items
+      std::vector<std::unique_ptr<AST::TraitImplItem>> impl_items;
+
+      const_TokenPtr t = lexer.peek_token ();
+      while (t->get_id () != RIGHT_CURLY)
+	{
+	  std::unique_ptr<AST::TraitImplItem> impl_item
+	    = parse_trait_impl_item ();
+
+	  if (impl_item == nullptr)
+	    {
+	      Error error (lexer.peek_token ()->get_locus (),
+			   "failed to parse trait impl item in trait impl");
+	      add_error (std::move (error));
+
+	      return nullptr;
+	    }
+
+	  impl_items.push_back (std::move (impl_item));
+
+	  t = lexer.peek_token ();
+
+	  // DEBUG
+	  rust_debug ("successfully parsed a trait impl item");
+	}
+      // DEBUG
+      rust_debug ("successfully finished trait impl items");
+
+      if (!skip_token (RIGHT_CURLY))
+	{
+	  // skip somewhere
+	  return nullptr;
+	}
+
+      // DEBUG
+      rust_debug ("successfully parsed trait impl");
+
+      impl_items.shrink_to_fit ();
+
+      return std::unique_ptr<AST::TraitImpl> (
+	new AST::TraitImpl (std::move (type_path), is_unsafe, has_exclam,
+			    std::move (impl_items), std::move (generic_params),
+			    std::move (type), std::move (where_clause),
+			    std::move (vis), std::move (inner_attrs),
+			    std::move (outer_attrs), locus));
+    }
+}
+
+// Parses a single inherent impl item (item inside an inherent impl block).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::InherentImplItem>
+Parser<ManagedTokenSource>::parse_inherent_impl_item ()
+{
+  // parse outer attributes (if they exist)
+  AST::AttrVec outer_attrs = parse_outer_attributes ();
+
+  // TODO: cleanup - currently an unreadable mess
+
+  // branch on next token:
+  const_TokenPtr t = lexer.peek_token ();
+  switch (t->get_id ())
+    {
+    case IDENTIFIER:
+      // FIXME: Arthur: Do we need to some lookahead here?
+      return parse_macro_invocation_semi (outer_attrs);
+    case SUPER:
+    case SELF:
+    case CRATE:
+      case PUB: {
+	// visibility, so not a macro invocation semi - must be constant,
+	// function, or method
+	AST::Visibility vis = parse_visibility ();
+
+	// TODO: is a recursive call to parse_inherent_impl_item better?
+	switch (lexer.peek_token ()->get_id ())
+	  {
+	  case EXTERN_TOK:
+	  case UNSAFE:
+	  case FN_TOK:
+	    // function or method
+	    return parse_inherent_impl_function_or_method (std::move (vis),
+							   std::move (
+							     outer_attrs));
+	  case CONST:
+	    // lookahead to resolve production - could be function/method or
+	    // const item
+	    t = lexer.peek_token (1);
+
+	    switch (t->get_id ())
+	      {
+	      case IDENTIFIER:
+	      case UNDERSCORE:
+		return parse_const_item (std::move (vis),
+					 std::move (outer_attrs));
+	      case UNSAFE:
+	      case EXTERN_TOK:
+	      case FN_TOK:
+		return parse_inherent_impl_function_or_method (std::move (vis),
+							       std::move (
+								 outer_attrs));
+	      default:
+		add_error (Error (t->get_locus (),
+				  "unexpected token %qs in some sort of const "
+				  "item in inherent impl",
+				  t->get_token_description ()));
+
+		lexer.skip_token (1); // TODO: is this right thing to do?
+		return nullptr;
+	      }
+	  default:
+	    add_error (
+	      Error (t->get_locus (),
+		     "unrecognised token %qs for item in inherent impl",
+		     t->get_token_description ()));
+	    // skip?
+	    return nullptr;
+	  }
+      }
+    case EXTERN_TOK:
+    case UNSAFE:
+    case FN_TOK:
+      // function or method
+      return parse_inherent_impl_function_or_method (
+	AST::Visibility::create_private (), std::move (outer_attrs));
+    case CONST:
+      /* lookahead to resolve production - could be function/method or const
+       * item */
+      t = lexer.peek_token (1);
+
+      switch (t->get_id ())
+	{
+	case IDENTIFIER:
+	case UNDERSCORE:
+	  return parse_const_item (AST::Visibility::create_private (),
+				   std::move (outer_attrs));
+	case UNSAFE:
+	case EXTERN_TOK:
+	case FN_TOK:
+	  return parse_inherent_impl_function_or_method (
+	    AST::Visibility::create_private (), std::move (outer_attrs));
+	default:
+	  add_error (Error (t->get_locus (),
+			    "unexpected token %qs in some sort of const item "
+			    "in inherent impl",
+			    t->get_token_description ()));
+
+	  lexer.skip_token (1); // TODO: is this right thing to do?
+	  return nullptr;
+	}
+      gcc_unreachable ();
+    default:
+      add_error (Error (t->get_locus (),
+			"unrecognised token %qs for item in inherent impl",
+			t->get_token_description ()));
+
+      // skip?
+      return nullptr;
+    }
+}
+
+/* For internal use only by parse_inherent_impl_item() - splits giant method
+ * into smaller ones and prevents duplication of logic. Strictly, this parses
+ * a function or method item inside an inherent impl item block. */
+// TODO: make this a templated function with "return type" as type param -
+// InherentImplItem is this specialisation of the template while TraitImplItem
+// will be the other.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::InherentImplItem>
+Parser<ManagedTokenSource>::parse_inherent_impl_function_or_method (
+  AST::Visibility vis, AST::AttrVec outer_attrs)
+{
+  Location locus = lexer.peek_token ()->get_locus ();
+  // parse function or method qualifiers
+  AST::FunctionQualifiers qualifiers = parse_function_qualifiers ();
+
+  skip_token (FN_TOK);
+
+  // parse function or method name
+  const_TokenPtr ident_tok = expect_token (IDENTIFIER);
+  if (ident_tok == nullptr)
+    return nullptr;
+
+  Identifier ident = ident_tok->get_str ();
+
+  // parse generic params
+  std::vector<std::unique_ptr<AST::GenericParam>> generic_params
+    = parse_generic_params_in_angles ();
+
+  if (!skip_token (LEFT_PAREN))
+    {
+      // skip after somewhere?
+      return nullptr;
+    }
+
+  // now for function vs method disambiguation - method has opening "self"
+  // param
+  AST::SelfParam self_param = parse_self_param ();
+  /* FIXME: ensure that self param doesn't accidently consume tokens for a
+   * function one idea is to lookahead up to 4 tokens to see whether self is
+   * one of them */
+  bool is_method = false;
+  if (!self_param.is_error ())
+    {
+      is_method = true;
+
+      /* skip comma so function and method regular params can be parsed in
+       * same way */
+      if (lexer.peek_token ()->get_id () == COMMA)
+	lexer.skip_token ();
+    }
+
+  // parse trait function params
+  std::vector<AST::FunctionParam> function_params
+    = parse_function_params ([] (TokenId id) { return id == RIGHT_PAREN; });
+
+  if (!skip_token (RIGHT_PAREN))
+    {
+      skip_after_end_block ();
+      return nullptr;
+    }
+
+  // parse return type (optional)
+  std::unique_ptr<AST::Type> return_type = parse_function_return_type ();
+
+  // parse where clause (optional)
+  AST::WhereClause where_clause = parse_where_clause ();
+
+  // parse function definition (in block) - semicolon not allowed
+  if (lexer.peek_token ()->get_id () == SEMICOLON)
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "%s declaration in inherent impl not allowed - must have "
+		   "a definition",
+		   is_method ? "method" : "function");
+      add_error (std::move (error));
+
+      lexer.skip_token ();
+      return nullptr;
+    }
+  std::unique_ptr<AST::BlockExpr> body = parse_block_expr ();
+  if (body == nullptr)
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "could not parse definition in inherent impl %s definition",
+		   is_method ? "method" : "function");
+      add_error (std::move (error));
+
+      skip_after_end_block ();
+      return nullptr;
+    }
+
+  // do actual if instead of ternary for return value optimisation
+  if (is_method)
+    {
+      return std::unique_ptr<AST::Method> (
+	new AST::Method (std::move (ident), std::move (qualifiers),
+			 std::move (generic_params), std::move (self_param),
+			 std::move (function_params), std::move (return_type),
+			 std::move (where_clause), std::move (body),
+			 std::move (vis), std::move (outer_attrs), locus));
+    }
+  else
+    {
+      return std::unique_ptr<AST::Function> (
+	new AST::Function (std::move (ident), std::move (qualifiers),
+			   std::move (generic_params),
+			   std::move (function_params), std::move (return_type),
+			   std::move (where_clause), std::move (body),
+			   std::move (vis), std::move (outer_attrs), locus));
+    }
+}
+
+// Parses a single trait impl item (item inside a trait impl block).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::TraitImplItem>
+Parser<ManagedTokenSource>::parse_trait_impl_item ()
+{
+  // parse outer attributes (if they exist)
+  AST::AttrVec outer_attrs = parse_outer_attributes ();
+
+  // TODO: clean this function up, it is basically unreadable hacks
+
+  // branch on next token:
+  const_TokenPtr t = lexer.peek_token ();
+  switch (t->get_id ())
+    {
+    case IDENTIFIER:
+    case SUPER:
+    case SELF:
+    case CRATE:
+    case DOLLAR_SIGN:
+      // these seem to be SimplePath tokens, so this is a macro invocation
+      // semi
+      return parse_macro_invocation_semi (std::move (outer_attrs));
+    case TYPE:
+      return parse_type_alias (AST::Visibility::create_private (),
+			       std::move (outer_attrs));
+      case PUB: {
+	// visibility, so not a macro invocation semi - must be constant,
+	// function, or method
+	AST::Visibility vis = parse_visibility ();
+
+	// TODO: is a recursive call to parse_trait_impl_item better?
+	switch (lexer.peek_token ()->get_id ())
+	  {
+	  case TYPE:
+	    return parse_type_alias (std::move (vis), std::move (outer_attrs));
+	  case EXTERN_TOK:
+	  case UNSAFE:
+	  case FN_TOK:
+	    // function or method
+	    return parse_trait_impl_function_or_method (std::move (vis),
+							std::move (
+							  outer_attrs));
+	  case CONST:
+	    // lookahead to resolve production - could be function/method or
+	    // const item
+	    t = lexer.peek_token (1);
+
+	    switch (t->get_id ())
+	      {
+	      case IDENTIFIER:
+	      case UNDERSCORE:
+		return parse_const_item (std::move (vis),
+					 std::move (outer_attrs));
+	      case UNSAFE:
+	      case EXTERN_TOK:
+	      case FN_TOK:
+		return parse_trait_impl_function_or_method (std::move (vis),
+							    std::move (
+							      outer_attrs));
+	      default:
+		add_error (Error (t->get_locus (),
+				  "unexpected token %qs in some sort of const "
+				  "item in trait impl",
+				  t->get_token_description ()));
+
+		lexer.skip_token (1); // TODO: is this right thing to do?
+		return nullptr;
+	      }
+	  default:
+	    add_error (Error (t->get_locus (),
+			      "unrecognised token %qs for item in trait impl",
+			      t->get_token_description ()));
+
+	    // skip?
+	    return nullptr;
+	  }
+      }
+    case EXTERN_TOK:
+    case UNSAFE:
+    case FN_TOK:
+      // function or method
+      return parse_trait_impl_function_or_method (
+	AST::Visibility::create_private (), std::move (outer_attrs));
+    case CONST:
+      // lookahead to resolve production - could be function/method or const
+      // item
+      t = lexer.peek_token (1);
+
+      switch (t->get_id ())
+	{
+	case IDENTIFIER:
+	case UNDERSCORE:
+	  return parse_const_item (AST::Visibility::create_private (),
+				   std::move (outer_attrs));
+	case UNSAFE:
+	case EXTERN_TOK:
+	case FN_TOK:
+	  return parse_trait_impl_function_or_method (
+	    AST::Visibility::create_private (), std::move (outer_attrs));
+	default:
+	  add_error (Error (
+	    t->get_locus (),
+	    "unexpected token %qs in some sort of const item in trait impl",
+	    t->get_token_description ()));
+
+	  lexer.skip_token (1); // TODO: is this right thing to do?
+	  return nullptr;
+	}
+      gcc_unreachable ();
+    default:
+      add_error (Error (t->get_locus (),
+			"unrecognised token %qs for item in trait impl",
+			t->get_token_description ()));
+
+      // skip?
+      return nullptr;
+    }
+}
+
+/* For internal use only by parse_trait_impl_item() - splits giant method into
+ * smaller ones and prevents duplication of logic. Strictly, this parses a
+ * function or method item inside a trait impl item block. */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::TraitImplItem>
+Parser<ManagedTokenSource>::parse_trait_impl_function_or_method (
+  AST::Visibility vis, AST::AttrVec outer_attrs)
+{
+  // this shares virtually all logic with
+  // parse_inherent_impl_function_or_method
+  // - template?
+  Location locus = lexer.peek_token ()->get_locus ();
+
+  // parse function or method qualifiers
+  AST::FunctionQualifiers qualifiers = parse_function_qualifiers ();
+
+  skip_token (FN_TOK);
+
+  // parse function or method name
+  const_TokenPtr ident_tok = expect_token (IDENTIFIER);
+  if (ident_tok == nullptr)
+    {
+      return nullptr;
+    }
+  Identifier ident = ident_tok->get_str ();
+
+  // DEBUG:
+  rust_debug (
+    "about to start parsing generic params in trait impl function or method");
+
+  // parse generic params
+  std::vector<std::unique_ptr<AST::GenericParam>> generic_params
+    = parse_generic_params_in_angles ();
+
+  // DEBUG:
+  rust_debug (
+    "finished parsing generic params in trait impl function or method");
+
+  if (!skip_token (LEFT_PAREN))
+    {
+      // skip after somewhere?
+      return nullptr;
+    }
+
+  // now for function vs method disambiguation - method has opening "self"
+  // param
+  AST::SelfParam self_param = parse_self_param ();
+  // FIXME: ensure that self param doesn't accidently consume tokens for a
+  // function
+  bool is_method = false;
+  if (!self_param.is_error ())
+    {
+      is_method = true;
+
+      // skip comma so function and method regular params can be parsed in
+      // same way
+      if (lexer.peek_token ()->get_id () == COMMA)
+	{
+	  lexer.skip_token ();
+	}
+
+      // DEBUG
+      rust_debug ("successfully parsed self param in method trait impl item");
+    }
+
+  // DEBUG
+  rust_debug (
+    "started to parse function params in function or method trait impl item");
+
+  // parse trait function params (only if next token isn't right paren)
+  std::vector<AST::FunctionParam> function_params;
+  if (lexer.peek_token ()->get_id () != RIGHT_PAREN)
+    {
+      function_params
+	= parse_function_params ([] (TokenId id) { return id == RIGHT_PAREN; });
+
+      if (function_params.empty ())
+	{
+	  Error error (
+	    lexer.peek_token ()->get_locus (),
+	    "failed to parse function params in trait impl %s definition",
+	    is_method ? "method" : "function");
+	  add_error (std::move (error));
+
+	  skip_after_next_block ();
+	  return nullptr;
+	}
+    }
+
+  // DEBUG
+  rust_debug ("successfully parsed function params in function or method "
+	      "trait impl item");
+
+  if (!skip_token (RIGHT_PAREN))
+    {
+      skip_after_next_block ();
+      return nullptr;
+    }
+
+  // parse return type (optional)
+  std::unique_ptr<AST::Type> return_type = parse_function_return_type ();
+
+  // DEBUG
+  rust_debug (
+    "successfully parsed return type in function or method trait impl item");
+
+  // parse where clause (optional)
+  AST::WhereClause where_clause = parse_where_clause ();
+
+  // DEBUG
+  rust_debug (
+    "successfully parsed where clause in function or method trait impl item");
+
+  // parse function definition (in block) - semicolon not allowed
+  if (lexer.peek_token ()->get_id () == SEMICOLON)
+    {
+      Error error (
+	lexer.peek_token ()->get_locus (),
+	"%s declaration in trait impl not allowed - must have a definition",
+	is_method ? "method" : "function");
+      add_error (std::move (error));
+
+      lexer.skip_token ();
+      return nullptr;
+    }
+  std::unique_ptr<AST::BlockExpr> body = parse_block_expr ();
+  if (body == nullptr)
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "could not parse definition in trait impl %s definition",
+		   is_method ? "method" : "function");
+      add_error (std::move (error));
+
+      skip_after_end_block ();
+      return nullptr;
+    }
+
+  // do actual if instead of ternary for return value optimisation
+  if (is_method)
+    {
+      return std::unique_ptr<AST::Method> (
+	new AST::Method (std::move (ident), std::move (qualifiers),
+			 std::move (generic_params), std::move (self_param),
+			 std::move (function_params), std::move (return_type),
+			 std::move (where_clause), std::move (body),
+			 std::move (vis), std::move (outer_attrs), locus));
+    }
+  else
+    {
+      return std::unique_ptr<AST::Function> (
+	new AST::Function (std::move (ident), std::move (qualifiers),
+			   std::move (generic_params),
+			   std::move (function_params), std::move (return_type),
+			   std::move (where_clause), std::move (body),
+			   std::move (vis), std::move (outer_attrs), locus));
+    }
+}
+
+// Parses an extern block of declarations.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ExternBlock>
+Parser<ManagedTokenSource>::parse_extern_block (AST::Visibility vis,
+						AST::AttrVec outer_attrs)
+{
+  Location locus = lexer.peek_token ()->get_locus ();
+  skip_token (EXTERN_TOK);
+
+  // detect optional abi name
+  std::string abi;
+  const_TokenPtr next_tok = lexer.peek_token ();
+  if (next_tok->get_id () == STRING_LITERAL)
+    {
+      lexer.skip_token ();
+      abi = next_tok->get_str ();
+    }
+
+  if (!skip_token (LEFT_CURLY))
+    {
+      skip_after_end_block ();
+      return nullptr;
+    }
+
+  AST::AttrVec inner_attrs = parse_inner_attributes ();
+
+  // parse declarations inside extern block
+  std::vector<std::unique_ptr<AST::ExternalItem>> extern_items;
+
+  const_TokenPtr t = lexer.peek_token ();
+  while (t->get_id () != RIGHT_CURLY)
+    {
+      std::unique_ptr<AST::ExternalItem> extern_item = parse_external_item ();
+
+      if (extern_item == nullptr)
+	{
+	  Error error (t->get_locus (),
+		       "failed to parse external item despite not reaching "
+		       "end of extern block");
+	  add_error (std::move (error));
+
+	  return nullptr;
+	}
+
+      extern_items.push_back (std::move (extern_item));
+
+      t = lexer.peek_token ();
+    }
+
+  if (!skip_token (RIGHT_CURLY))
+    {
+      // skip somewhere
+      return nullptr;
+    }
+
+  extern_items.shrink_to_fit ();
+
+  return std::unique_ptr<AST::ExternBlock> (
+    new AST::ExternBlock (std::move (abi), std::move (extern_items),
+			  std::move (vis), std::move (inner_attrs),
+			  std::move (outer_attrs), locus));
+}
+
+// Parses a single extern block item (static or function declaration).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ExternalItem>
+Parser<ManagedTokenSource>::parse_external_item ()
+{
+  // parse optional outer attributes
+  AST::AttrVec outer_attrs = parse_outer_attributes ();
+
+  Location locus = lexer.peek_token ()->get_locus ();
+
+  // parse optional visibility
+  AST::Visibility vis = parse_visibility ();
+
+  const_TokenPtr t = lexer.peek_token ();
+  switch (t->get_id ())
+    {
+    case IDENTIFIER:
+      return parse_macro_invocation_semi (outer_attrs);
+      case STATIC_TOK: {
+	// parse extern static item
+	lexer.skip_token ();
+
+	// parse mut (optional)
+	bool has_mut = false;
+	if (lexer.peek_token ()->get_id () == MUT)
+	  {
+	    lexer.skip_token ();
+	    has_mut = true;
+	  }
+
+	// parse identifier
+	const_TokenPtr ident_tok = expect_token (IDENTIFIER);
+	if (ident_tok == nullptr)
+	  {
+	    skip_after_semicolon ();
+	    return nullptr;
+	  }
+	Identifier ident = ident_tok->get_str ();
+
+	if (!skip_token (COLON))
+	  {
+	    skip_after_semicolon ();
+	    return nullptr;
+	  }
+
+	// parse type (required)
+	std::unique_ptr<AST::Type> type = parse_type ();
+	if (type == nullptr)
+	  {
+	    Error error (lexer.peek_token ()->get_locus (),
+			 "failed to parse type in external static item");
+	    add_error (std::move (error));
+
+	    skip_after_semicolon ();
+	    return nullptr;
+	  }
+
+	if (!skip_token (SEMICOLON))
+	  {
+	    // skip after somewhere?
+	    return nullptr;
+	  }
+
+	return std::unique_ptr<AST::ExternalStaticItem> (
+	  new AST::ExternalStaticItem (std::move (ident), std::move (type),
+				       has_mut, std::move (vis),
+				       std::move (outer_attrs), locus));
+      }
+      case FN_TOK: {
+	// parse extern function declaration item
+	// skip function token
+	lexer.skip_token ();
+
+	// parse identifier
+	const_TokenPtr ident_tok = expect_token (IDENTIFIER);
+	if (ident_tok == nullptr)
+	  {
+	    skip_after_semicolon ();
+	    return nullptr;
+	  }
+	Identifier ident = ident_tok->get_str ();
+
+	// parse (optional) generic params
+	std::vector<std::unique_ptr<AST::GenericParam>> generic_params
+	  = parse_generic_params_in_angles ();
+
+	if (!skip_token (LEFT_PAREN))
+	  {
+	    skip_after_semicolon ();
+	    return nullptr;
+	  }
+
+	// parse parameters
+	std::vector<AST::NamedFunctionParam> function_params;
+	bool is_variadic = false;
+	AST::AttrVec variadic_attrs;
+
+	const_TokenPtr t = lexer.peek_token ();
+	while (t->get_id () != RIGHT_PAREN)
+	  {
+	    AST::AttrVec maybe_variadic_attrs = parse_outer_attributes ();
+	    if (lexer.peek_token ()->get_id () == ELLIPSIS)
+	      {
+		// variadic - use attrs for this
+		lexer.skip_token ();
+		is_variadic = true;
+		variadic_attrs = std::move (maybe_variadic_attrs);
+		t = lexer.peek_token ();
+
+		if (t->get_id () != RIGHT_PAREN)
+		  {
+		    Error error (t->get_locus (),
+				 "expected right parentheses after variadic in "
+				 "named function "
+				 "parameters, found %qs",
+				 t->get_token_description ());
+		    add_error (std::move (error));
+
+		    skip_after_semicolon ();
+		    return nullptr;
+		  }
+
+		break;
+	      }
+
+	    AST::NamedFunctionParam param
+	      = parse_named_function_param (std::move (maybe_variadic_attrs));
+	    if (param.is_error ())
+	      {
+		Error error (t->get_locus (), "could not parse named function "
+					      "parameter in external function");
+		add_error (std::move (error));
+
+		skip_after_semicolon ();
+		return nullptr;
+	      }
+	    function_params.push_back (std::move (param));
+
+	    if (lexer.peek_token ()->get_id () != COMMA)
+	      break;
+
+	    // skip comma
+	    lexer.skip_token ();
+	    t = lexer.peek_token ();
+	  }
+
+	if (!skip_token (RIGHT_PAREN))
+	  {
+	    skip_after_semicolon ();
+	    return nullptr;
+	  }
+
+	// parse (optional) return type
+	std::unique_ptr<AST::Type> return_type = parse_function_return_type ();
+
+	// parse (optional) where clause
+	AST::WhereClause where_clause = parse_where_clause ();
+
+	if (!skip_token (SEMICOLON))
+	  {
+	    // skip somewhere?
+	    return nullptr;
+	  }
+
+	function_params.shrink_to_fit ();
+
+	return std::unique_ptr<AST::ExternalFunctionItem> (
+	  new AST::ExternalFunctionItem (
+	    std::move (ident), std::move (generic_params),
+	    std::move (return_type), std::move (where_clause),
+	    std::move (function_params), is_variadic,
+	    std::move (variadic_attrs), std::move (vis),
+	    std::move (outer_attrs), locus));
+      }
+    default:
+      // error
+      add_error (
+	Error (t->get_locus (),
+	       "unrecognised token %qs in extern block item declaration",
+	       t->get_token_description ()));
+
+      skip_after_semicolon ();
+      return nullptr;
+    }
+}
+
+/* Parses an extern block function param (with "pattern" being _ or an
+ * identifier). */
+template <typename ManagedTokenSource>
+AST::NamedFunctionParam
+Parser<ManagedTokenSource>::parse_named_function_param (
+  AST::AttrVec outer_attrs)
+{
+  // parse identifier/_
+  std::string name;
+
+  const_TokenPtr t = lexer.peek_token ();
+  Location name_location = t->get_locus ();
+  switch (t->get_id ())
+    {
+    case IDENTIFIER:
+      name = t->get_str ();
+      lexer.skip_token ();
+      break;
+    case UNDERSCORE:
+      name = "_";
+      lexer.skip_token ();
+      break;
+    default:
+      // this is not a function param, but not necessarily an error
+      return AST::NamedFunctionParam::create_error ();
+    }
+
+  if (!skip_token (COLON))
+    {
+      // skip after somewhere?
+      return AST::NamedFunctionParam::create_error ();
+    }
+
+  // parse (required) type
+  std::unique_ptr<AST::Type> param_type = parse_type ();
+  if (param_type == nullptr)
+    {
+      Error error (
+	lexer.peek_token ()->get_locus (),
+	"could not parse param type in extern block function declaration");
+      add_error (std::move (error));
+
+      skip_after_semicolon ();
+      return AST::NamedFunctionParam::create_error ();
+    }
+
+  return AST::NamedFunctionParam (std::move (name), std::move (param_type),
+				  std::move (outer_attrs), name_location);
+}
+
+// Parses a statement (will further disambiguate any statement).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::Stmt>
+Parser<ManagedTokenSource>::parse_stmt (ParseRestrictions restrictions)
+{
+  // quick exit for empty statement
+  // FIXME: Can we have empty statements without semicolons? Just nothing?
+  const_TokenPtr t = lexer.peek_token ();
+  if (t->get_id () == SEMICOLON)
+    {
+      lexer.skip_token ();
+      return std::unique_ptr<AST::EmptyStmt> (
+	new AST::EmptyStmt (t->get_locus ()));
+    }
+
+  // parse outer attributes
+  AST::AttrVec outer_attrs = parse_outer_attributes ();
+
+  // parsing this will be annoying because of the many different possibilities
+  /* best may be just to copy paste in parse_item switch, and failing that try
+   * to parse outer attributes, and then pass them in to either a let
+   * statement or (fallback) expression statement. */
+  // FIXME: think of a way to do this without such a large switch?
+  t = lexer.peek_token ();
+  switch (t->get_id ())
+    {
+    case LET:
+      // let statement
+      return parse_let_stmt (std::move (outer_attrs), restrictions);
+    case PUB:
+    case MOD:
+    case EXTERN_TOK:
+    case USE:
+    case FN_TOK:
+    case TYPE:
+    case STRUCT_TOK:
+    case ENUM_TOK:
+    case CONST:
+    case STATIC_TOK:
+    case TRAIT:
+    case IMPL:
+    /* TODO: implement union keyword but not really because of
+     * context-dependence crappy hack way to parse a union written below to
+     * separate it from the good code. */
+    // case UNION:
+    case UNSAFE: // maybe - unsafe traits are a thing
+      /* if any of these (should be all possible VisItem prefixes), parse a
+       * VisItem can't parse item because would require reparsing outer
+       * attributes */
+      return parse_vis_item (std::move (outer_attrs));
+      break;
+    case SUPER:
+    case SELF:
+    case CRATE:
+    case DOLLAR_SIGN:
+      // almost certainly macro invocation semi
+      return parse_macro_item (std::move (outer_attrs));
+      break;
+    // crappy hack to do union "keyword"
+    case IDENTIFIER:
+      if (t->get_str () == "union"
+	  && lexer.peek_token (1)->get_id () == IDENTIFIER)
+	{
+	  return parse_vis_item (std::move (outer_attrs));
+	  // or should this go straight to parsing union?
+	}
+      else if (t->get_str () == "macro_rules")
+	{
+	  // macro_rules! macro item
+	  return parse_macro_item (std::move (outer_attrs));
+	}
+      else if (lexer.peek_token (1)->get_id () == SCOPE_RESOLUTION
+	       || lexer.peek_token (1)->get_id () == EXCLAM)
+	{
+	  // FIXME: ensure doesn't take any expressions by mistake
+	  /* path (probably) or macro invocation, so probably a macro
+	   * invocation semi */
+	  return parse_macro_item (std::move (outer_attrs));
+	}
+      gcc_fallthrough ();
+      // TODO: find out how to disable gcc "implicit fallthrough" warning
+    default:
+      // fallback: expression statement
+      return parse_expr_stmt (std::move (outer_attrs), restrictions);
+      break;
+    }
+}
+
+// Parses a let statement.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::LetStmt>
+Parser<ManagedTokenSource>::parse_let_stmt (AST::AttrVec outer_attrs,
+					    ParseRestrictions restrictions)
+{
+  Location locus = lexer.peek_token ()->get_locus ();
+  skip_token (LET);
+
+  // parse pattern (required)
+  std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
+  if (pattern == nullptr)
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "failed to parse pattern in let statement");
+      add_error (std::move (error));
+
+      skip_after_semicolon ();
+      return nullptr;
+    }
+
+  // parse type declaration (optional)
+  std::unique_ptr<AST::Type> type = nullptr;
+  if (lexer.peek_token ()->get_id () == COLON)
+    {
+      // must have a type declaration
+      lexer.skip_token ();
+
+      type = parse_type ();
+      if (type == nullptr)
+	{
+	  Error error (lexer.peek_token ()->get_locus (),
+		       "failed to parse type in let statement");
+	  add_error (std::move (error));
+
+	  skip_after_semicolon ();
+	  return nullptr;
+	}
+    }
+
+  // parse expression to set variable to (optional)
+  std::unique_ptr<AST::Expr> expr = nullptr;
+  if (lexer.peek_token ()->get_id () == EQUAL)
+    {
+      // must have an expression
+      lexer.skip_token ();
+
+      expr = parse_expr ();
+      if (expr == nullptr)
+	{
+	  Error error (lexer.peek_token ()->get_locus (),
+		       "failed to parse expression in let statement");
+	  add_error (std::move (error));
+
+	  skip_after_semicolon ();
+	  return nullptr;
+	}
+    }
+
+  if (restrictions.consume_semi)
+    if (!skip_token (SEMICOLON))
+      return nullptr;
+
+  return std::unique_ptr<AST::LetStmt> (
+    new AST::LetStmt (std::move (pattern), std::move (expr), std::move (type),
+		      std::move (outer_attrs), locus));
+}
+
+// Parses a type path.
+template <typename ManagedTokenSource>
+AST::TypePath
+Parser<ManagedTokenSource>::parse_type_path ()
+{
+  bool has_opening_scope_resolution = false;
+  Location locus = lexer.peek_token ()->get_locus ();
+  if (lexer.peek_token ()->get_id () == SCOPE_RESOLUTION)
+    {
+      has_opening_scope_resolution = true;
+      lexer.skip_token ();
+    }
+
+  // create segment vector
+  std::vector<std::unique_ptr<AST::TypePathSegment>> segments;
+
+  // parse required initial segment
+  std::unique_ptr<AST::TypePathSegment> initial_segment
+    = parse_type_path_segment ();
+  if (initial_segment == nullptr)
+    {
+      // skip after somewhere?
+      // don't necessarily throw error but yeah
+      return AST::TypePath::create_error ();
+    }
+  segments.push_back (std::move (initial_segment));
+
+  // parse optional segments (as long as scope resolution operator exists)
+  const_TokenPtr t = lexer.peek_token ();
+  while (t->get_id () == SCOPE_RESOLUTION)
+    {
+      // skip scope resolution operator
+      lexer.skip_token ();
+
+      // parse the actual segment - it is an error if it doesn't exist now
+      std::unique_ptr<AST::TypePathSegment> segment
+	= parse_type_path_segment ();
+      if (segment == nullptr)
+	{
+	  // skip after somewhere?
+	  Error error (t->get_locus (), "could not parse type path segment");
+	  add_error (std::move (error));
+
+	  return AST::TypePath::create_error ();
+	}
+
+      segments.push_back (std::move (segment));
+
+      t = lexer.peek_token ();
+    }
+
+  segments.shrink_to_fit ();
+
+  return AST::TypePath (std::move (segments), locus,
+			has_opening_scope_resolution);
+}
+
+template <typename ManagedTokenSource>
+AST::GenericArg
+Parser<ManagedTokenSource>::parse_generic_arg ()
+{
+  auto tok = lexer.peek_token ();
+  std::unique_ptr<AST::Expr> expr = nullptr;
+
+  switch (tok->get_id ())
+    {
+      case IDENTIFIER: {
+	// This is a bit of a weird situation: With an identifier token, we
+	// could either have a valid type or a macro (FIXME: anything else?). So
+	// we need one bit of lookahead to differentiate if this is really
+	auto next_tok = lexer.peek_token (1);
+	if (next_tok->get_id () == EXCLAM)
+	  {
+	    auto type = parse_type ();
+	    if (type)
+	      return AST::GenericArg::create_type (std::move (type));
+	    else
+	      return AST::GenericArg::create_error ();
+	  }
+	lexer.skip_token ();
+	return AST::GenericArg::create_ambiguous (tok->get_str (),
+						  tok->get_locus ());
+      }
+    case LEFT_CURLY:
+      expr = parse_block_expr ();
+      break;
+    case MINUS:
+    case STRING_LITERAL:
+    case CHAR_LITERAL:
+    case INT_LITERAL:
+    case FLOAT_LITERAL:
+    case TRUE_LITERAL:
+    case FALSE_LITERAL:
+      expr = parse_literal_expr ();
+      break;
+      // FIXME: Because of this, error reporting is garbage for const generic
+      // parameter's default values
+      default: {
+	auto type = parse_type ();
+	// FIXME: Find a better way to do this?
+	if (type)
+	  return AST::GenericArg::create_type (std::move (type));
+	else
+	  return AST::GenericArg::create_error ();
+      }
+    }
+
+  if (!expr)
+    return AST::GenericArg::create_error ();
+
+  return AST::GenericArg::create_const (std::move (expr));
+}
+
+// Parses the generic arguments in each path segment.
+template <typename ManagedTokenSource>
+AST::GenericArgs
+Parser<ManagedTokenSource>::parse_path_generic_args ()
+{
+  if (!skip_token (LEFT_ANGLE))
+    {
+      // skip after somewhere?
+      return AST::GenericArgs::create_empty ();
+    }
+
+  // We need to parse all lifetimes, then parse types and const generics in
+  // any order.
+
+  // try to parse lifetimes first
+  std::vector<AST::Lifetime> lifetime_args;
+
+  const_TokenPtr t = lexer.peek_token ();
+  Location locus = t->get_locus ();
+  while (!is_right_angle_tok (t->get_id ()))
+    {
+      AST::Lifetime lifetime = parse_lifetime ();
+      if (lifetime.is_error ())
+	{
+	  // not necessarily an error
+	  break;
+	}
+
+      lifetime_args.push_back (std::move (lifetime));
+
+      // if next token isn't comma, then it must be end of list
+      if (lexer.peek_token ()->get_id () != COMMA)
+	{
+	  break;
+	}
+      // skip comma
+      lexer.skip_token ();
+
+      t = lexer.peek_token ();
+    }
+
+  // try to parse types and const generics second
+  std::vector<AST::GenericArg> generic_args;
+
+  // TODO: think of better control structure
+  t = lexer.peek_token ();
+  while (!is_right_angle_tok (t->get_id ()))
+    {
+      // FIXME: Is it fine to break if there is one binding? Can't there be
+      // bindings in between types?
+
+      // ensure not binding being parsed as type accidently
+      if (t->get_id () == IDENTIFIER
+	  && lexer.peek_token (1)->get_id () == EQUAL)
+	break;
+
+      auto arg = parse_generic_arg ();
+      if (!arg.is_error ())
+	{
+	  generic_args.emplace_back (std::move (arg));
+	}
+
+      // FIXME: Do we need to break if we encounter an error?
+
+      // if next token isn't comma, then it must be end of list
+      if (lexer.peek_token ()->get_id () != COMMA)
+	break;
+
+      // skip comma
+      lexer.skip_token ();
+      t = lexer.peek_token ();
+    }
+
+  // try to parse bindings third
+  std::vector<AST::GenericArgsBinding> binding_args;
+
+  // TODO: think of better control structure
+  t = lexer.peek_token ();
+  while (!is_right_angle_tok (t->get_id ()))
+    {
+      AST::GenericArgsBinding binding = parse_generic_args_binding ();
+      if (binding.is_error ())
+	{
+	  // not necessarily an error
+	  break;
+	}
+
+      binding_args.push_back (std::move (binding));
+
+      // if next token isn't comma, then it must be end of list
+      if (lexer.peek_token ()->get_id () != COMMA)
+	{
+	  break;
+	}
+      // skip comma
+      lexer.skip_token ();
+
+      t = lexer.peek_token ();
+    }
+
+  // skip any trailing commas
+  if (lexer.peek_token ()->get_id () == COMMA)
+    lexer.skip_token ();
+
+  if (!skip_generics_right_angle ())
+    return AST::GenericArgs::create_empty ();
+
+  lifetime_args.shrink_to_fit ();
+  generic_args.shrink_to_fit ();
+  binding_args.shrink_to_fit ();
+
+  return AST::GenericArgs (std::move (lifetime_args), std::move (generic_args),
+			   std::move (binding_args), locus);
+}
+
+// Parses a binding in a generic args path segment.
+template <typename ManagedTokenSource>
+AST::GenericArgsBinding
+Parser<ManagedTokenSource>::parse_generic_args_binding ()
+{
+  const_TokenPtr ident_tok = lexer.peek_token ();
+  if (ident_tok->get_id () != IDENTIFIER)
+    {
+      // allow non error-inducing use
+      // skip somewhere?
+      return AST::GenericArgsBinding::create_error ();
+    }
+  lexer.skip_token ();
+  Identifier ident = ident_tok->get_str ();
+
+  if (!skip_token (EQUAL))
+    {
+      // skip after somewhere?
+      return AST::GenericArgsBinding::create_error ();
+    }
+
+  // parse type (required)
+  std::unique_ptr<AST::Type> type = parse_type ();
+  if (type == nullptr)
+    {
+      // skip somewhere?
+      return AST::GenericArgsBinding::create_error ();
+    }
+
+  return AST::GenericArgsBinding (std::move (ident), std::move (type),
+				  ident_tok->get_locus ());
+}
+
+/* Parses a single type path segment (not including opening scope resolution,
+ * but includes any internal ones). Includes generic args or type path
+ * functions too. */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::TypePathSegment>
+Parser<ManagedTokenSource>::parse_type_path_segment ()
+{
+  Location locus = lexer.peek_token ()->get_locus ();
+  // parse ident segment part
+  AST::PathIdentSegment ident_segment = parse_path_ident_segment ();
+  if (ident_segment.is_error ())
+    {
+      // not necessarily an error
+      return nullptr;
+    }
+
+  /* lookahead to determine if variants exist - only consume scope resolution
+   * then */
+  bool has_separating_scope_resolution = false;
+  const_TokenPtr next = lexer.peek_token (1);
+  if (lexer.peek_token ()->get_id () == SCOPE_RESOLUTION
+      && (next->get_id () == LEFT_ANGLE || next->get_id () == LEFT_PAREN))
+    {
+      has_separating_scope_resolution = true;
+      lexer.skip_token ();
+    }
+
+  // branch into variants on next token
+  const_TokenPtr t = lexer.peek_token ();
+  switch (t->get_id ())
+    {
+      case LEFT_ANGLE: {
+	// parse generic args
+	AST::GenericArgs generic_args = parse_path_generic_args ();
+
+	return std::unique_ptr<AST::TypePathSegmentGeneric> (
+	  new AST::TypePathSegmentGeneric (std::move (ident_segment),
+					   has_separating_scope_resolution,
+					   std::move (generic_args), locus));
+      }
+      case LEFT_PAREN: {
+	// parse type path function
+	AST::TypePathFunction type_path_function
+	  = parse_type_path_function (locus);
+
+	if (type_path_function.is_error ())
+	  {
+	    // skip after somewhere?
+	    return nullptr;
+	  }
+
+	return std::unique_ptr<AST::TypePathSegmentFunction> (
+	  new AST::TypePathSegmentFunction (std::move (ident_segment),
+					    has_separating_scope_resolution,
+					    std::move (type_path_function),
+					    locus));
+      }
+    default:
+      // neither of them
+      return std::unique_ptr<AST::TypePathSegment> (
+	new AST::TypePathSegment (std::move (ident_segment),
+				  has_separating_scope_resolution, locus));
+    }
+  gcc_unreachable ();
+}
+
+// Parses a function call representation inside a type path.
+template <typename ManagedTokenSource>
+AST::TypePathFunction
+Parser<ManagedTokenSource>::parse_type_path_function (Location id_location)
+{
+  if (!skip_token (LEFT_PAREN))
+    {
+      // skip somewhere?
+      return AST::TypePathFunction::create_error ();
+    }
+
+  // parse function inputs
+  std::vector<std::unique_ptr<AST::Type>> inputs;
+
+  while (lexer.peek_token ()->get_id () != RIGHT_PAREN)
+    {
+      std::unique_ptr<AST::Type> type = parse_type ();
+      if (type == nullptr)
+	{
+	  /* this is an error as there should've been a ')' there if there
+	   * wasn't a type */
+	  Error error (
+	    lexer.peek_token ()->get_locus (),
+	    "failed to parse type in parameters of type path function");
+	  add_error (std::move (error));
+
+	  // skip somewhere?
+	  return AST::TypePathFunction::create_error ();
+	}
+
+      inputs.push_back (std::move (type));
+
+      // skip commas, including trailing commas
+      if (lexer.peek_token ()->get_id () != COMMA)
+	break;
+
+      lexer.skip_token ();
+    }
+
+  if (!skip_token (RIGHT_PAREN))
+    {
+      // skip somewhere?
+      return AST::TypePathFunction::create_error ();
+    }
+
+  // parse optional return type
+  std::unique_ptr<AST::Type> return_type = parse_function_return_type ();
+
+  inputs.shrink_to_fit ();
+  return AST::TypePathFunction (std::move (inputs), id_location,
+				std::move (return_type));
+}
+
+// Parses a path inside an expression that allows generic arguments.
+template <typename ManagedTokenSource>
+AST::PathInExpression
+Parser<ManagedTokenSource>::parse_path_in_expression ()
+{
+  Location locus = Linemap::unknown_location ();
+  bool has_opening_scope_resolution = false;
+  if (lexer.peek_token ()->get_id () == SCOPE_RESOLUTION)
+    {
+      has_opening_scope_resolution = true;
+
+      locus = lexer.peek_token ()->get_locus ();
+
+      lexer.skip_token ();
+    }
+
+  // create segment vector
+  std::vector<AST::PathExprSegment> segments;
+
+  if (locus == Linemap::unknown_location ())
+    {
+      locus = lexer.peek_token ()->get_locus ();
+    }
+
+  // parse required initial segment
+  AST::PathExprSegment initial_segment = parse_path_expr_segment ();
+  if (initial_segment.is_error ())
+    {
+      // skip after somewhere?
+      // don't necessarily throw error but yeah
+      return AST::PathInExpression::create_error ();
+    }
+  segments.push_back (std::move (initial_segment));
+
+  // parse optional segments (as long as scope resolution operator exists)
+  const_TokenPtr t = lexer.peek_token ();
+  while (t->get_id () == SCOPE_RESOLUTION)
+    {
+      // skip scope resolution operator
+      lexer.skip_token ();
+
+      // parse the actual segment - it is an error if it doesn't exist now
+      AST::PathExprSegment segment = parse_path_expr_segment ();
+      if (segment.is_error ())
+	{
+	  // skip after somewhere?
+	  Error error (t->get_locus (),
+		       "could not parse path expression segment");
+	  add_error (std::move (error));
+
+	  return AST::PathInExpression::create_error ();
+	}
+
+      segments.push_back (std::move (segment));
+
+      t = lexer.peek_token ();
+    }
+
+  segments.shrink_to_fit ();
+
+  return AST::PathInExpression (std::move (segments), {}, locus,
+				has_opening_scope_resolution);
+}
+
+/* Parses a single path in expression path segment (including generic
+ * arguments). */
+template <typename ManagedTokenSource>
+AST::PathExprSegment
+Parser<ManagedTokenSource>::parse_path_expr_segment ()
+{
+  Location locus = lexer.peek_token ()->get_locus ();
+  // parse ident segment
+  AST::PathIdentSegment ident = parse_path_ident_segment ();
+  if (ident.is_error ())
+    {
+      // not necessarily an error?
+      return AST::PathExprSegment::create_error ();
+    }
+
+  // parse generic args (and turbofish), if they exist
+  /* use lookahead to determine if they actually exist (don't want to
+   * accidently parse over next ident segment) */
+  if (lexer.peek_token ()->get_id () == SCOPE_RESOLUTION
+      && lexer.peek_token (1)->get_id () == LEFT_ANGLE)
+    {
+      // skip scope resolution
+      lexer.skip_token ();
+
+      AST::GenericArgs generic_args = parse_path_generic_args ();
+
+      return AST::PathExprSegment (std::move (ident), locus,
+				   std::move (generic_args));
+    }
+
+  // return a generic parameter-less expr segment if not found
+  return AST::PathExprSegment (std::move (ident), locus);
+}
+
+/* Parses a fully qualified path in expression (i.e. a pattern). FIXME does
+ * not parse outer attrs. */
+template <typename ManagedTokenSource>
+AST::QualifiedPathInExpression
+Parser<ManagedTokenSource>::parse_qualified_path_in_expression (
+  Location pratt_parsed_loc)
+{
+  /* Note: the Rust grammar is defined in such a way that it is impossible to
+   * determine whether a prospective qualified path is a
+   * QualifiedPathInExpression or QualifiedPathInType in all cases by the
+   * rules themselves (the only possible difference is a TypePathSegment with
+   * function, and lookahead to find this is too difficult). However, as this
+   * is a pattern and QualifiedPathInType is a type, I believe it that their
+   * construction will not be confused (due to rules regarding patterns vs
+   * types).
+   * As such, this function will not attempt to minimise errors created by
+   * their confusion. */
+
+  // parse the qualified path type (required)
+  AST::QualifiedPathType qual_path_type
+    = parse_qualified_path_type (pratt_parsed_loc);
+  if (qual_path_type.is_error ())
+    {
+      // TODO: should this create a parse error?
+      return AST::QualifiedPathInExpression::create_error ();
+    }
+  Location locus = qual_path_type.get_locus ();
+
+  // parse path segments
+  std::vector<AST::PathExprSegment> segments;
+
+  // parse initial required segment
+  if (!expect_token (SCOPE_RESOLUTION))
+    {
+      // skip after somewhere?
+
+      return AST::QualifiedPathInExpression::create_error ();
+    }
+  AST::PathExprSegment initial_segment = parse_path_expr_segment ();
+  if (initial_segment.is_error ())
+    {
+      // skip after somewhere?
+      Error error (lexer.peek_token ()->get_locus (),
+		   "required initial path expression segment in "
+		   "qualified path in expression could not be parsed");
+      add_error (std::move (error));
+
+      return AST::QualifiedPathInExpression::create_error ();
+    }
+  segments.push_back (std::move (initial_segment));
+
+  // parse optional segments (as long as scope resolution operator exists)
+  const_TokenPtr t = lexer.peek_token ();
+  while (t->get_id () == SCOPE_RESOLUTION)
+    {
+      // skip scope resolution operator
+      lexer.skip_token ();
+
+      // parse the actual segment - it is an error if it doesn't exist now
+      AST::PathExprSegment segment = parse_path_expr_segment ();
+      if (segment.is_error ())
+	{
+	  // skip after somewhere?
+	  Error error (t->get_locus (),
+		       "could not parse path expression segment in qualified "
+		       "path in expression");
+	  add_error (std::move (error));
+
+	  return AST::QualifiedPathInExpression::create_error ();
+	}
+
+      segments.push_back (std::move (segment));
+
+      t = lexer.peek_token ();
+    }
+
+  segments.shrink_to_fit ();
+
+  // FIXME: outer attr parsing
+  return AST::QualifiedPathInExpression (std::move (qual_path_type),
+					 std::move (segments), {}, locus);
+}
+
+// Parses the type syntactical construction at the start of a qualified path.
+template <typename ManagedTokenSource>
+AST::QualifiedPathType
+Parser<ManagedTokenSource>::parse_qualified_path_type (
+  Location pratt_parsed_loc)
+{
+  Location locus = pratt_parsed_loc;
+  /* TODO: should this actually be error? is there anywhere where this could
+   * be valid? */
+  if (locus == Linemap::unknown_location ())
+    {
+      locus = lexer.peek_token ()->get_locus ();
+      if (!skip_token (LEFT_ANGLE))
+	{
+	  // skip after somewhere?
+	  return AST::QualifiedPathType::create_error ();
+	}
+    }
+
+  // parse type (required)
+  std::unique_ptr<AST::Type> type = parse_type ();
+  if (type == nullptr)
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "could not parse type in qualified path type");
+      add_error (std::move (error));
+
+      // skip somewhere?
+      return AST::QualifiedPathType::create_error ();
+    }
+
+  // parse optional as clause
+  AST::TypePath as_type_path = AST::TypePath::create_error ();
+  if (lexer.peek_token ()->get_id () == AS)
+    {
+      lexer.skip_token ();
+
+      // parse type path, which is required now
+      as_type_path = parse_type_path ();
+      if (as_type_path.is_error ())
+	{
+	  Error error (
+	    lexer.peek_token ()->get_locus (),
+	    "could not parse type path in as clause in qualified path type");
+	  add_error (std::move (error));
+
+	  // skip somewhere?
+	  return AST::QualifiedPathType::create_error ();
+	}
+    }
+
+  /* NOTE: should actually be a right-angle token, so
+   * skip_generics_right_angle shouldn't be required */
+  if (!skip_token (RIGHT_ANGLE))
+    {
+      // skip after somewhere?
+      return AST::QualifiedPathType::create_error ();
+    }
+
+  return AST::QualifiedPathType (std::move (type), locus,
+				 std::move (as_type_path));
+}
+
+// Parses a fully qualified path in type (i.e. a type).
+template <typename ManagedTokenSource>
+AST::QualifiedPathInType
+Parser<ManagedTokenSource>::parse_qualified_path_in_type ()
+{
+  Location locus = lexer.peek_token ()->get_locus ();
+  // parse the qualified path type (required)
+  AST::QualifiedPathType qual_path_type = parse_qualified_path_type ();
+  if (qual_path_type.is_error ())
+    {
+      // TODO: should this create a parse error?
+      return AST::QualifiedPathInType::create_error ();
+    }
+
+  // parse initial required segment
+  if (!expect_token (SCOPE_RESOLUTION))
+    {
+      // skip after somewhere?
+
+      return AST::QualifiedPathInType::create_error ();
+    }
+  std::unique_ptr<AST::TypePathSegment> initial_segment
+    = parse_type_path_segment ();
+  if (initial_segment == nullptr)
+    {
+      // skip after somewhere?
+      Error error (lexer.peek_token ()->get_locus (),
+		   "required initial type path segment in qualified path in "
+		   "type could not be parsed");
+      add_error (std::move (error));
+
+      return AST::QualifiedPathInType::create_error ();
+    }
+
+  // parse optional segments (as long as scope resolution operator exists)
+  std::vector<std::unique_ptr<AST::TypePathSegment>> segments;
+  const_TokenPtr t = lexer.peek_token ();
+  while (t->get_id () == SCOPE_RESOLUTION)
+    {
+      // skip scope resolution operator
+      lexer.skip_token ();
+
+      // parse the actual segment - it is an error if it doesn't exist now
+      std::unique_ptr<AST::TypePathSegment> segment
+	= parse_type_path_segment ();
+      if (segment == nullptr)
+	{
+	  // skip after somewhere?
+	  Error error (
+	    t->get_locus (),
+	    "could not parse type path segment in qualified path in type");
+	  add_error (std::move (error));
+
+	  return AST::QualifiedPathInType::create_error ();
+	}
+
+      segments.push_back (std::move (segment));
+
+      t = lexer.peek_token ();
+    }
+
+  segments.shrink_to_fit ();
+
+  return AST::QualifiedPathInType (std::move (qual_path_type),
+				   std::move (initial_segment),
+				   std::move (segments), locus);
+}
+
+// Parses a self param. Also handles self param not existing.
+template <typename ManagedTokenSource>
+AST::SelfParam
+Parser<ManagedTokenSource>::parse_self_param ()
+{
+  bool has_reference = false;
+  AST::Lifetime lifetime = AST::Lifetime::error ();
+
+  Location locus = lexer.peek_token ()->get_locus ();
+
+  // test if self is a reference parameter
+  if (lexer.peek_token ()->get_id () == AMP)
+    {
+      has_reference = true;
+      lexer.skip_token ();
+
+      // now test whether it has a lifetime
+      if (lexer.peek_token ()->get_id () == LIFETIME)
+	{
+	  lifetime = parse_lifetime ();
+
+	  // something went wrong somehow
+	  if (lifetime.is_error ())
+	    {
+	      Error error (lexer.peek_token ()->get_locus (),
+			   "failed to parse lifetime in self param");
+	      add_error (std::move (error));
+
+	      // skip after somewhere?
+	      return AST::SelfParam::create_error ();
+	    }
+	}
+    }
+
+  // test for mut
+  bool has_mut = false;
+  if (lexer.peek_token ()->get_id () == MUT)
+    {
+      has_mut = true;
+      lexer.skip_token ();
+    }
+
+  // skip self token
+  const_TokenPtr self_tok = lexer.peek_token ();
+  if (self_tok->get_id () != SELF)
+    {
+      // skip after somewhere?
+      return AST::SelfParam::create_error ();
+    }
+  lexer.skip_token ();
+
+  // parse optional type
+  std::unique_ptr<AST::Type> type = nullptr;
+  if (lexer.peek_token ()->get_id () == COLON)
+    {
+      lexer.skip_token ();
+
+      // type is now required
+      type = parse_type ();
+      if (type == nullptr)
+	{
+	  Error error (lexer.peek_token ()->get_locus (),
+		       "could not parse type in self param");
+	  add_error (std::move (error));
+
+	  // skip after somewhere?
+	  return AST::SelfParam::create_error ();
+	}
+    }
+
+  // ensure that cannot have both type and reference
+  if (type != nullptr && has_reference)
+    {
+      Error error (
+	lexer.peek_token ()->get_locus (),
+	"cannot have both a reference and a type specified in a self param");
+      add_error (std::move (error));
+
+      // skip after somewhere?
+      return AST::SelfParam::create_error ();
+    }
+
+  if (has_reference)
+    {
+      return AST::SelfParam (std::move (lifetime), has_mut, locus);
+    }
+  else
+    {
+      // note that type may be nullptr here and that's fine
+      return AST::SelfParam (std::move (type), has_mut, locus);
+    }
+}
+
+/* Parses a method. Note that this function is probably useless because using
+ * lookahead to determine whether a function is a method is a PITA (maybe not
+ * even doable), so most places probably parse a "function or method" and then
+ * resolve it into whatever it is afterward. As such, this is only here for
+ * algorithmically defining the grammar rule. */
+template <typename ManagedTokenSource>
+AST::Method
+Parser<ManagedTokenSource>::parse_method ()
+{
+  Location locus = lexer.peek_token ()->get_locus ();
+  /* Note: as a result of the above, this will not attempt to disambiguate a
+   * function parse qualifiers */
+  AST::FunctionQualifiers qualifiers = parse_function_qualifiers ();
+
+  skip_token (FN_TOK);
+
+  const_TokenPtr ident_tok = expect_token (IDENTIFIER);
+  if (ident_tok == nullptr)
+    {
+      skip_after_next_block ();
+      return AST::Method::create_error ();
+    }
+  Identifier method_name = ident_tok->get_str ();
+
+  // parse generic params - if exist
+  std::vector<std::unique_ptr<AST::GenericParam>> generic_params
+    = parse_generic_params_in_angles ();
+
+  if (!skip_token (LEFT_PAREN))
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "method missing opening parentheses before parameter list");
+      add_error (std::move (error));
+
+      skip_after_next_block ();
+      return AST::Method::create_error ();
+    }
+
+  // parse self param
+  AST::SelfParam self_param = parse_self_param ();
+  if (self_param.is_error ())
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "could not parse self param in method");
+      add_error (std::move (error));
+
+      skip_after_next_block ();
+      return AST::Method::create_error ();
+    }
+
+  // skip comma if it exists
+  if (lexer.peek_token ()->get_id () == COMMA)
+    lexer.skip_token ();
+
+  // parse function parameters
+  std::vector<AST::FunctionParam> function_params
+    = parse_function_params ([] (TokenId id) { return id == RIGHT_PAREN; });
+
+  if (!skip_token (RIGHT_PAREN))
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "method declaration missing closing parentheses after "
+		   "parameter list");
+      add_error (std::move (error));
+
+      skip_after_next_block ();
+      return AST::Method::create_error ();
+    }
+
+  // parse function return type - if exists
+  std::unique_ptr<AST::Type> return_type = parse_function_return_type ();
+
+  // parse where clause - if exists
+  AST::WhereClause where_clause = parse_where_clause ();
+
+  // parse block expression
+  std::unique_ptr<AST::BlockExpr> block_expr = parse_block_expr ();
+  if (block_expr == nullptr)
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "method declaration missing block expression");
+      add_error (std::move (error));
+
+      skip_after_end_block ();
+      return AST::Method::create_error ();
+    }
+
+  // does not parse visibility, but this method isn't used, so doesn't matter
+  return AST::Method (std::move (method_name), std::move (qualifiers),
+		      std::move (generic_params), std::move (self_param),
+		      std::move (function_params), std::move (return_type),
+		      std::move (where_clause), std::move (block_expr),
+		      AST::Visibility::create_error (), AST::AttrVec (), locus);
+}
+
+/* Parses an expression statement (disambiguates to expression with or without
+ * block statement). */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ExprStmt>
+Parser<ManagedTokenSource>::parse_expr_stmt (AST::AttrVec outer_attrs,
+					     ParseRestrictions restrictions)
+{
+  /* potential thoughts - define new virtual method "has_block()" on expr.
+   * parse expr and then determine whether semicolon is needed as a result of
+   * this method. but then this would require dynamic_cast, which is not
+   * allowed. */
+
+  /* okay new thought - big switch to disambiguate exprs with blocks - either
+   * block expr, async block expr, unsafe block expr, loop expr, if expr, if
+   * let expr, or match expr. So all others are exprs without block. */
+  /* new thought: possible initial tokens: 'loop', 'while', 'for', lifetime
+   * (and then ':' and then loop), 'if', 'match', '{', 'async', 'unsafe' (and
+   * then
+   * '{')). This seems to have no ambiguity. */
+
+  const_TokenPtr t = lexer.peek_token ();
+  /* TODO: should the switch just directly call the individual parse methods
+   * rather than adding another layer of indirection with
+   * parse_expr_stmt_with_block()? */
+  switch (t->get_id ())
+    {
+    case LOOP:
+    case WHILE:
+    case FOR:
+    case IF:
+    case MATCH_TOK:
+    case LEFT_CURLY:
+    case ASYNC:
+      // expression with block
+      return parse_expr_stmt_with_block (std::move (outer_attrs));
+      case LIFETIME: {
+	/* FIXME: are there any expressions without blocks that can have
+	 * lifetime as their first token? Or is loop expr the only one? */
+	// safe side for now:
+	if (lexer.peek_token (1)->get_id () == COLON
+	    && lexer.peek_token (2)->get_id () == LOOP)
+	  {
+	    return parse_expr_stmt_with_block (std::move (outer_attrs));
+	  }
+	else
+	  {
+	    return parse_expr_stmt_without_block (std::move (outer_attrs),
+						  restrictions);
+	  }
+      }
+      case UNSAFE: {
+	/* FIXME: are there any expressions without blocks that can have
+	 * unsafe as their first token? Or is unsafe the only one? */
+	// safe side for now
+	if (lexer.peek_token (1)->get_id () == LEFT_CURLY)
+	  {
+	    return parse_expr_stmt_with_block (std::move (outer_attrs));
+	  }
+	else
+	  {
+	    return parse_expr_stmt_without_block (std::move (outer_attrs),
+						  restrictions);
+	  }
+      }
+    default:
+      // not a parse expr with block, so must be expr without block
+      /* TODO: if possible, be more selective about possible expr without
+       * block initial tokens in order to prevent more syntactical errors at
+       * parse time. */
+      return parse_expr_stmt_without_block (std::move (outer_attrs),
+					    restrictions);
+    }
+}
+
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ExprWithBlock>
+Parser<ManagedTokenSource>::parse_expr_with_block (AST::AttrVec outer_attrs)
+{
+  std::unique_ptr<AST::ExprWithBlock> expr_parsed = nullptr;
+
+  const_TokenPtr t = lexer.peek_token ();
+  switch (t->get_id ())
+    {
+    case IF:
+      // if or if let, so more lookahead to find out
+      if (lexer.peek_token (1)->get_id () == LET)
+	{
+	  // if let expr
+	  expr_parsed = parse_if_let_expr (std::move (outer_attrs));
+	  break;
+	}
+      else
+	{
+	  // if expr
+	  expr_parsed = parse_if_expr (std::move (outer_attrs));
+	  break;
+	}
+    case LOOP:
+      // infinite loop
+      expr_parsed = parse_loop_expr (std::move (outer_attrs));
+      break;
+    case FOR:
+      // "for" iterator loop
+      expr_parsed = parse_for_loop_expr (std::move (outer_attrs));
+      break;
+      case WHILE: {
+	// while or while let, so more lookahead to find out
+	if (lexer.peek_token (1)->get_id () == LET)
+	  {
+	    // while let loop expr
+	    expr_parsed = parse_while_let_loop_expr (std::move (outer_attrs));
+	    break;
+	  }
+	else
+	  {
+	    // while loop expr
+	    expr_parsed = parse_while_loop_expr (std::move (outer_attrs));
+	    break;
+	  }
+      }
+    case MATCH_TOK:
+      // match expression
+      expr_parsed = parse_match_expr (std::move (outer_attrs));
+      break;
+    case LEFT_CURLY:
+      // block expression
+      expr_parsed = parse_block_expr (std::move (outer_attrs));
+      break;
+    case ASYNC:
+      // async block expression
+      expr_parsed = parse_async_block_expr (std::move (outer_attrs));
+      break;
+    case UNSAFE:
+      // unsafe block expression
+      expr_parsed = parse_unsafe_block_expr (std::move (outer_attrs));
+      break;
+    case LIFETIME:
+      // some kind of loop expr (with loop label)
+      expr_parsed = parse_labelled_loop_expr (std::move (outer_attrs));
+      break;
+    default:
+      add_error (Error (
+	t->get_locus (),
+	"could not recognise expr beginning with %qs as an expr with block in"
+	" parsing expr statement",
+	t->get_token_description ()));
+
+      skip_after_next_block ();
+      return nullptr;
+    }
+
+  // ensure expr parsed exists
+  if (expr_parsed == nullptr)
+    {
+      Error error (t->get_locus (),
+		   "failed to parse expr with block in parsing expr statement");
+      add_error (std::move (error));
+
+      skip_after_end_block ();
+      return nullptr;
+    }
+
+  return expr_parsed;
+}
+
+/* Parses a expression statement containing an expression with block.
+ * Disambiguates internally. */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ExprStmtWithBlock>
+Parser<ManagedTokenSource>::parse_expr_stmt_with_block (
+  AST::AttrVec outer_attrs)
+{
+  auto expr_parsed = parse_expr_with_block (std::move (outer_attrs));
+  auto locus = expr_parsed->get_locus ();
+
+  // return expr stmt created from expr
+  return std::unique_ptr<AST::ExprStmtWithBlock> (
+    new AST::ExprStmtWithBlock (std::move (expr_parsed), locus,
+				lexer.peek_token ()->get_id () == SEMICOLON));
+}
+
+/* Parses an expression statement containing an expression without block.
+ * Disambiguates further. */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ExprStmtWithoutBlock>
+Parser<ManagedTokenSource>::parse_expr_stmt_without_block (
+  AST::AttrVec outer_attrs, ParseRestrictions restrictions)
+{
+  /* TODO: maybe move more logic for expr without block in here for better
+   * error handling */
+
+  // attempt to parse via parse_expr_without_block - seems to work
+  std::unique_ptr<AST::ExprWithoutBlock> expr = nullptr;
+  Location locus = lexer.peek_token ()->get_locus ();
+
+  restrictions.expr_can_be_stmt = true;
+
+  expr = parse_expr_without_block (std::move (outer_attrs), restrictions);
+  if (expr == nullptr)
+    {
+      // expr is required, error
+      Error error (lexer.peek_token ()->get_locus (),
+		   "failed to parse expr without block in expr statement");
+      add_error (std::move (error));
+
+      skip_after_semicolon ();
+      return nullptr;
+    }
+
+  if (restrictions.consume_semi)
+    if (!skip_token (SEMICOLON))
+      return nullptr;
+
+  return std::unique_ptr<AST::ExprStmtWithoutBlock> (
+    new AST::ExprStmtWithoutBlock (std::move (expr), locus));
+}
+
+/* Parses an expression without a block associated with it (further
+ * disambiguates). */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ExprWithoutBlock>
+Parser<ManagedTokenSource>::parse_expr_without_block (
+  AST::AttrVec outer_attrs, ParseRestrictions restrictions)
+{
+  /* Notes on types of expr without block:
+   *  - literal expr          tokens that are literals
+   *  - path expr             path_in_expr or qual_path_in_expr
+   *  - operator expr         many different types
+   *     unary:
+   *      borrow expr         ( '&' | '&&' ) 'mut'? expr
+   *      dereference expr    '*' expr
+   *      error propagation   expr '?'
+   *      negation            '-' expr
+   *      not                 '!' expr
+   *     binary: all start with expr
+   *  - grouped/paren expr    '(' inner_attributes expr ')'
+   *  - array expr            '[' inner_attributes array_elems? ']'
+   *  - await expr            expr '.' 'await'
+   *  - (array/slice) index expr  expr '[' expr ']'
+   *  - tuple expr            '(' inner_attributes tuple_elems? ')'
+   *      note that a single elem tuple is distinguished from a grouped expr
+   * by a trailing comma, i.e. a grouped expr is preferred over a tuple expr
+   *  - tuple index expr      expr '.' tuple_index
+   *  - struct expr           path_in_expr (and optional other stuff)
+   *  - enum variant expr     path_in_expr (and optional other stuff)
+   *      this means that there is no syntactic difference between an enum
+   * variant and a struct
+   *      - only name resolution can tell the difference. Thus, maybe rework
+   * AST to take this into account ("struct or enum" nodes?)
+   *  - (function) call expr  expr '(' call_params? ')'
+   *  - method call expr      expr '.' path_expr_segment '(' call_params? ')'
+   *  - field expr            expr '.' identifier
+   *      note that method call expr is preferred, i.e. field expr must not be
+   * followed by parenthesised expression sequence.
+   *  - closure expr          'move'? ( '||' | '|' closure_params? '|' ) (
+   * expr | '->' type_no_bounds block_expr )
+   *  - continue expr         'continue' labelled_lifetime?
+   *  - break expr            'break' labelled_lifetime? expr?
+   *  - range expr            many different types but all involve '..' or
+   * '..='
+   *  - return expr           'return' as 1st tok
+   *  - macro invocation      identifier then :: or identifier then !
+   * (simple_path '!')
+   *
+   * any that have rules beginning with 'expr' should probably be
+   * pratt-parsed,
+   * with parsing type to use determined by token AND lookahead. */
+
+  // ok well at least can do easy ones
+  const_TokenPtr t = lexer.peek_token ();
+  switch (t->get_id ())
+    {
+    case RETURN_TOK:
+      // return expr
+      return parse_return_expr (std::move (outer_attrs));
+    case BREAK:
+      // break expr
+      return parse_break_expr (std::move (outer_attrs));
+    case CONTINUE:
+      // continue expr
+      return parse_continue_expr (std::move (outer_attrs));
+    case MOVE:
+      // closure expr (though not all closure exprs require this)
+      return parse_closure_expr (std::move (outer_attrs));
+    case LEFT_SQUARE:
+      // array expr (creation, not index)
+      return parse_array_expr (std::move (outer_attrs));
+      default: {
+	/* HACK: piggyback on pratt parsed expr and abuse polymorphism to
+	 * essentially downcast */
+
+	std::unique_ptr<AST::Expr> expr
+	  = parse_expr (std::move (outer_attrs), restrictions);
+
+	if (expr == nullptr)
+	  {
+	    Error error (t->get_locus (),
+			 "failed to parse expression for expression without "
+			 "block (pratt-parsed expression is null)");
+	    add_error (std::move (error));
+
+	    return nullptr;
+	  }
+
+	std::unique_ptr<AST::ExprWithoutBlock> expr_without_block (
+	  expr->as_expr_without_block ());
+
+	if (expr_without_block != nullptr)
+	  {
+	    return expr_without_block;
+	  }
+	else
+	  {
+	    Error error (t->get_locus (),
+			 "converted expr without block is null");
+	    add_error (std::move (error));
+
+	    return nullptr;
+	  }
+      }
+    }
+}
+
+// Parses a block expression, including the curly braces at start and end.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::BlockExpr>
+Parser<ManagedTokenSource>::parse_block_expr (AST::AttrVec outer_attrs,
+					      Location pratt_parsed_loc)
+{
+  Location locus = pratt_parsed_loc;
+  if (locus == Linemap::unknown_location ())
+    {
+      locus = lexer.peek_token ()->get_locus ();
+      if (!skip_token (LEFT_CURLY))
+	{
+	  skip_after_end_block ();
+	  return nullptr;
+	}
+    }
+
+  AST::AttrVec inner_attrs = parse_inner_attributes ();
+
+  // parse statements and expression
+  std::vector<std::unique_ptr<AST::Stmt>> stmts;
+  std::unique_ptr<AST::Expr> expr = nullptr;
+
+  const_TokenPtr t = lexer.peek_token ();
+  while (t->get_id () != RIGHT_CURLY)
+    {
+      ExprOrStmt expr_or_stmt = parse_stmt_or_expr_without_block ();
+      if (expr_or_stmt.is_error ())
+	{
+	  Error error (t->get_locus (),
+		       "failed to parse statement or expression without "
+		       "block in block expression");
+	  add_error (std::move (error));
+
+	  return nullptr;
+	}
+
+      t = lexer.peek_token ();
+
+      if (expr_or_stmt.stmt != nullptr)
+	{
+	  stmts.push_back (std::move (expr_or_stmt.stmt));
+	}
+      else
+	{
+	  // assign to expression and end parsing inside
+	  expr = std::move (expr_or_stmt.expr);
+	  break;
+	}
+    }
+
+  Location end_locus = t->get_locus ();
+
+  if (!skip_token (RIGHT_CURLY))
+    {
+      Error error (t->get_locus (),
+		   "error may be from having an expression (as opposed to "
+		   "statement) in the body of the function but not last");
+      add_error (std::move (error));
+
+      skip_after_end_block ();
+      return nullptr;
+    }
+
+  // grammar allows for empty block expressions
+
+  stmts.shrink_to_fit ();
+
+  return std::unique_ptr<AST::BlockExpr> (
+    new AST::BlockExpr (std::move (stmts), std::move (expr),
+			std::move (inner_attrs), std::move (outer_attrs), locus,
+			end_locus));
+}
+
+/* Parses a "grouped" expression (expression in parentheses), used to control
+ * precedence. */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::GroupedExpr>
+Parser<ManagedTokenSource>::parse_grouped_expr (AST::AttrVec outer_attrs)
+{
+  Location locus = lexer.peek_token ()->get_locus ();
+  skip_token (LEFT_PAREN);
+
+  AST::AttrVec inner_attrs = parse_inner_attributes ();
+
+  // parse required expr inside parentheses
+  std::unique_ptr<AST::Expr> expr_in_parens = parse_expr ();
+  if (expr_in_parens == nullptr)
+    {
+      // skip after somewhere?
+      // error?
+      return nullptr;
+    }
+
+  if (!skip_token (RIGHT_PAREN))
+    {
+      // skip after somewhere?
+      return nullptr;
+    }
+
+  return std::unique_ptr<AST::GroupedExpr> (
+    new AST::GroupedExpr (std::move (expr_in_parens), std::move (inner_attrs),
+			  std::move (outer_attrs), locus));
+}
+
+// Parses a closure expression (closure definition).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ClosureExpr>
+Parser<ManagedTokenSource>::parse_closure_expr (AST::AttrVec outer_attrs)
+{
+  Location locus = lexer.peek_token ()->get_locus ();
+  // detect optional "move"
+  bool has_move = false;
+  if (lexer.peek_token ()->get_id () == MOVE)
+    {
+      lexer.skip_token ();
+      has_move = true;
+    }
+
+  // handle parameter list
+  std::vector<AST::ClosureParam> params;
+
+  const_TokenPtr t = lexer.peek_token ();
+  switch (t->get_id ())
+    {
+    case OR:
+      // skip token, no parameters
+      lexer.skip_token ();
+      break;
+    case PIPE:
+      // actually may have parameters
+      lexer.skip_token ();
+
+      while (t->get_id () != PIPE)
+	{
+	  AST::ClosureParam param = parse_closure_param ();
+	  if (param.is_error ())
+	    {
+	      // TODO is this really an error?
+	      Error error (t->get_locus (), "could not parse closure param");
+	      add_error (std::move (error));
+
+	      break;
+	    }
+	  params.push_back (std::move (param));
+
+	  if (lexer.peek_token ()->get_id () != COMMA)
+	    {
+	      // not an error but means param list is done
+	      break;
+	    }
+	  // skip comma
+	  lexer.skip_token ();
+
+	  t = lexer.peek_token ();
+	}
+      params.shrink_to_fit ();
+      break;
+    default:
+      add_error (Error (t->get_locus (),
+			"unexpected token %qs in closure expression - expected "
+			"%<|%> or %<||%>",
+			t->get_token_description ()));
+
+      // skip somewhere?
+      return nullptr;
+    }
+
+  // again branch based on next token
+  t = lexer.peek_token ();
+  if (t->get_id () == RETURN_TYPE)
+    {
+      // must be return type closure with block expr
+
+      // skip "return type" token
+      lexer.skip_token ();
+
+      // parse actual type, which is required
+      std::unique_ptr<AST::TypeNoBounds> type = parse_type_no_bounds ();
+      if (type == nullptr)
+	{
+	  // error
+	  Error error (t->get_locus (), "failed to parse type for closure");
+	  add_error (std::move (error));
+
+	  // skip somewhere?
+	  return nullptr;
+	}
+
+      // parse block expr, which is required
+      std::unique_ptr<AST::BlockExpr> block = parse_block_expr ();
+      if (block == nullptr)
+	{
+	  // error
+	  Error error (lexer.peek_token ()->get_locus (),
+		       "failed to parse block expr in closure");
+	  add_error (std::move (error));
+
+	  // skip somewhere?
+	  return nullptr;
+	}
+
+      return std::unique_ptr<AST::ClosureExprInnerTyped> (
+	new AST::ClosureExprInnerTyped (std::move (type), std::move (block),
+					std::move (params), locus, has_move,
+					std::move (outer_attrs)));
+    }
+  else
+    {
+      // must be expr-only closure
+
+      // parse expr, which is required
+      std::unique_ptr<AST::Expr> expr = parse_expr ();
+      if (expr == nullptr)
+	{
+	  Error error (t->get_locus (),
+		       "failed to parse expression in closure");
+	  add_error (std::move (error));
+
+	  // skip somewhere?
+	  return nullptr;
+	}
+
+      return std::unique_ptr<AST::ClosureExprInner> (
+	new AST::ClosureExprInner (std::move (expr), std::move (params), locus,
+				   has_move, std::move (outer_attrs)));
+    }
+}
+
+// Parses a literal token (to literal expression).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::LiteralExpr>
+Parser<ManagedTokenSource>::parse_literal_expr (AST::AttrVec outer_attrs)
+{
+  // TODO: change if literal representation in lexer changes
+
+  std::string literal_value;
+  AST::Literal::LitType type = AST::Literal::STRING;
+
+  // branch based on token
+  const_TokenPtr t = lexer.peek_token ();
+  switch (t->get_id ())
+    {
+    case CHAR_LITERAL:
+      type = AST::Literal::CHAR;
+      literal_value = t->get_str ();
+      lexer.skip_token ();
+      break;
+    case STRING_LITERAL:
+      type = AST::Literal::STRING;
+      literal_value = t->get_str ();
+      lexer.skip_token ();
+      break;
+    case BYTE_CHAR_LITERAL:
+      type = AST::Literal::BYTE;
+      literal_value = t->get_str ();
+      lexer.skip_token ();
+      break;
+    case BYTE_STRING_LITERAL:
+      type = AST::Literal::BYTE_STRING;
+      literal_value = t->get_str ();
+      lexer.skip_token ();
+      break;
+    case INT_LITERAL:
+      type = AST::Literal::INT;
+      literal_value = t->get_str ();
+      lexer.skip_token ();
+      break;
+    case FLOAT_LITERAL:
+      type = AST::Literal::FLOAT;
+      literal_value = t->get_str ();
+      lexer.skip_token ();
+      break;
+    // case BOOL_LITERAL
+    // use true and false keywords rather than "bool literal" Rust terminology
+    case TRUE_LITERAL:
+      type = AST::Literal::BOOL;
+      literal_value = "true";
+      lexer.skip_token ();
+      break;
+    case FALSE_LITERAL:
+      type = AST::Literal::BOOL;
+      literal_value = "false";
+      lexer.skip_token ();
+      break;
+    default:
+      // error - cannot be a literal expr
+      add_error (Error (t->get_locus (),
+			"unexpected token %qs when parsing literal expression",
+			t->get_token_description ()));
+
+      // skip?
+      return nullptr;
+    }
+
+  // create literal based on stuff in switch
+  return std::unique_ptr<AST::LiteralExpr> (
+    new AST::LiteralExpr (std::move (literal_value), std::move (type),
+			  t->get_type_hint (), std::move (outer_attrs),
+			  t->get_locus ()));
+}
+
+// Parses a return expression (including any expression to return).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ReturnExpr>
+Parser<ManagedTokenSource>::parse_return_expr (AST::AttrVec outer_attrs,
+					       Location pratt_parsed_loc)
+{
+  Location locus = pratt_parsed_loc;
+  if (locus == Linemap::unknown_location ())
+    {
+      locus = lexer.peek_token ()->get_locus ();
+      skip_token (RETURN_TOK);
+    }
+
+  // parse expression to return, if it exists
+  ParseRestrictions restrictions;
+  restrictions.expr_can_be_null = true;
+  std::unique_ptr<AST::Expr> returned_expr
+    = parse_expr (AST::AttrVec (), restrictions);
+
+  return std::unique_ptr<AST::ReturnExpr> (
+    new AST::ReturnExpr (std::move (returned_expr), std::move (outer_attrs),
+			 locus));
+}
+
+/* Parses a break expression (including any label to break to AND any return
+ * expression). */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::BreakExpr>
+Parser<ManagedTokenSource>::parse_break_expr (AST::AttrVec outer_attrs,
+					      Location pratt_parsed_loc)
+{
+  Location locus = pratt_parsed_loc;
+  if (locus == Linemap::unknown_location ())
+    {
+      locus = lexer.peek_token ()->get_locus ();
+      skip_token (BREAK);
+    }
+
+  // parse label (lifetime) if it exists - create dummy first
+  AST::Lifetime label = AST::Lifetime::error ();
+  if (lexer.peek_token ()->get_id () == LIFETIME)
+    {
+      label = parse_lifetime ();
+    }
+
+  // parse break return expression if it exists
+  ParseRestrictions restrictions;
+  restrictions.expr_can_be_null = true;
+  std::unique_ptr<AST::Expr> return_expr
+    = parse_expr (AST::AttrVec (), restrictions);
+
+  return std::unique_ptr<AST::BreakExpr> (
+    new AST::BreakExpr (std::move (label), std::move (return_expr),
+			std::move (outer_attrs), locus));
+}
+
+// Parses a continue expression (including any label to continue from).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ContinueExpr>
+Parser<ManagedTokenSource>::parse_continue_expr (AST::AttrVec outer_attrs,
+						 Location pratt_parsed_loc)
+{
+  Location locus = pratt_parsed_loc;
+  if (locus == Linemap::unknown_location ())
+    {
+      locus = lexer.peek_token ()->get_locus ();
+      skip_token (CONTINUE);
+    }
+
+  // parse label (lifetime) if it exists - create dummy first
+  AST::Lifetime label = AST::Lifetime::error ();
+  if (lexer.peek_token ()->get_id () == LIFETIME)
+    {
+      label = parse_lifetime ();
+    }
+
+  return std::unique_ptr<AST::ContinueExpr> (
+    new AST::ContinueExpr (std::move (label), std::move (outer_attrs), locus));
+}
+
+// Parses a loop label used in loop expressions.
+template <typename ManagedTokenSource>
+AST::LoopLabel
+Parser<ManagedTokenSource>::parse_loop_label ()
+{
+  // parse lifetime - if doesn't exist, assume no label
+  const_TokenPtr t = lexer.peek_token ();
+  if (t->get_id () != LIFETIME)
+    {
+      // not necessarily an error
+      return AST::LoopLabel::error ();
+    }
+  /* FIXME: check for named lifetime requirement here? or check in semantic
+   * analysis phase? */
+  AST::Lifetime label = parse_lifetime ();
+
+  if (!skip_token (COLON))
+    {
+      // skip somewhere?
+      return AST::LoopLabel::error ();
+    }
+
+  return AST::LoopLabel (std::move (label), t->get_locus ());
+}
+
+/* Parses an if expression of any kind, including with else, else if, else if
+ * let, and neither. Note that any outer attributes will be ignored because if
+ * expressions don't support them. */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::IfExpr>
+Parser<ManagedTokenSource>::parse_if_expr (AST::AttrVec outer_attrs,
+					   Location pratt_parsed_loc)
+{
+  // TODO: make having outer attributes an error?
+  Location locus = pratt_parsed_loc;
+  if (locus == Linemap::unknown_location ())
+    {
+      locus = lexer.peek_token ()->get_locus ();
+      if (!skip_token (IF))
+	{
+	  skip_after_end_block ();
+	  return nullptr;
+	}
+    }
+
+  // detect accidental if let
+  if (lexer.peek_token ()->get_id () == LET)
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "if let expression probably exists, but is being parsed "
+		   "as an if expression. This may be a parser error");
+      add_error (std::move (error));
+
+      // skip somewhere?
+      return nullptr;
+    }
+
+  /* parse required condition expr - HACK to prevent struct expr from being
+   * parsed */
+  ParseRestrictions no_struct_expr;
+  no_struct_expr.can_be_struct_expr = false;
+  std::unique_ptr<AST::Expr> condition = parse_expr ({}, no_struct_expr);
+  if (condition == nullptr)
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "failed to parse condition expression in if expression");
+      add_error (std::move (error));
+
+      // skip somewhere?
+      return nullptr;
+    }
+
+  // parse required block expr
+  std::unique_ptr<AST::BlockExpr> if_body = parse_block_expr ();
+  if (if_body == nullptr)
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "failed to parse if body block expression in if expression");
+      add_error (std::move (error));
+
+      // skip somewhere?
+      return nullptr;
+    }
+
+  // branch to parse end or else (and then else, else if, or else if let)
+  if (lexer.peek_token ()->get_id () != ELSE)
+    {
+      // single selection - end of if expression
+      return std::unique_ptr<AST::IfExpr> (
+	new AST::IfExpr (std::move (condition), std::move (if_body),
+			 std::move (outer_attrs), locus));
+    }
+  else
+    {
+      // double or multiple selection - branch on end, else if, or else if let
+
+      // skip "else"
+      lexer.skip_token ();
+
+      // branch on whether next token is '{' or 'if'
+      const_TokenPtr t = lexer.peek_token ();
+      switch (t->get_id ())
+	{
+	  case LEFT_CURLY: {
+	    // double selection - else
+	    // parse else block expr (required)
+	    std::unique_ptr<AST::BlockExpr> else_body = parse_block_expr ();
+	    if (else_body == nullptr)
+	      {
+		Error error (lexer.peek_token ()->get_locus (),
+			     "failed to parse else body block expression in "
+			     "if expression");
+		add_error (std::move (error));
+
+		// skip somewhere?
+		return nullptr;
+	      }
+
+	    return std::unique_ptr<AST::IfExprConseqElse> (
+	      new AST::IfExprConseqElse (std::move (condition),
+					 std::move (if_body),
+					 std::move (else_body),
+					 std::move (outer_attrs), locus));
+	  }
+	  case IF: {
+	    // multiple selection - else if or else if let
+	    // branch on whether next token is 'let' or not
+	    if (lexer.peek_token (1)->get_id () == LET)
+	      {
+		// parse if let expr (required)
+		std::unique_ptr<AST::IfLetExpr> if_let_expr
+		  = parse_if_let_expr ();
+		if (if_let_expr == nullptr)
+		  {
+		    Error error (lexer.peek_token ()->get_locus (),
+				 "failed to parse (else) if let expression "
+				 "after if expression");
+		    add_error (std::move (error));
+
+		    // skip somewhere?
+		    return nullptr;
+		  }
+
+		return std::unique_ptr<AST::IfExprConseqIfLet> (
+		  new AST::IfExprConseqIfLet (std::move (condition),
+					      std::move (if_body),
+					      std::move (if_let_expr),
+					      std::move (outer_attrs), locus));
+	      }
+	    else
+	      {
+		// parse if expr (required)
+		std::unique_ptr<AST::IfExpr> if_expr = parse_if_expr ();
+		if (if_expr == nullptr)
+		  {
+		    Error error (lexer.peek_token ()->get_locus (),
+				 "failed to parse (else) if expression after "
+				 "if expression");
+		    add_error (std::move (error));
+
+		    // skip somewhere?
+		    return nullptr;
+		  }
+
+		return std::unique_ptr<AST::IfExprConseqIf> (
+		  new AST::IfExprConseqIf (std::move (condition),
+					   std::move (if_body),
+					   std::move (if_expr),
+					   std::move (outer_attrs), locus));
+	      }
+	  }
+	default:
+	  // error - invalid token
+	  add_error (Error (t->get_locus (),
+			    "unexpected token %qs after else in if expression",
+			    t->get_token_description ()));
+
+	  // skip somewhere?
+	  return nullptr;
+	}
+    }
+}
+
+/* Parses an if let expression of any kind, including with else, else if, else
+ * if let, and none. Note that any outer attributes will be ignored as if let
+ * expressions don't support them. */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::IfLetExpr>
+Parser<ManagedTokenSource>::parse_if_let_expr (AST::AttrVec outer_attrs,
+					       Location pratt_parsed_loc)
+{
+  // TODO: make having outer attributes an error?
+  Location locus = pratt_parsed_loc;
+  if (locus == Linemap::unknown_location ())
+    {
+      locus = lexer.peek_token ()->get_locus ();
+      if (!skip_token (IF))
+	{
+	  skip_after_end_block ();
+	  return nullptr;
+	}
+    }
+
+  // detect accidental if expr parsed as if let expr
+  if (lexer.peek_token ()->get_id () != LET)
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "if expression probably exists, but is being parsed as an "
+		   "if let expression. This may be a parser error");
+      add_error (std::move (error));
+
+      // skip somewhere?
+      return nullptr;
+    }
+  lexer.skip_token ();
+
+  // parse match arm patterns (which are required)
+  std::vector<std::unique_ptr<AST::Pattern>> match_arm_patterns
+    = parse_match_arm_patterns (EQUAL);
+  if (match_arm_patterns.empty ())
+    {
+      Error error (
+	lexer.peek_token ()->get_locus (),
+	"failed to parse any match arm patterns in if let expression");
+      add_error (std::move (error));
+
+      // skip somewhere?
+      return nullptr;
+    }
+
+  if (!skip_token (EQUAL))
+    {
+      // skip somewhere?
+      return nullptr;
+    }
+
+  // parse expression (required) - HACK to prevent struct expr being parsed
+  ParseRestrictions no_struct_expr;
+  no_struct_expr.can_be_struct_expr = false;
+  std::unique_ptr<AST::Expr> scrutinee_expr = parse_expr ({}, no_struct_expr);
+  if (scrutinee_expr == nullptr)
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "failed to parse scrutinee expression in if let expression");
+      add_error (std::move (error));
+
+      // skip somewhere?
+      return nullptr;
+    }
+  /* TODO: check for expression not being a struct expression or lazy boolean
+   * expression here? or actually probably in semantic analysis. */
+
+  // parse block expression (required)
+  std::unique_ptr<AST::BlockExpr> if_let_body = parse_block_expr ();
+  if (if_let_body == nullptr)
+    {
+      Error error (
+	lexer.peek_token ()->get_locus (),
+	"failed to parse if let body block expression in if let expression");
+      add_error (std::move (error));
+
+      // skip somewhere?
+      return nullptr;
+    }
+
+  // branch to parse end or else (and then else, else if, or else if let)
+  if (lexer.peek_token ()->get_id () != ELSE)
+    {
+      // single selection - end of if let expression
+      return std::unique_ptr<AST::IfLetExpr> (
+	new AST::IfLetExpr (std::move (match_arm_patterns),
+			    std::move (scrutinee_expr), std::move (if_let_body),
+			    std::move (outer_attrs), locus));
+    }
+  else
+    {
+      // double or multiple selection - branch on end, else if, or else if let
+
+      // skip "else"
+      lexer.skip_token ();
+
+      // branch on whether next token is '{' or 'if'
+      const_TokenPtr t = lexer.peek_token ();
+      switch (t->get_id ())
+	{
+	  case LEFT_CURLY: {
+	    // double selection - else
+	    // parse else block expr (required)
+	    std::unique_ptr<AST::BlockExpr> else_body = parse_block_expr ();
+	    if (else_body == nullptr)
+	      {
+		Error error (lexer.peek_token ()->get_locus (),
+			     "failed to parse else body block expression in "
+			     "if let expression");
+		add_error (std::move (error));
+
+		// skip somewhere?
+		return nullptr;
+	      }
+
+	    return std::unique_ptr<AST::IfLetExprConseqElse> (
+	      new AST::IfLetExprConseqElse (std::move (match_arm_patterns),
+					    std::move (scrutinee_expr),
+					    std::move (if_let_body),
+					    std::move (else_body),
+					    std::move (outer_attrs), locus));
+	  }
+	  case IF: {
+	    // multiple selection - else if or else if let
+	    // branch on whether next token is 'let' or not
+	    if (lexer.peek_token (1)->get_id () == LET)
+	      {
+		// parse if let expr (required)
+		std::unique_ptr<AST::IfLetExpr> if_let_expr
+		  = parse_if_let_expr ();
+		if (if_let_expr == nullptr)
+		  {
+		    Error error (lexer.peek_token ()->get_locus (),
+				 "failed to parse (else) if let expression "
+				 "after if let expression");
+		    add_error (std::move (error));
+
+		    // skip somewhere?
+		    return nullptr;
+		  }
+
+		return std::unique_ptr<AST::IfLetExprConseqIfLet> (
+		  new AST::IfLetExprConseqIfLet (
+		    std::move (match_arm_patterns), std::move (scrutinee_expr),
+		    std::move (if_let_body), std::move (if_let_expr),
+		    std::move (outer_attrs), locus));
+	      }
+	    else
+	      {
+		// parse if expr (required)
+		std::unique_ptr<AST::IfExpr> if_expr = parse_if_expr ();
+		if (if_expr == nullptr)
+		  {
+		    Error error (lexer.peek_token ()->get_locus (),
+				 "failed to parse (else) if expression after "
+				 "if let expression");
+		    add_error (std::move (error));
+
+		    // skip somewhere?
+		    return nullptr;
+		  }
+
+		return std::unique_ptr<AST::IfLetExprConseqIf> (
+		  new AST::IfLetExprConseqIf (std::move (match_arm_patterns),
+					      std::move (scrutinee_expr),
+					      std::move (if_let_body),
+					      std::move (if_expr),
+					      std::move (outer_attrs), locus));
+	      }
+	  }
+	default:
+	  // error - invalid token
+	  add_error (
+	    Error (t->get_locus (),
+		   "unexpected token %qs after else in if let expression",
+		   t->get_token_description ()));
+
+	  // skip somewhere?
+	  return nullptr;
+	}
+    }
+}
+
+/* TODO: possibly decide on different method of handling label (i.e. not
+ * parameter) */
+
+/* Parses a "loop" infinite loop expression. Label is not parsed and should be
+ * parsed via parse_labelled_loop_expr, which would call this. */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::LoopExpr>
+Parser<ManagedTokenSource>::parse_loop_expr (AST::AttrVec outer_attrs,
+					     AST::LoopLabel label,
+					     Location pratt_parsed_loc)
+{
+  Location locus = pratt_parsed_loc;
+  if (locus == Linemap::unknown_location ())
+    {
+      if (label.is_error ())
+	locus = lexer.peek_token ()->get_locus ();
+      else
+	locus = label.get_locus ();
+
+      if (!skip_token (LOOP))
+	{
+	  skip_after_end_block ();
+	  return nullptr;
+	}
+    }
+  else
+    {
+      if (!label.is_error ())
+	locus = label.get_locus ();
+    }
+
+  // parse loop body, which is required
+  std::unique_ptr<AST::BlockExpr> loop_body = parse_block_expr ();
+  if (loop_body == nullptr)
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "could not parse loop body in (infinite) loop expression");
+      add_error (std::move (error));
+
+      return nullptr;
+    }
+
+  return std::unique_ptr<AST::LoopExpr> (
+    new AST::LoopExpr (std::move (loop_body), locus, std::move (label),
+		       std::move (outer_attrs)));
+}
+
+/* Parses a "while" loop expression. Label is not parsed and should be parsed
+ * via parse_labelled_loop_expr, which would call this. */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::WhileLoopExpr>
+Parser<ManagedTokenSource>::parse_while_loop_expr (AST::AttrVec outer_attrs,
+						   AST::LoopLabel label,
+						   Location pratt_parsed_loc)
+{
+  Location locus = pratt_parsed_loc;
+  if (locus == Linemap::unknown_location ())
+    {
+      if (label.is_error ())
+	locus = lexer.peek_token ()->get_locus ();
+      else
+	locus = label.get_locus ();
+
+      if (!skip_token (WHILE))
+	{
+	  skip_after_end_block ();
+	  return nullptr;
+	}
+    }
+  else
+    {
+      if (!label.is_error ())
+	locus = label.get_locus ();
+    }
+
+  // ensure it isn't a while let loop
+  if (lexer.peek_token ()->get_id () == LET)
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "appears to be while let loop but is being parsed by "
+		   "while loop - this may be a compiler issue");
+      add_error (std::move (error));
+
+      // skip somewhere?
+      return nullptr;
+    }
+
+  // parse loop predicate (required) with HACK to prevent struct expr parsing
+  ParseRestrictions no_struct_expr;
+  no_struct_expr.can_be_struct_expr = false;
+  std::unique_ptr<AST::Expr> predicate = parse_expr ({}, no_struct_expr);
+  if (predicate == nullptr)
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "failed to parse predicate expression in while loop");
+      add_error (std::move (error));
+
+      // skip somewhere?
+      return nullptr;
+    }
+  /* TODO: check that it isn't struct expression here? actually, probably in
+   * semantic analysis */
+
+  // parse loop body (required)
+  std::unique_ptr<AST::BlockExpr> body = parse_block_expr ();
+  if (body == nullptr)
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "failed to parse loop body block expression in while loop");
+      add_error (std::move (error));
+
+      // skip somewhere
+      return nullptr;
+    }
+
+  return std::unique_ptr<AST::WhileLoopExpr> (
+    new AST::WhileLoopExpr (std::move (predicate), std::move (body), locus,
+			    std::move (label), std::move (outer_attrs)));
+}
+
+/* Parses a "while let" loop expression. Label is not parsed and should be
+ * parsed via parse_labelled_loop_expr, which would call this. */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::WhileLetLoopExpr>
+Parser<ManagedTokenSource>::parse_while_let_loop_expr (AST::AttrVec outer_attrs,
+						       AST::LoopLabel label)
+{
+  Location locus = Linemap::unknown_location ();
+  if (label.is_error ())
+    locus = lexer.peek_token ()->get_locus ();
+  else
+    locus = label.get_locus ();
+  skip_token (WHILE);
+
+  /* check for possible accidental recognition of a while loop as a while let
+   * loop */
+  if (lexer.peek_token ()->get_id () != LET)
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "appears to be a while loop but is being parsed by "
+		   "while let loop - this may be a compiler issue");
+      add_error (std::move (error));
+
+      // skip somewhere
+      return nullptr;
+    }
+  // as this token is definitely let now, save the computation of comparison
+  lexer.skip_token ();
+
+  // parse predicate patterns
+  std::vector<std::unique_ptr<AST::Pattern>> predicate_patterns
+    = parse_match_arm_patterns (EQUAL);
+  // TODO: have to ensure that there is at least 1 pattern?
+
+  if (!skip_token (EQUAL))
+    {
+      // skip somewhere?
+      return nullptr;
+    }
+
+  /* parse predicate expression, which is required (and HACK to prevent struct
+   * expr) */
+  ParseRestrictions no_struct_expr;
+  no_struct_expr.can_be_struct_expr = false;
+  std::unique_ptr<AST::Expr> predicate_expr = parse_expr ({}, no_struct_expr);
+  if (predicate_expr == nullptr)
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "failed to parse predicate expression in while let loop");
+      add_error (std::move (error));
+
+      // skip somewhere?
+      return nullptr;
+    }
+  /* TODO: ensure that struct expression is not parsed? Actually, probably in
+   * semantic analysis. */
+
+  // parse loop body, which is required
+  std::unique_ptr<AST::BlockExpr> body = parse_block_expr ();
+  if (body == nullptr)
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "failed to parse block expr (loop body) of while let loop");
+      add_error (std::move (error));
+
+      // skip somewhere?
+      return nullptr;
+    }
+
+  return std::unique_ptr<AST::WhileLetLoopExpr> (new AST::WhileLetLoopExpr (
+    std::move (predicate_patterns), std::move (predicate_expr),
+    std::move (body), locus, std::move (label), std::move (outer_attrs)));
+}
+
+/* Parses a "for" iterative loop. Label is not parsed and should be parsed via
+ * parse_labelled_loop_expr, which would call this. */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ForLoopExpr>
+Parser<ManagedTokenSource>::parse_for_loop_expr (AST::AttrVec outer_attrs,
+						 AST::LoopLabel label)
+{
+  Location locus = Linemap::unknown_location ();
+  if (label.is_error ())
+    locus = lexer.peek_token ()->get_locus ();
+  else
+    locus = label.get_locus ();
+  skip_token (FOR);
+
+  // parse pattern, which is required
+  std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
+  if (pattern == nullptr)
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "failed to parse iterator pattern in for loop");
+      add_error (std::move (error));
+
+      // skip somewhere?
+      return nullptr;
+    }
+
+  if (!skip_token (IN))
+    {
+      // skip somewhere?
+      return nullptr;
+    }
+
+  /* parse iterator expression, which is required - also HACK to prevent
+   * struct expr */
+  ParseRestrictions no_struct_expr;
+  no_struct_expr.can_be_struct_expr = false;
+  std::unique_ptr<AST::Expr> expr = parse_expr ({}, no_struct_expr);
+  if (expr == nullptr)
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "failed to parse iterator expression in for loop");
+      add_error (std::move (error));
+
+      // skip somewhere?
+      return nullptr;
+    }
+  // TODO: check to ensure this isn't struct expr? Or in semantic analysis.
+
+  // parse loop body, which is required
+  std::unique_ptr<AST::BlockExpr> body = parse_block_expr ();
+  if (body == nullptr)
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "failed to parse loop body block expression in for loop");
+      add_error (std::move (error));
+
+      // skip somewhere?
+      return nullptr;
+    }
+
+  return std::unique_ptr<AST::ForLoopExpr> (
+    new AST::ForLoopExpr (std::move (pattern), std::move (expr),
+			  std::move (body), locus, std::move (label),
+			  std::move (outer_attrs)));
+}
+
+// Parses a loop expression with label (any kind of loop - disambiguates).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::BaseLoopExpr>
+Parser<ManagedTokenSource>::parse_labelled_loop_expr (AST::AttrVec outer_attrs)
+{
+  /* TODO: decide whether it should not work if there is no label, or parse it
+   * with no label at the moment, I will make it not work with no label
+   * because that's the implication. */
+
+  if (lexer.peek_token ()->get_id () != LIFETIME)
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "expected lifetime in labelled loop expr (to parse loop "
+		   "label) - found %qs",
+		   lexer.peek_token ()->get_token_description ());
+      add_error (std::move (error));
+
+      // skip?
+      return nullptr;
+    }
+
+  // parse loop label (required)
+  AST::LoopLabel label = parse_loop_label ();
+  if (label.is_error ())
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "failed to parse loop label in labelled loop expr");
+      add_error (std::move (error));
+
+      // skip?
+      return nullptr;
+    }
+
+  // branch on next token
+  const_TokenPtr t = lexer.peek_token ();
+  switch (t->get_id ())
+    {
+    case LOOP:
+      return parse_loop_expr (std::move (outer_attrs), std::move (label));
+    case FOR:
+      return parse_for_loop_expr (std::move (outer_attrs), std::move (label));
+    case WHILE:
+      // further disambiguate into while vs while let
+      if (lexer.peek_token (1)->get_id () == LET)
+	{
+	  return parse_while_let_loop_expr (std::move (outer_attrs),
+					    std::move (label));
+	}
+      else
+	{
+	  return parse_while_loop_expr (std::move (outer_attrs),
+					std::move (label));
+	}
+    default:
+      // error
+      add_error (Error (t->get_locus (),
+			"unexpected token %qs when parsing labelled loop",
+			t->get_token_description ()));
+
+      // skip?
+      return nullptr;
+    }
+}
+
+// Parses a match expression.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::MatchExpr>
+Parser<ManagedTokenSource>::parse_match_expr (AST::AttrVec outer_attrs,
+					      Location pratt_parsed_loc)
+{
+  Location locus = pratt_parsed_loc;
+  if (locus == Linemap::unknown_location ())
+    {
+      locus = lexer.peek_token ()->get_locus ();
+      skip_token (MATCH_TOK);
+    }
+
+  /* parse scrutinee expression, which is required (and HACK to prevent struct
+   * expr) */
+  ParseRestrictions no_struct_expr;
+  no_struct_expr.can_be_struct_expr = false;
+  std::unique_ptr<AST::Expr> scrutinee = parse_expr ({}, no_struct_expr);
+  if (scrutinee == nullptr)
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "failed to parse scrutinee expression in match expression");
+      add_error (std::move (error));
+
+      // skip somewhere?
+      return nullptr;
+    }
+  /* TODO: check for scrutinee expr not being struct expr? or do so in
+   * semantic analysis */
+
+  if (!skip_token (LEFT_CURLY))
+    {
+      // skip somewhere?
+      return nullptr;
+    }
+
+  // parse inner attributes (if they exist)
+  AST::AttrVec inner_attrs = parse_inner_attributes ();
+
+  // parse match arms (if they exist)
+  // std::vector<std::unique_ptr<AST::MatchCase> > match_arms;
+  std::vector<AST::MatchCase> match_arms;
+
+  // parse match cases
+  while (lexer.peek_token ()->get_id () != RIGHT_CURLY)
+    {
+      // parse match arm itself, which is required
+      AST::MatchArm arm = parse_match_arm ();
+      if (arm.is_error ())
+	{
+	  // TODO is this worth throwing everything away?
+	  Error error (lexer.peek_token ()->get_locus (),
+		       "failed to parse match arm in match arms");
+	  add_error (std::move (error));
+
+	  return nullptr;
+	}
+
+      if (!skip_token (MATCH_ARROW))
+	{
+	  // skip after somewhere?
+	  // TODO is returning here a good idea? or is break better?
+	  return nullptr;
+	}
+
+      ParseRestrictions restrictions;
+      restrictions.expr_can_be_stmt = true;
+      restrictions.consume_semi = false;
+
+      std::unique_ptr<AST::ExprStmt> expr = parse_expr_stmt ({}, restrictions);
+      if (expr == nullptr)
+	{
+	  Error error (lexer.peek_token ()->get_locus (),
+		       "failed to parse expr in match arm in match expr");
+	  add_error (std::move (error));
+
+	  // skip somewhere?
+	  return nullptr;
+	}
+      bool is_expr_without_block
+	= expr->get_type () == AST::ExprStmt::ExprStmtType::WITHOUT_BLOCK;
+
+      // construct match case expr and add to cases
+      switch (expr->get_type ())
+	{
+	  case AST::ExprStmt::ExprStmtType::WITH_BLOCK: {
+	    AST::ExprStmtWithBlock *cast
+	      = static_cast<AST::ExprStmtWithBlock *> (expr.get ());
+	    std::unique_ptr<AST::Expr> e = cast->get_expr ()->clone_expr ();
+	    match_arms.push_back (
+	      AST::MatchCase (std::move (arm), std::move (e)));
+	  }
+	  break;
+
+	  case AST::ExprStmt::ExprStmtType::WITHOUT_BLOCK: {
+	    AST::ExprStmtWithoutBlock *cast
+	      = static_cast<AST::ExprStmtWithoutBlock *> (expr.get ());
+	    std::unique_ptr<AST::Expr> e = cast->get_expr ()->clone_expr ();
+	    match_arms.push_back (
+	      AST::MatchCase (std::move (arm), std::move (e)));
+	  }
+	  break;
+	}
+
+      // handle comma presence
+      if (lexer.peek_token ()->get_id () != COMMA)
+	{
+	  if (!is_expr_without_block)
+	    {
+	      // allowed even if not final case
+	      continue;
+	    }
+	  else if (is_expr_without_block
+		   && lexer.peek_token ()->get_id () != RIGHT_CURLY)
+	    {
+	      // not allowed if not final case
+	      Error error (lexer.peek_token ()->get_locus (),
+			   "exprwithoutblock requires comma after match case "
+			   "expression in match arm (if not final case)");
+	      add_error (std::move (error));
+
+	      return nullptr;
+	    }
+	  else
+	    {
+	      // otherwise, must be final case, so fine
+	      break;
+	    }
+	}
+      lexer.skip_token ();
+    }
+
+  if (!skip_token (RIGHT_CURLY))
+    {
+      // skip somewhere?
+      return nullptr;
+    }
+
+  match_arms.shrink_to_fit ();
+
+  return std::unique_ptr<AST::MatchExpr> (
+    new AST::MatchExpr (std::move (scrutinee), std::move (match_arms),
+			std::move (inner_attrs), std::move (outer_attrs),
+			locus));
+}
+
+// Parses the "pattern" part of the match arm (the 'case x:' equivalent).
+template <typename ManagedTokenSource>
+AST::MatchArm
+Parser<ManagedTokenSource>::parse_match_arm ()
+{
+  // parse optional outer attributes
+  AST::AttrVec outer_attrs = parse_outer_attributes ();
+
+  // DEBUG
+  rust_debug ("about to start parsing match arm patterns");
+
+  // break early if find right curly
+  if (lexer.peek_token ()->get_id () == RIGHT_CURLY)
+    {
+      // not an error
+      return AST::MatchArm::create_error ();
+    }
+
+  // parse match arm patterns - at least 1 is required
+  std::vector<std::unique_ptr<AST::Pattern>> match_arm_patterns
+    = parse_match_arm_patterns (RIGHT_CURLY);
+  if (match_arm_patterns.empty ())
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "failed to parse any patterns in match arm");
+      add_error (std::move (error));
+
+      // skip somewhere?
+      return AST::MatchArm::create_error ();
+    }
+
+  // DEBUG
+  rust_debug ("successfully parsed match arm patterns");
+
+  // parse match arm guard expr if it exists
+  std::unique_ptr<AST::Expr> guard_expr = nullptr;
+  if (lexer.peek_token ()->get_id () == IF)
+    {
+      lexer.skip_token ();
+
+      guard_expr = parse_expr ();
+      if (guard_expr == nullptr)
+	{
+	  Error error (lexer.peek_token ()->get_locus (),
+		       "failed to parse guard expression in match arm");
+	  add_error (std::move (error));
+
+	  // skip somewhere?
+	  return AST::MatchArm::create_error ();
+	}
+    }
+
+  // DEBUG
+  rust_debug ("successfully parsed match arm");
+
+  return AST::MatchArm (std::move (match_arm_patterns),
+			lexer.peek_token ()->get_locus (),
+			std::move (guard_expr), std::move (outer_attrs));
+}
+
+/* Parses the patterns used in a match arm. End token id is the id of the
+ * token that would exist after the patterns are done (e.g. '}' for match
+ * expr, '=' for if let and while let). */
+template <typename ManagedTokenSource>
+std::vector<std::unique_ptr<AST::Pattern>>
+Parser<ManagedTokenSource>::parse_match_arm_patterns (TokenId end_token_id)
+{
+  // skip optional leading '|'
+  if (lexer.peek_token ()->get_id () == PIPE)
+    lexer.skip_token ();
+  /* TODO: do I even need to store the result of this? can't be used.
+   * If semantically different, I need a wrapped "match arm patterns" object
+   * for this. */
+
+  std::vector<std::unique_ptr<AST::Pattern>> patterns;
+
+  // quick break out if end_token_id
+  if (lexer.peek_token ()->get_id () == end_token_id)
+    return patterns;
+
+  // parse required pattern - if doesn't exist, return empty
+  std::unique_ptr<AST::Pattern> initial_pattern = parse_pattern ();
+  if (initial_pattern == nullptr)
+    {
+      // FIXME: should this be an error?
+      return patterns;
+    }
+  patterns.push_back (std::move (initial_pattern));
+
+  // DEBUG
+  rust_debug ("successfully parsed initial match arm pattern");
+
+  // parse new patterns as long as next char is '|'
+  const_TokenPtr t = lexer.peek_token ();
+  while (t->get_id () == PIPE)
+    {
+      // skip pipe token
+      lexer.skip_token ();
+
+      // break if hit end token id
+      if (lexer.peek_token ()->get_id () == end_token_id)
+	break;
+
+      // parse pattern
+      std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
+      if (pattern == nullptr)
+	{
+	  // this is an error
+	  Error error (lexer.peek_token ()->get_locus (),
+		       "failed to parse pattern in match arm patterns");
+	  add_error (std::move (error));
+
+	  // skip somewhere?
+	  return {};
+	}
+
+      patterns.push_back (std::move (pattern));
+
+      t = lexer.peek_token ();
+    }
+
+  patterns.shrink_to_fit ();
+
+  return patterns;
+}
+
+// Parses an async block expression.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::AsyncBlockExpr>
+Parser<ManagedTokenSource>::parse_async_block_expr (AST::AttrVec outer_attrs)
+{
+  Location locus = lexer.peek_token ()->get_locus ();
+  skip_token (ASYNC);
+
+  // detect optional move token
+  bool has_move = false;
+  if (lexer.peek_token ()->get_id () == MOVE)
+    {
+      lexer.skip_token ();
+      has_move = true;
+    }
+
+  // parse block expression (required)
+  std::unique_ptr<AST::BlockExpr> block_expr = parse_block_expr ();
+  if (block_expr == nullptr)
+    {
+      Error error (
+	lexer.peek_token ()->get_locus (),
+	"failed to parse block expression of async block expression");
+      add_error (std::move (error));
+
+      // skip somewhere?
+      return nullptr;
+    }
+
+  return std::unique_ptr<AST::AsyncBlockExpr> (
+    new AST::AsyncBlockExpr (std::move (block_expr), has_move,
+			     std::move (outer_attrs), locus));
+}
+
+// Parses an unsafe block expression.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::UnsafeBlockExpr>
+Parser<ManagedTokenSource>::parse_unsafe_block_expr (AST::AttrVec outer_attrs,
+						     Location pratt_parsed_loc)
+{
+  Location locus = pratt_parsed_loc;
+  if (locus == Linemap::unknown_location ())
+    {
+      locus = lexer.peek_token ()->get_locus ();
+      skip_token (UNSAFE);
+    }
+
+  // parse block expression (required)
+  std::unique_ptr<AST::BlockExpr> block_expr = parse_block_expr ();
+  if (block_expr == nullptr)
+    {
+      Error error (
+	lexer.peek_token ()->get_locus (),
+	"failed to parse block expression of unsafe block expression");
+      add_error (std::move (error));
+
+      // skip somewhere?
+      return nullptr;
+    }
+
+  return std::unique_ptr<AST::UnsafeBlockExpr> (
+    new AST::UnsafeBlockExpr (std::move (block_expr), std::move (outer_attrs),
+			      locus));
+}
+
+// Parses an array definition expression.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ArrayExpr>
+Parser<ManagedTokenSource>::parse_array_expr (AST::AttrVec outer_attrs,
+					      Location pratt_parsed_loc)
+{
+  Location locus = pratt_parsed_loc;
+  if (locus == Linemap::unknown_location ())
+    {
+      locus = lexer.peek_token ()->get_locus ();
+      skip_token (LEFT_SQUARE);
+    }
+
+  // parse optional inner attributes
+  AST::AttrVec inner_attrs = parse_inner_attributes ();
+
+  // parse the "array elements" section, which is optional
+  if (lexer.peek_token ()->get_id () == RIGHT_SQUARE)
+    {
+      // no array elements
+      lexer.skip_token ();
+
+      std::vector<std::unique_ptr<AST::Expr>> exprs;
+      auto array_elems
+	= Rust::make_unique<AST::ArrayElemsValues> (std::move (exprs), locus);
+      return Rust::make_unique<AST::ArrayExpr> (std::move (array_elems),
+						std::move (inner_attrs),
+						std::move (outer_attrs), locus);
+    }
+  else
+    {
+      // should have array elements
+      // parse initial expression, which is required for either
+      std::unique_ptr<AST::Expr> initial_expr = parse_expr ();
+      if (initial_expr == nullptr)
+	{
+	  Error error (lexer.peek_token ()->get_locus (),
+		       "could not parse expression in array expression "
+		       "(even though arrayelems seems to be present)");
+	  add_error (std::move (error));
+
+	  // skip somewhere?
+	  return nullptr;
+	}
+
+      if (lexer.peek_token ()->get_id () == SEMICOLON)
+	{
+	  // copy array elems
+	  lexer.skip_token ();
+
+	  // parse copy amount expression (required)
+	  std::unique_ptr<AST::Expr> copy_amount = parse_expr ();
+	  if (copy_amount == nullptr)
+	    {
+	      Error error (lexer.peek_token ()->get_locus (),
+			   "could not parse copy amount expression in array "
+			   "expression (arrayelems)");
+	      add_error (std::move (error));
+
+	      // skip somewhere?
+	      return nullptr;
+	    }
+
+	  skip_token (RIGHT_SQUARE);
+
+	  std::unique_ptr<AST::ArrayElemsCopied> copied_array_elems (
+	    new AST::ArrayElemsCopied (std::move (initial_expr),
+				       std::move (copy_amount), locus));
+	  return std::unique_ptr<AST::ArrayExpr> (
+	    new AST::ArrayExpr (std::move (copied_array_elems),
+				std::move (inner_attrs),
+				std::move (outer_attrs), locus));
+	}
+      else if (lexer.peek_token ()->get_id () == RIGHT_SQUARE)
+	{
+	  // single-element array expression
+	  std::vector<std::unique_ptr<AST::Expr>> exprs;
+	  exprs.reserve (1);
+	  exprs.push_back (std::move (initial_expr));
+	  exprs.shrink_to_fit ();
+
+	  skip_token (RIGHT_SQUARE);
+
+	  std::unique_ptr<AST::ArrayElemsValues> array_elems (
+	    new AST::ArrayElemsValues (std::move (exprs), locus));
+	  return std::unique_ptr<AST::ArrayExpr> (
+	    new AST::ArrayExpr (std::move (array_elems),
+				std::move (inner_attrs),
+				std::move (outer_attrs), locus));
+	}
+      else if (lexer.peek_token ()->get_id () == COMMA)
+	{
+	  // multi-element array expression (or trailing comma)
+	  std::vector<std::unique_ptr<AST::Expr>> exprs;
+	  exprs.push_back (std::move (initial_expr));
+
+	  const_TokenPtr t = lexer.peek_token ();
+	  while (t->get_id () == COMMA)
+	    {
+	      lexer.skip_token ();
+
+	      // quick break if right square bracket
+	      if (lexer.peek_token ()->get_id () == RIGHT_SQUARE)
+		break;
+
+	      // parse expression (required)
+	      std::unique_ptr<AST::Expr> expr = parse_expr ();
+	      if (expr == nullptr)
+		{
+		  Error error (lexer.peek_token ()->get_locus (),
+			       "failed to parse element in array expression");
+		  add_error (std::move (error));
+
+		  // skip somewhere?
+		  return nullptr;
+		}
+	      exprs.push_back (std::move (expr));
+
+	      t = lexer.peek_token ();
+	    }
+
+	  skip_token (RIGHT_SQUARE);
+
+	  exprs.shrink_to_fit ();
+
+	  std::unique_ptr<AST::ArrayElemsValues> array_elems (
+	    new AST::ArrayElemsValues (std::move (exprs), locus));
+	  return std::unique_ptr<AST::ArrayExpr> (
+	    new AST::ArrayExpr (std::move (array_elems),
+				std::move (inner_attrs),
+				std::move (outer_attrs), locus));
+	}
+      else
+	{
+	  // error
+	  Error error (lexer.peek_token ()->get_locus (),
+		       "unexpected token %qs in array expression (arrayelems)",
+		       lexer.peek_token ()->get_token_description ());
+	  add_error (std::move (error));
+
+	  // skip somewhere?
+	  return nullptr;
+	}
+    }
+}
+
+// Parses a single parameter used in a closure definition.
+template <typename ManagedTokenSource>
+AST::ClosureParam
+Parser<ManagedTokenSource>::parse_closure_param ()
+{
+  AST::AttrVec outer_attrs = parse_outer_attributes ();
+
+  // parse pattern (which is required)
+  std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
+  if (pattern == nullptr)
+    {
+      // not necessarily an error
+      return AST::ClosureParam::create_error ();
+    }
+
+  // parse optional type of param
+  std::unique_ptr<AST::Type> type = nullptr;
+  if (lexer.peek_token ()->get_id () == COLON)
+    {
+      lexer.skip_token ();
+
+      // parse type, which is now required
+      type = parse_type ();
+      if (type == nullptr)
+	{
+	  Error error (lexer.peek_token ()->get_locus (),
+		       "failed to parse type in closure parameter");
+	  add_error (std::move (error));
+
+	  // skip somewhere?
+	  return AST::ClosureParam::create_error ();
+	}
+    }
+
+  return AST::ClosureParam (std::move (pattern), pattern->get_locus (),
+			    std::move (type), std::move (outer_attrs));
+}
+
+// Parses a grouped or tuple expression (disambiguates).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ExprWithoutBlock>
+Parser<ManagedTokenSource>::parse_grouped_or_tuple_expr (
+  AST::AttrVec outer_attrs, Location pratt_parsed_loc)
+{
+  // adjustment to allow Pratt parsing to reuse function without copy-paste
+  Location locus = pratt_parsed_loc;
+  if (locus == Linemap::unknown_location ())
+    {
+      locus = lexer.peek_token ()->get_locus ();
+      skip_token (LEFT_PAREN);
+    }
+
+  // parse optional inner attributes
+  AST::AttrVec inner_attrs = parse_inner_attributes ();
+
+  if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
+    {
+      // must be empty tuple
+      lexer.skip_token ();
+
+      // create tuple with empty tuple elems
+      return std::unique_ptr<AST::TupleExpr> (
+	new AST::TupleExpr (std::vector<std::unique_ptr<AST::Expr>> (),
+			    std::move (inner_attrs), std::move (outer_attrs),
+			    locus));
+    }
+
+  // parse first expression (required)
+  std::unique_ptr<AST::Expr> first_expr = parse_expr ();
+  if (first_expr == nullptr)
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "failed to parse expression in grouped or tuple expression");
+      add_error (std::move (error));
+
+      // skip after somewhere?
+      return nullptr;
+    }
+
+  // detect whether grouped expression with right parentheses as next token
+  if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
+    {
+      // must be grouped expr
+      lexer.skip_token ();
+
+      // create grouped expr
+      return std::unique_ptr<AST::GroupedExpr> (
+	new AST::GroupedExpr (std::move (first_expr), std::move (inner_attrs),
+			      std::move (outer_attrs), locus));
+    }
+  else if (lexer.peek_token ()->get_id () == COMMA)
+    {
+      // tuple expr
+      std::vector<std::unique_ptr<AST::Expr>> exprs;
+      exprs.push_back (std::move (first_expr));
+
+      // parse potential other tuple exprs
+      const_TokenPtr t = lexer.peek_token ();
+      while (t->get_id () == COMMA)
+	{
+	  lexer.skip_token ();
+
+	  // break out if right paren
+	  if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
+	    break;
+
+	  // parse expr, which is now required
+	  std::unique_ptr<AST::Expr> expr = parse_expr ();
+	  if (expr == nullptr)
+	    {
+	      Error error (lexer.peek_token ()->get_locus (),
+			   "failed to parse expr in tuple expr");
+	      add_error (std::move (error));
+
+	      // skip somewhere?
+	      return nullptr;
+	    }
+	  exprs.push_back (std::move (expr));
+
+	  t = lexer.peek_token ();
+	}
+
+      // skip right paren
+      skip_token (RIGHT_PAREN);
+
+      return std::unique_ptr<AST::TupleExpr> (
+	new AST::TupleExpr (std::move (exprs), std::move (inner_attrs),
+			    std::move (outer_attrs), locus));
+    }
+  else
+    {
+      // error
+      const_TokenPtr t = lexer.peek_token ();
+      Error error (t->get_locus (),
+		   "unexpected token %qs in grouped or tuple expression "
+		   "(parenthesised expression) - expected %<)%> for grouped "
+		   "expr and %<,%> for tuple expr",
+		   t->get_token_description ());
+      add_error (std::move (error));
+
+      // skip somewhere?
+      return nullptr;
+    }
+}
+
+// Parses a type (will further disambiguate any type).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::Type>
+Parser<ManagedTokenSource>::parse_type (bool save_errors)
+{
+  /* rules for all types:
+   * NeverType:               '!'
+   * SliceType:               '[' Type ']'
+   * InferredType:            '_'
+   * MacroInvocation:         SimplePath '!' DelimTokenTree
+   * ParenthesisedType:       '(' Type ')'
+   * ImplTraitType:           'impl' TypeParamBounds
+   *  TypeParamBounds (not type)  TypeParamBound ( '+' TypeParamBound )* '+'?
+   *  TypeParamBound          Lifetime | TraitBound
+   * ImplTraitTypeOneBound:   'impl' TraitBound
+   * TraitObjectType:         'dyn'? TypeParamBounds
+   * TraitObjectTypeOneBound: 'dyn'? TraitBound
+   *  TraitBound              '?'? ForLifetimes? TypePath | '(' '?'?
+   * ForLifetimes? TypePath ')' BareFunctionType:        ForLifetimes?
+   * FunctionQualifiers 'fn' etc. ForLifetimes (not type) 'for' '<'
+   * LifetimeParams '>' FunctionQualifiers      ( 'async' | 'const' )?
+   * 'unsafe'?
+   * ('extern' abi?)? QualifiedPathInType:     '<' Type ( 'as' TypePath )? '>'
+   * (
+   * '::' TypePathSegment )+ TypePath:                '::'? TypePathSegment (
+   * '::' TypePathSegment)* ArrayType:               '[' Type ';' Expr ']'
+   * ReferenceType:           '&' Lifetime? 'mut'? TypeNoBounds
+   * RawPointerType:          '*' ( 'mut' | 'const' ) TypeNoBounds
+   * TupleType:               '(' Type etc. - regular tuple stuff. Also
+   * regular tuple vs parenthesised precedence
+   *
+   * Disambiguate between macro and type path via type path being parsed, and
+   * then if '!' found, convert type path to simple path for macro. Usual
+   * disambiguation for tuple vs parenthesised. For ImplTraitType and
+   * TraitObjectType individual disambiguations, they seem more like "special
+   * cases", so probably just try to parse the more general ImplTraitType or
+   * TraitObjectType and return OneBound versions if they satisfy those
+   * criteria. */
+
+  const_TokenPtr t = lexer.peek_token ();
+  switch (t->get_id ())
+    {
+    case EXCLAM:
+      // never type - can't be macro as no path beforehand
+      lexer.skip_token ();
+      return std::unique_ptr<AST::NeverType> (
+	new AST::NeverType (t->get_locus ()));
+    case LEFT_SQUARE:
+      // slice type or array type - requires further disambiguation
+      return parse_slice_or_array_type ();
+      case LEFT_ANGLE: {
+	// qualified path in type
+	AST::QualifiedPathInType path = parse_qualified_path_in_type ();
+	if (path.is_error ())
+	  {
+	    if (save_errors)
+	      {
+		Error error (t->get_locus (),
+			     "failed to parse qualified path in type");
+		add_error (std::move (error));
+	      }
+
+	    return nullptr;
+	  }
+	return std::unique_ptr<AST::QualifiedPathInType> (
+	  new AST::QualifiedPathInType (std::move (path)));
+      }
+    case UNDERSCORE:
+      // inferred type
+      lexer.skip_token ();
+      return std::unique_ptr<AST::InferredType> (
+	new AST::InferredType (t->get_locus ()));
+    case ASTERISK:
+      // raw pointer type
+      return parse_raw_pointer_type ();
+    case AMP: // does this also include AMP_AMP?
+      // reference type
+      return parse_reference_type ();
+      case LIFETIME: {
+	/* probably a lifetime bound, so probably type param bounds in
+	 * TraitObjectType */
+	std::vector<std::unique_ptr<AST::TypeParamBound>> bounds
+	  = parse_type_param_bounds ();
+
+	return std::unique_ptr<AST::TraitObjectType> (
+	  new AST::TraitObjectType (std::move (bounds), t->get_locus (),
+				    false));
+      }
+    case IDENTIFIER:
+    case SUPER:
+    case SELF:
+    case SELF_ALIAS:
+    case CRATE:
+    case DOLLAR_SIGN:
+      case SCOPE_RESOLUTION: {
+	// macro invocation or type path - requires further disambiguation.
+	/* for parsing path component of each rule, perhaps parse it as a
+	 * typepath and attempt conversion to simplepath if a trailing '!' is
+	 * found */
+	/* Type path also includes TraitObjectTypeOneBound BUT if it starts
+	 * with it, it is exactly the same as a TypePath syntactically, so
+	 * this is a syntactical ambiguity. As such, the parser will parse it
+	 * as a TypePath. This, however, does not prevent TraitObjectType from
+	 * starting with a typepath. */
+
+	// parse path as type path
+	AST::TypePath path = parse_type_path ();
+	if (path.is_error ())
+	  {
+	    if (save_errors)
+	      {
+		Error error (t->get_locus (),
+			     "failed to parse path as first component of type");
+		add_error (std::move (error));
+	      }
+
+	    return nullptr;
+	  }
+	Location locus = path.get_locus ();
+
+	// branch on next token
+	t = lexer.peek_token ();
+	switch (t->get_id ())
+	  {
+	    case EXCLAM: {
+	      // macro invocation
+	      // convert to simple path
+	      AST::SimplePath macro_path = path.as_simple_path ();
+	      if (macro_path.is_empty ())
+		{
+		  if (save_errors)
+		    {
+		      Error error (t->get_locus (),
+				   "failed to parse simple path in macro "
+				   "invocation (for type)");
+		      add_error (std::move (error));
+		    }
+
+		  return nullptr;
+		}
+
+	      lexer.skip_token ();
+
+	      AST::DelimTokenTree tok_tree = parse_delim_token_tree ();
+
+	      return std::unique_ptr<AST::MacroInvocation> (
+		new AST::MacroInvocation (
+		  AST::MacroInvocData (std::move (macro_path),
+				       std::move (tok_tree)),
+		  {}, locus));
+	    }
+	    case PLUS: {
+	      // type param bounds
+	      std::vector<std::unique_ptr<AST::TypeParamBound>> bounds;
+
+	      // convert type path to trait bound
+	      std::unique_ptr<AST::TraitBound> path_bound (
+		new AST::TraitBound (std::move (path), locus, false, false));
+	      bounds.push_back (std::move (path_bound));
+
+	      /* parse rest of bounds - FIXME: better way to find when to stop
+	       * parsing */
+	      while (t->get_id () == PLUS)
+		{
+		  lexer.skip_token ();
+
+		  // parse bound if it exists - if not, assume end of sequence
+		  std::unique_ptr<AST::TypeParamBound> bound
+		    = parse_type_param_bound ();
+		  if (bound == nullptr)
+		    {
+		      break;
+		    }
+		  bounds.push_back (std::move (bound));
+
+		  t = lexer.peek_token ();
+		}
+
+	      return std::unique_ptr<AST::TraitObjectType> (
+		new AST::TraitObjectType (std::move (bounds), locus, false));
+	    }
+	  default:
+	    // assume that this is a type path and not an error
+	    return std::unique_ptr<AST::TypePath> (
+	      new AST::TypePath (std::move (path)));
+	  }
+      }
+    case LEFT_PAREN:
+      /* tuple type or parenthesised type - requires further disambiguation
+       * (the usual). ok apparently can be a parenthesised TraitBound too, so
+       * could be TraitObjectTypeOneBound or TraitObjectType */
+      return parse_paren_prefixed_type ();
+    case FOR:
+      // TraitObjectTypeOneBound or BareFunctionType
+      return parse_for_prefixed_type ();
+    case ASYNC:
+    case CONST:
+    case UNSAFE:
+    case EXTERN_TOK:
+    case FN_TOK:
+      // bare function type (with no for lifetimes)
+      return parse_bare_function_type (std::vector<AST::LifetimeParam> ());
+    case IMPL:
+      lexer.skip_token ();
+      if (lexer.peek_token ()->get_id () == LIFETIME)
+	{
+	  /* cannot be one bound because lifetime prevents it from being
+	   * traitbound */
+	  std::vector<std::unique_ptr<AST::TypeParamBound>> bounds
+	    = parse_type_param_bounds ();
+
+	  return std::unique_ptr<AST::ImplTraitType> (
+	    new AST::ImplTraitType (std::move (bounds), t->get_locus ()));
+	}
+      else
+	{
+	  // should be trait bound, so parse trait bound
+	  std::unique_ptr<AST::TraitBound> initial_bound = parse_trait_bound ();
+	  if (initial_bound == nullptr)
+	    {
+	      if (save_errors)
+		{
+		  Error error (lexer.peek_token ()->get_locus (),
+			       "failed to parse ImplTraitType initial bound");
+		  add_error (std::move (error));
+		}
+
+	      return nullptr;
+	    }
+
+	  Location locus = t->get_locus ();
+
+	  // short cut if next token isn't '+'
+	  t = lexer.peek_token ();
+	  if (t->get_id () != PLUS)
+	    {
+	      // convert trait bound to value object
+	      AST::TraitBound value_bound (*initial_bound);
+
+	      // DEBUG: removed as unique ptr, so should auto-delete
+	      // delete initial_bound;
+
+	      return std::unique_ptr<AST::ImplTraitTypeOneBound> (
+		new AST::ImplTraitTypeOneBound (std::move (value_bound),
+						locus));
+	    }
+
+	  // parse additional type param bounds
+	  std::vector<std::unique_ptr<AST::TypeParamBound>> bounds;
+	  bounds.push_back (std::move (initial_bound));
+	  while (t->get_id () == PLUS)
+	    {
+	      lexer.skip_token ();
+
+	      // parse bound if it exists
+	      std::unique_ptr<AST::TypeParamBound> bound
+		= parse_type_param_bound ();
+	      if (bound == nullptr)
+		{
+		  // not an error as trailing plus may exist
+		  break;
+		}
+	      bounds.push_back (std::move (bound));
+
+	      t = lexer.peek_token ();
+	    }
+
+	  return std::unique_ptr<AST::ImplTraitType> (
+	    new AST::ImplTraitType (std::move (bounds), locus));
+	}
+    case DYN:
+      case QUESTION_MARK: {
+	// either TraitObjectType or TraitObjectTypeOneBound
+	bool has_dyn = false;
+	if (t->get_id () == DYN)
+	  {
+	    lexer.skip_token ();
+	    has_dyn = true;
+	  }
+
+	if (lexer.peek_token ()->get_id () == LIFETIME)
+	  {
+	    /* cannot be one bound because lifetime prevents it from being
+	     * traitbound */
+	    std::vector<std::unique_ptr<AST::TypeParamBound>> bounds
+	      = parse_type_param_bounds ();
+
+	    return std::unique_ptr<AST::TraitObjectType> (
+	      new AST::TraitObjectType (std::move (bounds), t->get_locus (),
+					has_dyn));
+	  }
+	else
+	  {
+	    // should be trait bound, so parse trait bound
+	    std::unique_ptr<AST::TraitBound> initial_bound
+	      = parse_trait_bound ();
+	    if (initial_bound == nullptr)
+	      {
+		if (save_errors)
+		  {
+		    Error error (
+		      lexer.peek_token ()->get_locus (),
+		      "failed to parse TraitObjectType initial bound");
+		    add_error (std::move (error));
+		  }
+
+		return nullptr;
+	      }
+
+	    // short cut if next token isn't '+'
+	    t = lexer.peek_token ();
+	    if (t->get_id () != PLUS)
+	      {
+		// convert trait bound to value object
+		AST::TraitBound value_bound (*initial_bound);
+
+		// DEBUG: removed as unique ptr, so should auto delete
+		// delete initial_bound;
+
+		return std::unique_ptr<AST::TraitObjectTypeOneBound> (
+		  new AST::TraitObjectTypeOneBound (std::move (value_bound),
+						    t->get_locus (), has_dyn));
+	      }
+
+	    // parse additional type param bounds
+	    std::vector<std::unique_ptr<AST::TypeParamBound>> bounds;
+	    bounds.push_back (std::move (initial_bound));
+	    while (t->get_id () == PLUS)
+	      {
+		lexer.skip_token ();
+
+		// parse bound if it exists
+		std::unique_ptr<AST::TypeParamBound> bound
+		  = parse_type_param_bound ();
+		if (bound == nullptr)
+		  {
+		    // not an error as trailing plus may exist
+		    break;
+		  }
+		bounds.push_back (std::move (bound));
+
+		t = lexer.peek_token ();
+	      }
+
+	    return std::unique_ptr<AST::TraitObjectType> (
+	      new AST::TraitObjectType (std::move (bounds), t->get_locus (),
+					has_dyn));
+	  }
+      }
+    default:
+      if (save_errors)
+	add_error (Error (t->get_locus (), "unrecognised token %qs in type",
+			  t->get_token_description ()));
+
+      return nullptr;
+    }
+}
+
+/* Parses a type that has '(' as its first character. Returns a tuple type,
+ * parenthesised type, TraitObjectTypeOneBound, or TraitObjectType depending
+ * on following characters. */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::Type>
+Parser<ManagedTokenSource>::parse_paren_prefixed_type ()
+{
+  /* NOTE: Syntactical ambiguity of a parenthesised trait bound is considered
+   * a trait bound, not a parenthesised type, so that it can still be used in
+   * type param bounds. */
+
+  /* NOTE: this implementation is really shit but I couldn't think of a better
+   * one. It requires essentially breaking polymorphism and downcasting via
+   * virtual method abuse, as it was copied from the rustc implementation (in
+   * which types are reified due to tagged union), after a more OOP attempt by
+   * me failed. */
+  Location left_delim_locus = lexer.peek_token ()->get_locus ();
+
+  // skip left delim
+  lexer.skip_token ();
+  /* while next token isn't close delim, parse comma-separated types, saving
+   * whether trailing comma happens */
+  const_TokenPtr t = lexer.peek_token ();
+  bool trailing_comma = true;
+  std::vector<std::unique_ptr<AST::Type>> types;
+
+  while (t->get_id () != RIGHT_PAREN)
+    {
+      std::unique_ptr<AST::Type> type = parse_type ();
+      if (type == nullptr)
+	{
+	  Error error (t->get_locus (),
+		       "failed to parse type inside parentheses (probably "
+		       "tuple or parenthesised)");
+	  add_error (std::move (error));
+
+	  return nullptr;
+	}
+      types.push_back (std::move (type));
+
+      t = lexer.peek_token ();
+      if (t->get_id () != COMMA)
+	{
+	  trailing_comma = false;
+	  break;
+	}
+      lexer.skip_token ();
+
+      t = lexer.peek_token ();
+    }
+
+  if (!skip_token (RIGHT_PAREN))
+    {
+      return nullptr;
+    }
+
+  // if only one type and no trailing comma, then not a tuple type
+  if (types.size () == 1 && !trailing_comma)
+    {
+      // must be a TraitObjectType (with more than one bound)
+      if (lexer.peek_token ()->get_id () == PLUS)
+	{
+	  // create type param bounds vector
+	  std::vector<std::unique_ptr<AST::TypeParamBound>> bounds;
+
+	  // HACK: convert type to traitbound and add to bounds
+	  std::unique_ptr<AST::Type> released_ptr = std::move (types[0]);
+	  std::unique_ptr<AST::TraitBound> converted_bound (
+	    released_ptr->to_trait_bound (true));
+	  if (converted_bound == nullptr)
+	    {
+	      Error error (
+		lexer.peek_token ()->get_locus (),
+		"failed to hackily converted parsed type to trait bound");
+	      add_error (std::move (error));
+
+	      return nullptr;
+	    }
+	  bounds.push_back (std::move (converted_bound));
+
+	  t = lexer.peek_token ();
+	  while (t->get_id () == PLUS)
+	    {
+	      lexer.skip_token ();
+
+	      // attempt to parse typeparambound
+	      std::unique_ptr<AST::TypeParamBound> bound
+		= parse_type_param_bound ();
+	      if (bound == nullptr)
+		{
+		  // not an error if null
+		  break;
+		}
+	      bounds.push_back (std::move (bound));
+
+	      t = lexer.peek_token ();
+	    }
+
+	  return std::unique_ptr<AST::TraitObjectType> (
+	    new AST::TraitObjectType (std::move (bounds), left_delim_locus,
+				      false));
+	}
+      else
+	{
+	  // release vector pointer
+	  std::unique_ptr<AST::Type> released_ptr = std::move (types[0]);
+	  /* HACK: attempt to convert to trait bound. if fails, parenthesised
+	   * type */
+	  std::unique_ptr<AST::TraitBound> converted_bound (
+	    released_ptr->to_trait_bound (true));
+	  if (converted_bound == nullptr)
+	    {
+	      // parenthesised type
+	      return std::unique_ptr<AST::ParenthesisedType> (
+		new AST::ParenthesisedType (std::move (released_ptr),
+					    left_delim_locus));
+	    }
+	  else
+	    {
+	      // trait object type (one bound)
+
+	      // get value semantics trait bound
+	      AST::TraitBound value_bound (*converted_bound);
+
+	      return std::unique_ptr<AST::TraitObjectTypeOneBound> (
+		new AST::TraitObjectTypeOneBound (value_bound,
+						  left_delim_locus));
+	    }
+	}
+    }
+  else
+    {
+      return std::unique_ptr<AST::TupleType> (
+	new AST::TupleType (std::move (types), left_delim_locus));
+    }
+  /* TODO: ensure that this ensures that dynamic dispatch for traits is not
+   * lost somehow */
+}
+
+/* Parses a type that has 'for' as its first character. This means it has a
+ * "for lifetimes", so returns either a BareFunctionType, TraitObjectType, or
+ * TraitObjectTypeOneBound depending on following characters. */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::Type>
+Parser<ManagedTokenSource>::parse_for_prefixed_type ()
+{
+  Location for_locus = lexer.peek_token ()->get_locus ();
+  // parse for lifetimes in type
+  std::vector<AST::LifetimeParam> for_lifetimes = parse_for_lifetimes ();
+
+  // branch on next token - either function or a trait type
+  const_TokenPtr t = lexer.peek_token ();
+  switch (t->get_id ())
+    {
+    case ASYNC:
+    case CONST:
+    case UNSAFE:
+    case EXTERN_TOK:
+    case FN_TOK:
+      return parse_bare_function_type (std::move (for_lifetimes));
+    case SCOPE_RESOLUTION:
+    case IDENTIFIER:
+    case SUPER:
+    case SELF:
+    case SELF_ALIAS:
+    case CRATE:
+      case DOLLAR_SIGN: {
+	// path, so trait type
+
+	// parse type path to finish parsing trait bound
+	AST::TypePath path = parse_type_path ();
+
+	t = lexer.peek_token ();
+	if (t->get_id () != PLUS)
+	  {
+	    // must be one-bound trait type
+	    // create trait bound value object
+	    AST::TraitBound bound (std::move (path), for_locus, false, false,
+				   std::move (for_lifetimes));
+
+	    return std::unique_ptr<AST::TraitObjectTypeOneBound> (
+	      new AST::TraitObjectTypeOneBound (std::move (bound), for_locus));
+	  }
+
+	/* more than one bound trait type (or at least parsed as it - could be
+	 * trailing '+') create trait bound pointer and bounds */
+	std::unique_ptr<AST::TraitBound> initial_bound (
+	  new AST::TraitBound (std::move (path), for_locus, false, false,
+			       std::move (for_lifetimes)));
+	std::vector<std::unique_ptr<AST::TypeParamBound>> bounds;
+	bounds.push_back (std::move (initial_bound));
+
+	while (t->get_id () == PLUS)
+	  {
+	    lexer.skip_token ();
+
+	    // parse type param bound if it exists
+	    std::unique_ptr<AST::TypeParamBound> bound
+	      = parse_type_param_bound ();
+	    if (bound == nullptr)
+	      {
+		// not an error - e.g. trailing plus
+		return nullptr;
+	      }
+	    bounds.push_back (std::move (bound));
+
+	    t = lexer.peek_token ();
+	  }
+
+	return std::unique_ptr<AST::TraitObjectType> (
+	  new AST::TraitObjectType (std::move (bounds), for_locus, false));
+      }
+    default:
+      // error
+      add_error (Error (t->get_locus (),
+			"unrecognised token %qs in bare function type or trait "
+			"object type or trait object type one bound",
+			t->get_token_description ()));
+
+      return nullptr;
+    }
+}
+
+// Parses a maybe named param used in bare function types.
+template <typename ManagedTokenSource>
+AST::MaybeNamedParam
+Parser<ManagedTokenSource>::parse_maybe_named_param (AST::AttrVec outer_attrs)
+{
+  /* Basically guess that param is named if first token is identifier or
+   * underscore and second token is semicolon. This should probably have no
+   * exceptions. rustc uses backtracking to parse these, but at the time of
+   * writing gccrs has no backtracking capabilities. */
+  const_TokenPtr current = lexer.peek_token ();
+  const_TokenPtr next = lexer.peek_token (1);
+
+  Identifier name;
+  AST::MaybeNamedParam::ParamKind kind = AST::MaybeNamedParam::UNNAMED;
+
+  if (current->get_id () == IDENTIFIER && next->get_id () == COLON)
+    {
+      // named param
+      name = current->get_str ();
+      kind = AST::MaybeNamedParam::IDENTIFIER;
+      lexer.skip_token (1);
+    }
+  else if (current->get_id () == UNDERSCORE && next->get_id () == COLON)
+    {
+      // wildcard param
+      name = "_";
+      kind = AST::MaybeNamedParam::WILDCARD;
+      lexer.skip_token (1);
+    }
+
+  // parse type (required)
+  std::unique_ptr<AST::Type> type = parse_type ();
+  if (type == nullptr)
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "failed to parse type in maybe named param");
+      add_error (std::move (error));
+
+      return AST::MaybeNamedParam::create_error ();
+    }
+
+  return AST::MaybeNamedParam (std::move (name), kind, std::move (type),
+			       std::move (outer_attrs), current->get_locus ());
+}
+
+/* Parses a bare function type (with the given for lifetimes for convenience -
+ * does not parse them itself). */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::BareFunctionType>
+Parser<ManagedTokenSource>::parse_bare_function_type (
+  std::vector<AST::LifetimeParam> for_lifetimes)
+{
+  // TODO: pass in for lifetime location as param
+  Location best_try_locus = lexer.peek_token ()->get_locus ();
+
+  AST::FunctionQualifiers qualifiers = parse_function_qualifiers ();
+
+  if (!skip_token (FN_TOK))
+    return nullptr;
+
+  if (!skip_token (LEFT_PAREN))
+    return nullptr;
+
+  // parse function params, if they exist
+  std::vector<AST::MaybeNamedParam> params;
+  bool is_variadic = false;
+  AST::AttrVec variadic_attrs;
+
+  const_TokenPtr t = lexer.peek_token ();
+  while (t->get_id () != RIGHT_PAREN)
+    {
+      AST::AttrVec temp_attrs = parse_outer_attributes ();
+
+      if (lexer.peek_token ()->get_id () == ELLIPSIS)
+	{
+	  lexer.skip_token ();
+	  is_variadic = true;
+	  variadic_attrs = std::move (temp_attrs);
+
+	  t = lexer.peek_token ();
+
+	  if (t->get_id () != RIGHT_PAREN)
+	    {
+	      Error error (t->get_locus (),
+			   "expected right parentheses after variadic in maybe "
+			   "named function "
+			   "parameters, found %qs",
+			   t->get_token_description ());
+	      add_error (std::move (error));
+
+	      return nullptr;
+	    }
+
+	  break;
+	}
+
+      AST::MaybeNamedParam param
+	= parse_maybe_named_param (std::move (temp_attrs));
+      if (param.is_error ())
+	{
+	  Error error (
+	    lexer.peek_token ()->get_locus (),
+	    "failed to parse maybe named param in bare function type");
+	  add_error (std::move (error));
+
+	  return nullptr;
+	}
+      params.push_back (std::move (param));
+
+      if (lexer.peek_token ()->get_id () != COMMA)
+	break;
+
+      lexer.skip_token ();
+      t = lexer.peek_token ();
+    }
+
+  if (!skip_token (RIGHT_PAREN))
+    return nullptr;
+
+  // bare function return type, if exists
+  std::unique_ptr<AST::TypeNoBounds> return_type = nullptr;
+  if (lexer.peek_token ()->get_id () == RETURN_TYPE)
+    {
+      lexer.skip_token ();
+
+      // parse required TypeNoBounds
+      return_type = parse_type_no_bounds ();
+      if (return_type == nullptr)
+	{
+	  Error error (lexer.peek_token ()->get_locus (),
+		       "failed to parse return type (type no bounds) in bare "
+		       "function type");
+	  add_error (std::move (error));
+
+	  return nullptr;
+	}
+    }
+
+  return std::unique_ptr<AST::BareFunctionType> (
+    new AST::BareFunctionType (std::move (for_lifetimes),
+			       std::move (qualifiers), std::move (params),
+			       is_variadic, std::move (variadic_attrs),
+			       std::move (return_type), best_try_locus));
+}
+
+// Parses a reference type (mutable or immutable, with given lifetime).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ReferenceType>
+Parser<ManagedTokenSource>::parse_reference_type ()
+{
+  Location locus = lexer.peek_token ()->get_locus ();
+  skip_token (AMP);
+
+  // parse optional lifetime
+  AST::Lifetime lifetime = AST::Lifetime::error ();
+  if (lexer.peek_token ()->get_id () == LIFETIME)
+    {
+      lifetime = parse_lifetime ();
+      if (lifetime.is_error ())
+	{
+	  Error error (lexer.peek_token ()->get_locus (),
+		       "failed to parse lifetime in reference type");
+	  add_error (std::move (error));
+
+	  return nullptr;
+	}
+    }
+
+  bool is_mut = false;
+  if (lexer.peek_token ()->get_id () == MUT)
+    {
+      lexer.skip_token ();
+      is_mut = true;
+    }
+
+  // parse type no bounds, which is required
+  std::unique_ptr<AST::TypeNoBounds> type = parse_type_no_bounds ();
+  if (type == nullptr)
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "failed to parse referenced type in reference type");
+      add_error (std::move (error));
+
+      return nullptr;
+    }
+
+  return std::unique_ptr<AST::ReferenceType> (
+    new AST::ReferenceType (is_mut, std::move (type), locus,
+			    std::move (lifetime)));
+}
+
+// Parses a raw (unsafe) pointer type.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::RawPointerType>
+Parser<ManagedTokenSource>::parse_raw_pointer_type ()
+{
+  Location locus = lexer.peek_token ()->get_locus ();
+  skip_token (ASTERISK);
+
+  AST::RawPointerType::PointerType kind = AST::RawPointerType::CONST;
+
+  // branch on next token for pointer kind info
+  const_TokenPtr t = lexer.peek_token ();
+  switch (t->get_id ())
+    {
+    case MUT:
+      kind = AST::RawPointerType::MUT;
+      lexer.skip_token ();
+      break;
+    case CONST:
+      kind = AST::RawPointerType::CONST;
+      lexer.skip_token ();
+      break;
+    default:
+      add_error (Error (t->get_locus (),
+			"unrecognised token %qs in raw pointer type",
+			t->get_token_description ()));
+
+      return nullptr;
+    }
+
+  // parse type no bounds (required)
+  std::unique_ptr<AST::TypeNoBounds> type = parse_type_no_bounds ();
+  if (type == nullptr)
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "failed to parse pointed type of raw pointer type");
+      add_error (std::move (error));
+
+      return nullptr;
+    }
+
+  return std::unique_ptr<AST::RawPointerType> (
+    new AST::RawPointerType (kind, std::move (type), locus));
+}
+
+/* Parses a slice or array type, depending on following arguments (as
+ * lookahead is not possible). */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::TypeNoBounds>
+Parser<ManagedTokenSource>::parse_slice_or_array_type ()
+{
+  Location locus = lexer.peek_token ()->get_locus ();
+  skip_token (LEFT_SQUARE);
+
+  // parse inner type (required)
+  std::unique_ptr<AST::Type> inner_type = parse_type ();
+  if (inner_type == nullptr)
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "failed to parse inner type in slice or array type");
+      add_error (std::move (error));
+
+      return nullptr;
+    }
+
+  // branch on next token
+  const_TokenPtr t = lexer.peek_token ();
+  switch (t->get_id ())
+    {
+    case RIGHT_SQUARE:
+      // slice type
+      lexer.skip_token ();
+
+      return std::unique_ptr<AST::SliceType> (
+	new AST::SliceType (std::move (inner_type), locus));
+      case SEMICOLON: {
+	// array type
+	lexer.skip_token ();
+
+	// parse required array size expression
+	std::unique_ptr<AST::Expr> size = parse_expr ();
+	if (size == nullptr)
+	  {
+	    Error error (lexer.peek_token ()->get_locus (),
+			 "failed to parse size expression in array type");
+	    add_error (std::move (error));
+
+	    return nullptr;
+	  }
+
+	if (!skip_token (RIGHT_SQUARE))
+	  {
+	    return nullptr;
+	  }
+
+	return std::unique_ptr<AST::ArrayType> (
+	  new AST::ArrayType (std::move (inner_type), std::move (size), locus));
+      }
+    default:
+      // error
+      add_error (
+	Error (t->get_locus (),
+	       "unrecognised token %qs in slice or array type after inner type",
+	       t->get_token_description ()));
+
+      return nullptr;
+    }
+}
+
+// Parses a type, taking into account type boundary disambiguation.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::TypeNoBounds>
+Parser<ManagedTokenSource>::parse_type_no_bounds ()
+{
+  const_TokenPtr t = lexer.peek_token ();
+  switch (t->get_id ())
+    {
+    case EXCLAM:
+      // never type - can't be macro as no path beforehand
+      lexer.skip_token ();
+      return std::unique_ptr<AST::NeverType> (
+	new AST::NeverType (t->get_locus ()));
+    case LEFT_SQUARE:
+      // slice type or array type - requires further disambiguation
+      return parse_slice_or_array_type ();
+      case LEFT_ANGLE: {
+	// qualified path in type
+	AST::QualifiedPathInType path = parse_qualified_path_in_type ();
+	if (path.is_error ())
+	  {
+	    Error error (t->get_locus (),
+			 "failed to parse qualified path in type");
+	    add_error (std::move (error));
+
+	    return nullptr;
+	  }
+	return std::unique_ptr<AST::QualifiedPathInType> (
+	  new AST::QualifiedPathInType (std::move (path)));
+      }
+    case UNDERSCORE:
+      // inferred type
+      lexer.skip_token ();
+      return std::unique_ptr<AST::InferredType> (
+	new AST::InferredType (t->get_locus ()));
+    case ASTERISK:
+      // raw pointer type
+      return parse_raw_pointer_type ();
+    case AMP: // does this also include AMP_AMP?
+      // reference type
+      return parse_reference_type ();
+    case LIFETIME:
+      /* probably a lifetime bound, so probably type param bounds in
+       * TraitObjectType. this is not allowed, but detection here for error
+       * message */
+      add_error (Error (t->get_locus (),
+			"lifetime bounds (i.e. in type param bounds, in "
+			"TraitObjectType) are not allowed as TypeNoBounds"));
+
+      return nullptr;
+    case IDENTIFIER:
+    case SUPER:
+    case SELF:
+    case SELF_ALIAS:
+    case CRATE:
+    case DOLLAR_SIGN:
+      case SCOPE_RESOLUTION: {
+	// macro invocation or type path - requires further disambiguation.
+	/* for parsing path component of each rule, perhaps parse it as a
+	 * typepath and attempt conversion to simplepath if a trailing '!' is
+	 * found */
+	/* Type path also includes TraitObjectTypeOneBound BUT if it starts
+	 * with it, it is exactly the same as a TypePath syntactically, so
+	 * this is a syntactical ambiguity. As such, the parser will parse it
+	 * as a TypePath. This, however, does not prevent TraitObjectType from
+	 * starting with a typepath. */
+
+	// parse path as type path
+	AST::TypePath path = parse_type_path ();
+	if (path.is_error ())
+	  {
+	    Error error (
+	      t->get_locus (),
+	      "failed to parse path as first component of type no bounds");
+	    add_error (std::move (error));
+
+	    return nullptr;
+	  }
+	Location locus = path.get_locus ();
+
+	// branch on next token
+	t = lexer.peek_token ();
+	switch (t->get_id ())
+	  {
+	    case EXCLAM: {
+	      // macro invocation
+	      // convert to simple path
+	      AST::SimplePath macro_path = path.as_simple_path ();
+	      if (macro_path.is_empty ())
+		{
+		  Error error (t->get_locus (),
+			       "failed to parse simple path in macro "
+			       "invocation (for type)");
+		  add_error (std::move (error));
+
+		  return nullptr;
+		}
+
+	      lexer.skip_token ();
+
+	      AST::DelimTokenTree tok_tree = parse_delim_token_tree ();
+
+	      return std::unique_ptr<AST::MacroInvocation> (
+		new AST::MacroInvocation (
+		  AST::MacroInvocData (std::move (macro_path),
+				       std::move (tok_tree)),
+		  {}, locus));
+	    }
+	  default:
+	    // assume that this is a type path and not an error
+	    return std::unique_ptr<AST::TypePath> (
+	      new AST::TypePath (std::move (path)));
+	  }
+      }
+    case LEFT_PAREN:
+      /* tuple type or parenthesised type - requires further disambiguation
+       * (the usual). ok apparently can be a parenthesised TraitBound too, so
+       * could be TraitObjectTypeOneBound */
+      return parse_paren_prefixed_type_no_bounds ();
+    case FOR:
+    case ASYNC:
+    case CONST:
+    case UNSAFE:
+    case EXTERN_TOK:
+    case FN_TOK:
+      // bare function type (with no for lifetimes)
+      return parse_bare_function_type (std::vector<AST::LifetimeParam> ());
+    case IMPL:
+      lexer.skip_token ();
+      if (lexer.peek_token ()->get_id () == LIFETIME)
+	{
+	  /* cannot be one bound because lifetime prevents it from being
+	   * traitbound not allowed as type no bounds, only here for error
+	   * message */
+	  Error error (
+	    lexer.peek_token ()->get_locus (),
+	    "lifetime (probably lifetime bound, in type param "
+	    "bounds, in ImplTraitType) is not allowed in TypeNoBounds");
+	  add_error (std::move (error));
+
+	  return nullptr;
+	}
+      else
+	{
+	  // should be trait bound, so parse trait bound
+	  std::unique_ptr<AST::TraitBound> initial_bound = parse_trait_bound ();
+	  if (initial_bound == nullptr)
+	    {
+	      Error error (lexer.peek_token ()->get_locus (),
+			   "failed to parse ImplTraitTypeOneBound bound");
+	      add_error (std::move (error));
+
+	      return nullptr;
+	    }
+
+	  Location locus = t->get_locus ();
+
+	  // ensure not a trait with multiple bounds
+	  t = lexer.peek_token ();
+	  if (t->get_id () == PLUS)
+	    {
+	      Error error (t->get_locus (),
+			   "plus after trait bound means an ImplTraitType, "
+			   "which is not allowed as a TypeNoBounds");
+	      add_error (std::move (error));
+
+	      return nullptr;
+	    }
+
+	  // convert trait bound to value object
+	  AST::TraitBound value_bound (*initial_bound);
+
+	  return std::unique_ptr<AST::ImplTraitTypeOneBound> (
+	    new AST::ImplTraitTypeOneBound (std::move (value_bound), locus));
+	}
+    case DYN:
+      case QUESTION_MARK: {
+	// either TraitObjectTypeOneBound
+	bool has_dyn = false;
+	if (t->get_id () == DYN)
+	  {
+	    lexer.skip_token ();
+	    has_dyn = true;
+	  }
+
+	if (lexer.peek_token ()->get_id () == LIFETIME)
+	  {
+	    /* means that cannot be TraitObjectTypeOneBound - so here for
+	     * error message */
+	    Error error (lexer.peek_token ()->get_locus (),
+			 "lifetime as bound in TraitObjectTypeOneBound "
+			 "is not allowed, so cannot be TypeNoBounds");
+	    add_error (std::move (error));
+
+	    return nullptr;
+	  }
+
+	// should be trait bound, so parse trait bound
+	std::unique_ptr<AST::TraitBound> initial_bound = parse_trait_bound ();
+	if (initial_bound == nullptr)
+	  {
+	    Error error (
+	      lexer.peek_token ()->get_locus (),
+	      "failed to parse TraitObjectTypeOneBound initial bound");
+	    add_error (std::move (error));
+
+	    return nullptr;
+	  }
+
+	Location locus = t->get_locus ();
+
+	// detect error with plus as next token
+	t = lexer.peek_token ();
+	if (t->get_id () == PLUS)
+	  {
+	    Error error (t->get_locus (),
+			 "plus after trait bound means a TraitObjectType, "
+			 "which is not allowed as a TypeNoBounds");
+	    add_error (std::move (error));
+
+	    return nullptr;
+	  }
+
+	// convert trait bound to value object
+	AST::TraitBound value_bound (*initial_bound);
+
+	return std::unique_ptr<AST::TraitObjectTypeOneBound> (
+	  new AST::TraitObjectTypeOneBound (std::move (value_bound), locus,
+					    has_dyn));
+      }
+    default:
+      add_error (Error (t->get_locus (),
+			"unrecognised token %qs in type no bounds",
+			t->get_token_description ()));
+
+      return nullptr;
+    }
+}
+
+// Parses a type no bounds beginning with '('.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::TypeNoBounds>
+Parser<ManagedTokenSource>::parse_paren_prefixed_type_no_bounds ()
+{
+  /* NOTE: this could probably be parsed without the HACK solution of
+   * parse_paren_prefixed_type, but I was lazy. So FIXME for future.*/
+
+  /* NOTE: again, syntactical ambiguity of a parenthesised trait bound is
+   * considered a trait bound, not a parenthesised type, so that it can still
+   * be used in type param bounds. */
+
+  Location left_paren_locus = lexer.peek_token ()->get_locus ();
+
+  // skip left delim
+  lexer.skip_token ();
+  /* while next token isn't close delim, parse comma-separated types, saving
+   * whether trailing comma happens */
+  const_TokenPtr t = lexer.peek_token ();
+  bool trailing_comma = true;
+  std::vector<std::unique_ptr<AST::Type>> types;
+
+  while (t->get_id () != RIGHT_PAREN)
+    {
+      std::unique_ptr<AST::Type> type = parse_type ();
+      if (type == nullptr)
+	{
+	  Error error (t->get_locus (),
+		       "failed to parse type inside parentheses (probably "
+		       "tuple or parenthesised)");
+	  add_error (std::move (error));
+
+	  return nullptr;
+	}
+      types.push_back (std::move (type));
+
+      t = lexer.peek_token ();
+      if (t->get_id () != COMMA)
+	{
+	  trailing_comma = false;
+	  break;
+	}
+      lexer.skip_token ();
+
+      t = lexer.peek_token ();
+    }
+
+  if (!skip_token (RIGHT_PAREN))
+    {
+      return nullptr;
+    }
+
+  // if only one type and no trailing comma, then not a tuple type
+  if (types.size () == 1 && !trailing_comma)
+    {
+      // must be a TraitObjectType (with more than one bound)
+      if (lexer.peek_token ()->get_id () == PLUS)
+	{
+	  // error - this is not allowed for type no bounds
+	  Error error (lexer.peek_token ()->get_locus (),
+		       "plus (implying TraitObjectType as type param "
+		       "bounds) is not allowed in type no bounds");
+	  add_error (std::move (error));
+
+	  return nullptr;
+	}
+      else
+	{
+	  // release vector pointer
+	  std::unique_ptr<AST::Type> released_ptr = std::move (types[0]);
+	  /* HACK: attempt to convert to trait bound. if fails, parenthesised
+	   * type */
+	  std::unique_ptr<AST::TraitBound> converted_bound (
+	    released_ptr->to_trait_bound (true));
+	  if (converted_bound == nullptr)
+	    {
+	      // parenthesised type
+	      return std::unique_ptr<AST::ParenthesisedType> (
+		new AST::ParenthesisedType (std::move (released_ptr),
+					    left_paren_locus));
+	    }
+	  else
+	    {
+	      // trait object type (one bound)
+
+	      // get value semantics trait bound
+	      AST::TraitBound value_bound (*converted_bound);
+
+	      return std::unique_ptr<AST::TraitObjectTypeOneBound> (
+		new AST::TraitObjectTypeOneBound (value_bound,
+						  left_paren_locus));
+	    }
+	}
+    }
+  else
+    {
+      return std::unique_ptr<AST::TupleType> (
+	new AST::TupleType (std::move (types), left_paren_locus));
+    }
+  /* TODO: ensure that this ensures that dynamic dispatch for traits is not
+   * lost somehow */
+}
+
+/* Parses a literal pattern or range pattern. Assumes that literals passed in
+ * are valid range pattern bounds. Do not pass in paths in expressions, for
+ * instance. */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::Pattern>
+Parser<ManagedTokenSource>::parse_literal_or_range_pattern ()
+{
+  const_TokenPtr range_lower = lexer.peek_token ();
+  AST::Literal::LitType type = AST::Literal::STRING;
+  bool has_minus = false;
+
+  // get lit type
+  switch (range_lower->get_id ())
+    {
+    case CHAR_LITERAL:
+      type = AST::Literal::CHAR;
+      lexer.skip_token ();
+      break;
+    case BYTE_CHAR_LITERAL:
+      type = AST::Literal::BYTE;
+      lexer.skip_token ();
+      break;
+    case INT_LITERAL:
+      type = AST::Literal::INT;
+      lexer.skip_token ();
+      break;
+    case FLOAT_LITERAL:
+      type = AST::Literal::FLOAT;
+      lexer.skip_token ();
+      break;
+    case MINUS:
+      // branch on next token
+      range_lower = lexer.peek_token (1);
+      switch (range_lower->get_id ())
+	{
+	case INT_LITERAL:
+	  type = AST::Literal::INT;
+	  has_minus = true;
+	  lexer.skip_token (1);
+	  break;
+	case FLOAT_LITERAL:
+	  type = AST::Literal::FLOAT;
+	  has_minus = true;
+	  lexer.skip_token (1);
+	  break;
+	default:
+	  add_error (Error (range_lower->get_locus (),
+			    "token type %qs cannot be parsed as range pattern "
+			    "bound or literal after minus symbol",
+			    range_lower->get_token_description ()));
+
+	  return nullptr;
+	}
+      break;
+    default:
+      add_error (
+	Error (range_lower->get_locus (),
+	       "token type %qs cannot be parsed as range pattern bound",
+	       range_lower->get_token_description ()));
+
+      return nullptr;
+    }
+
+  const_TokenPtr next = lexer.peek_token ();
+  if (next->get_id () == DOT_DOT_EQ || next->get_id () == ELLIPSIS)
+    {
+      // range pattern
+      lexer.skip_token ();
+      std::unique_ptr<AST::RangePatternBound> lower (
+	new AST::RangePatternBoundLiteral (
+	  AST::Literal (range_lower->get_str (), type,
+			PrimitiveCoreType::CORETYPE_UNKNOWN),
+	  range_lower->get_locus (), has_minus));
+
+      std::unique_ptr<AST::RangePatternBound> upper
+	= parse_range_pattern_bound ();
+      if (upper == nullptr)
+	{
+	  Error error (next->get_locus (),
+		       "failed to parse range pattern bound in range pattern");
+	  add_error (std::move (error));
+
+	  return nullptr;
+	}
+
+      return std::unique_ptr<AST::RangePattern> (
+	new AST::RangePattern (std::move (lower), std::move (upper),
+			       range_lower->get_locus ()));
+    }
+  else
+    {
+      // literal pattern
+      return std::unique_ptr<AST::LiteralPattern> (
+	new AST::LiteralPattern (range_lower->get_str (), type,
+				 range_lower->get_locus ()));
+    }
+}
+
+// Parses a range pattern bound (value only).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::RangePatternBound>
+Parser<ManagedTokenSource>::parse_range_pattern_bound ()
+{
+  const_TokenPtr range_lower = lexer.peek_token ();
+  Location range_lower_locus = range_lower->get_locus ();
+
+  // get lit type
+  switch (range_lower->get_id ())
+    {
+    case CHAR_LITERAL:
+      lexer.skip_token ();
+      return std::unique_ptr<AST::RangePatternBoundLiteral> (
+	new AST::RangePatternBoundLiteral (
+	  AST::Literal (range_lower->get_str (), AST::Literal::CHAR,
+			range_lower->get_type_hint ()),
+	  range_lower_locus));
+    case BYTE_CHAR_LITERAL:
+      lexer.skip_token ();
+      return std::unique_ptr<AST::RangePatternBoundLiteral> (
+	new AST::RangePatternBoundLiteral (
+	  AST::Literal (range_lower->get_str (), AST::Literal::BYTE,
+			range_lower->get_type_hint ()),
+	  range_lower_locus));
+    case INT_LITERAL:
+      lexer.skip_token ();
+      return std::unique_ptr<AST::RangePatternBoundLiteral> (
+	new AST::RangePatternBoundLiteral (
+	  AST::Literal (range_lower->get_str (), AST::Literal::INT,
+			range_lower->get_type_hint ()),
+	  range_lower_locus));
+    case FLOAT_LITERAL:
+      lexer.skip_token ();
+      rust_debug ("warning: used deprecated float range pattern bound");
+      return std::unique_ptr<AST::RangePatternBoundLiteral> (
+	new AST::RangePatternBoundLiteral (
+	  AST::Literal (range_lower->get_str (), AST::Literal::FLOAT,
+			range_lower->get_type_hint ()),
+	  range_lower_locus));
+    case MINUS:
+      // branch on next token
+      range_lower = lexer.peek_token (1);
+      switch (range_lower->get_id ())
+	{
+	case INT_LITERAL:
+	  lexer.skip_token (1);
+	  return std::unique_ptr<AST::RangePatternBoundLiteral> (
+	    new AST::RangePatternBoundLiteral (
+	      AST::Literal (range_lower->get_str (), AST::Literal::INT,
+			    range_lower->get_type_hint ()),
+	      range_lower_locus, true));
+	case FLOAT_LITERAL:
+	  lexer.skip_token (1);
+	  rust_debug ("warning: used deprecated float range pattern bound");
+	  return std::unique_ptr<AST::RangePatternBoundLiteral> (
+	    new AST::RangePatternBoundLiteral (
+	      AST::Literal (range_lower->get_str (), AST::Literal::FLOAT,
+			    range_lower->get_type_hint ()),
+	      range_lower_locus, true));
+	default:
+	  add_error (Error (range_lower->get_locus (),
+			    "token type %qs cannot be parsed as range pattern "
+			    "bound after minus symbol",
+			    range_lower->get_token_description ()));
+
+	  return nullptr;
+	}
+    case IDENTIFIER:
+    case SUPER:
+    case SELF:
+    case SELF_ALIAS:
+    case CRATE:
+    case SCOPE_RESOLUTION:
+      case DOLLAR_SIGN: {
+	// path in expression
+	AST::PathInExpression path = parse_path_in_expression ();
+	if (path.is_error ())
+	  {
+	    Error error (
+	      range_lower->get_locus (),
+	      "failed to parse path in expression range pattern bound");
+	    add_error (std::move (error));
+
+	    return nullptr;
+	  }
+	return std::unique_ptr<AST::RangePatternBoundPath> (
+	  new AST::RangePatternBoundPath (std::move (path)));
+      }
+      case LEFT_ANGLE: {
+	// qualified path in expression
+	AST::QualifiedPathInExpression path
+	  = parse_qualified_path_in_expression ();
+	if (path.is_error ())
+	  {
+	    Error error (range_lower->get_locus (),
+			 "failed to parse qualified path in expression range "
+			 "pattern bound");
+	    add_error (std::move (error));
+
+	    return nullptr;
+	  }
+	return std::unique_ptr<AST::RangePatternBoundQualPath> (
+	  new AST::RangePatternBoundQualPath (std::move (path)));
+      }
+    default:
+      add_error (
+	Error (range_lower->get_locus (),
+	       "token type %qs cannot be parsed as range pattern bound",
+	       range_lower->get_token_description ()));
+
+      return nullptr;
+    }
+}
+
+// Parses a pattern (will further disambiguate any pattern).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::Pattern>
+Parser<ManagedTokenSource>::parse_pattern ()
+{
+  const_TokenPtr t = lexer.peek_token ();
+  switch (t->get_id ())
+    {
+    case TRUE_LITERAL:
+      lexer.skip_token ();
+      return std::unique_ptr<AST::LiteralPattern> (
+	new AST::LiteralPattern ("true", AST::Literal::BOOL, t->get_locus ()));
+    case FALSE_LITERAL:
+      lexer.skip_token ();
+      return std::unique_ptr<AST::LiteralPattern> (
+	new AST::LiteralPattern ("false", AST::Literal::BOOL, t->get_locus ()));
+    case CHAR_LITERAL:
+    case BYTE_CHAR_LITERAL:
+    case INT_LITERAL:
+    case FLOAT_LITERAL:
+      return parse_literal_or_range_pattern ();
+    case STRING_LITERAL:
+      lexer.skip_token ();
+      return std::unique_ptr<AST::LiteralPattern> (
+	new AST::LiteralPattern (t->get_str (), AST::Literal::STRING,
+				 t->get_locus ()));
+    case BYTE_STRING_LITERAL:
+      lexer.skip_token ();
+      return std::unique_ptr<AST::LiteralPattern> (
+	new AST::LiteralPattern (t->get_str (), AST::Literal::BYTE_STRING,
+				 t->get_locus ()));
+    // raw string and raw byte string literals too if they are readded to
+    // lexer
+    case MINUS:
+      if (lexer.peek_token (1)->get_id () == INT_LITERAL)
+	{
+	  return parse_literal_or_range_pattern ();
+	}
+      else if (lexer.peek_token (1)->get_id () == FLOAT_LITERAL)
+	{
+	  return parse_literal_or_range_pattern ();
+	}
+      else
+	{
+	  Error error (t->get_locus (), "unexpected token %<-%> in pattern - "
+					"did you forget an integer literal");
+	  add_error (std::move (error));
+
+	  return nullptr;
+	}
+    case UNDERSCORE:
+      lexer.skip_token ();
+      return std::unique_ptr<AST::WildcardPattern> (
+	new AST::WildcardPattern (t->get_locus ()));
+    case REF:
+    case MUT:
+      return parse_identifier_pattern ();
+    case IDENTIFIER:
+      /* if identifier with no scope resolution afterwards, identifier
+       * pattern. if scope resolution afterwards, path pattern (or range
+       * pattern or struct pattern or tuple struct pattern) or macro
+       * invocation */
+      return parse_ident_leading_pattern ();
+    case AMP:
+    case LOGICAL_AND:
+      // reference pattern
+      return parse_reference_pattern ();
+    case LEFT_PAREN:
+      // tuple pattern or grouped pattern
+      return parse_grouped_or_tuple_pattern ();
+    case LEFT_SQUARE:
+      // slice pattern
+      return parse_slice_pattern ();
+      case LEFT_ANGLE: {
+	// qualified path in expression or qualified range pattern bound
+	AST::QualifiedPathInExpression path
+	  = parse_qualified_path_in_expression ();
+
+	if (lexer.peek_token ()->get_id () == DOT_DOT_EQ
+	    || lexer.peek_token ()->get_id () == ELLIPSIS)
+	  {
+	    // qualified range pattern bound, so parse rest of range pattern
+	    bool has_ellipsis_syntax
+	      = lexer.peek_token ()->get_id () == ELLIPSIS;
+	    lexer.skip_token ();
+
+	    std::unique_ptr<AST::RangePatternBoundQualPath> lower_bound (
+	      new AST::RangePatternBoundQualPath (std::move (path)));
+	    std::unique_ptr<AST::RangePatternBound> upper_bound
+	      = parse_range_pattern_bound ();
+
+	    return std::unique_ptr<AST::RangePattern> (
+	      new AST::RangePattern (std::move (lower_bound),
+				     std::move (upper_bound), t->get_locus (),
+				     has_ellipsis_syntax));
+	  }
+	else
+	  {
+	    // just qualified path in expression
+	    return std::unique_ptr<AST::QualifiedPathInExpression> (
+	      new AST::QualifiedPathInExpression (std::move (path)));
+	  }
+      }
+    case SUPER:
+    case SELF:
+    case SELF_ALIAS:
+    case CRATE:
+    case SCOPE_RESOLUTION:
+      case DOLLAR_SIGN: {
+	// path in expression or range pattern bound
+	AST::PathInExpression path = parse_path_in_expression ();
+
+	const_TokenPtr next = lexer.peek_token ();
+	switch (next->get_id ())
+	  {
+	  case DOT_DOT_EQ:
+	    case ELLIPSIS: {
+	      // qualified range pattern bound, so parse rest of range pattern
+	      bool has_ellipsis_syntax
+		= lexer.peek_token ()->get_id () == ELLIPSIS;
+	      lexer.skip_token ();
+
+	      std::unique_ptr<AST::RangePatternBoundPath> lower_bound (
+		new AST::RangePatternBoundPath (std::move (path)));
+	      std::unique_ptr<AST::RangePatternBound> upper_bound
+		= parse_range_pattern_bound ();
+
+	      return std::unique_ptr<AST::RangePattern> (new AST::RangePattern (
+		std::move (lower_bound), std::move (upper_bound),
+		Linemap::unknown_location (), has_ellipsis_syntax));
+	    }
+	  case EXCLAM:
+	    return parse_macro_invocation_partial (std::move (path),
+						   AST::AttrVec ());
+	    case LEFT_PAREN: {
+	      // tuple struct
+	      lexer.skip_token ();
+
+	      // check if empty tuple
+	      if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
+		{
+		  lexer.skip_token ();
+		  return std::unique_ptr<AST::TupleStructPattern> (
+		    new AST::TupleStructPattern (std::move (path), nullptr));
+		}
+
+	      // parse items
+	      std::unique_ptr<AST::TupleStructItems> items
+		= parse_tuple_struct_items ();
+	      if (items == nullptr)
+		{
+		  Error error (lexer.peek_token ()->get_locus (),
+			       "failed to parse tuple struct items");
+		  add_error (std::move (error));
+
+		  return nullptr;
+		}
+
+	      if (!skip_token (RIGHT_PAREN))
+		{
+		  return nullptr;
+		}
+
+	      return std::unique_ptr<AST::TupleStructPattern> (
+		new AST::TupleStructPattern (std::move (path),
+					     std::move (items)));
+	    }
+	    case LEFT_CURLY: {
+	      // struct
+	      lexer.skip_token ();
+
+	      // parse elements (optional)
+	      AST::StructPatternElements elems = parse_struct_pattern_elems ();
+
+	      if (!skip_token (RIGHT_CURLY))
+		{
+		  return nullptr;
+		}
+
+	      return std::unique_ptr<AST::StructPattern> (
+		new AST::StructPattern (std::move (path), t->get_locus (),
+					std::move (elems)));
+	    }
+	  default:
+	    // assume path in expression
+	    return std::unique_ptr<AST::PathInExpression> (
+	      new AST::PathInExpression (std::move (path)));
+	  }
+      }
+    default:
+      add_error (Error (t->get_locus (), "unexpected token %qs in pattern",
+			t->get_token_description ()));
+
+      return nullptr;
+    }
+}
+
+// Parses a single or double reference pattern.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ReferencePattern>
+Parser<ManagedTokenSource>::parse_reference_pattern ()
+{
+  // parse double or single ref
+  bool is_double_ref = false;
+  const_TokenPtr t = lexer.peek_token ();
+  switch (t->get_id ())
+    {
+    case AMP:
+      // still false
+      lexer.skip_token ();
+      break;
+    case LOGICAL_AND:
+      is_double_ref = true;
+      lexer.skip_token ();
+      break;
+    default:
+      add_error (Error (t->get_locus (),
+			"unexpected token %qs in reference pattern",
+			t->get_token_description ()));
+
+      return nullptr;
+    }
+
+  // parse mut (if it exists)
+  bool is_mut = false;
+  if (lexer.peek_token ()->get_id () == MUT)
+    {
+      is_mut = true;
+      lexer.skip_token ();
+    }
+
+  // parse pattern to get reference of (required)
+  std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
+  if (pattern == nullptr)
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "failed to parse pattern in reference pattern");
+      add_error (std::move (error));
+
+      // skip somewhere?
+      return nullptr;
+    }
+
+  return std::unique_ptr<AST::ReferencePattern> (
+    new AST::ReferencePattern (std::move (pattern), is_mut, is_double_ref,
+			       t->get_locus ()));
+}
+
+/* Parses a grouped pattern or tuple pattern. Prefers grouped over tuple if
+ * only a single element with no commas. */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::Pattern>
+Parser<ManagedTokenSource>::parse_grouped_or_tuple_pattern ()
+{
+  Location paren_locus = lexer.peek_token ()->get_locus ();
+  skip_token (LEFT_PAREN);
+
+  // detect '..' token (ranged with no lower range)
+  if (lexer.peek_token ()->get_id () == DOT_DOT)
+    {
+      lexer.skip_token ();
+
+      // parse new patterns while next token is a comma
+      std::vector<std::unique_ptr<AST::Pattern>> patterns;
+
+      const_TokenPtr t = lexer.peek_token ();
+      while (t->get_id () == COMMA)
+	{
+	  lexer.skip_token ();
+
+	  // break if next token is ')'
+	  if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
+	    {
+	      break;
+	    }
+
+	  // parse pattern, which is required
+	  std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
+	  if (pattern == nullptr)
+	    {
+	      Error error (
+		lexer.peek_token ()->get_locus (),
+		"failed to parse pattern inside ranged tuple pattern");
+	      add_error (std::move (error));
+
+	      // skip somewhere?
+	      return nullptr;
+	    }
+	  patterns.push_back (std::move (pattern));
+
+	  t = lexer.peek_token ();
+	}
+
+      if (!skip_token (RIGHT_PAREN))
+	{
+	  // skip somewhere?
+	  return nullptr;
+	}
+
+      // create ranged tuple pattern items with only upper items
+      std::unique_ptr<AST::TuplePatternItemsRanged> items (
+	new AST::TuplePatternItemsRanged (
+	  std::vector<std::unique_ptr<AST::Pattern>> (), std::move (patterns)));
+      return std::unique_ptr<AST::TuplePattern> (
+	new AST::TuplePattern (std::move (items), paren_locus));
+    }
+
+  // parse initial pattern (required)
+  std::unique_ptr<AST::Pattern> initial_pattern = parse_pattern ();
+  if (initial_pattern == nullptr)
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "failed to parse pattern in grouped or tuple pattern");
+      add_error (std::move (error));
+
+      return nullptr;
+    }
+
+  // branch on whether next token is a comma or not
+  const_TokenPtr t = lexer.peek_token ();
+  switch (t->get_id ())
+    {
+    case RIGHT_PAREN:
+      // grouped pattern
+      lexer.skip_token ();
+
+      return std::unique_ptr<AST::GroupedPattern> (
+	new AST::GroupedPattern (std::move (initial_pattern), paren_locus));
+      case COMMA: {
+	// tuple pattern
+	lexer.skip_token ();
+
+	// create vector of patterns
+	std::vector<std::unique_ptr<AST::Pattern>> patterns;
+	patterns.push_back (std::move (initial_pattern));
+
+	t = lexer.peek_token ();
+	while (t->get_id () != RIGHT_PAREN && t->get_id () != DOT_DOT)
+	  {
+	    // parse pattern (required)
+	    std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
+	    if (pattern == nullptr)
+	      {
+		Error error (t->get_locus (),
+			     "failed to parse pattern in tuple pattern");
+		add_error (std::move (error));
+
+		return nullptr;
+	      }
+	    patterns.push_back (std::move (pattern));
+
+	    if (lexer.peek_token ()->get_id () != COMMA)
+	      break;
+
+	    lexer.skip_token ();
+	    t = lexer.peek_token ();
+	  }
+
+	t = lexer.peek_token ();
+	if (t->get_id () == RIGHT_PAREN)
+	  {
+	    // non-ranged tuple pattern
+	    lexer.skip_token ();
+
+	    std::unique_ptr<AST::TuplePatternItemsMultiple> items (
+	      new AST::TuplePatternItemsMultiple (std::move (patterns)));
+	    return std::unique_ptr<AST::TuplePattern> (
+	      new AST::TuplePattern (std::move (items), paren_locus));
+	  }
+	else if (t->get_id () == DOT_DOT)
+	  {
+	    // ranged tuple pattern
+	    lexer.skip_token ();
+
+	    // parse upper patterns
+	    std::vector<std::unique_ptr<AST::Pattern>> upper_patterns;
+	    t = lexer.peek_token ();
+	    while (t->get_id () == COMMA)
+	      {
+		lexer.skip_token ();
+
+		// break if end
+		if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
+		  break;
+
+		// parse pattern (required)
+		std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
+		if (pattern == nullptr)
+		  {
+		    Error error (lexer.peek_token ()->get_locus (),
+				 "failed to parse pattern in tuple pattern");
+		    add_error (std::move (error));
+
+		    return nullptr;
+		  }
+		upper_patterns.push_back (std::move (pattern));
+
+		t = lexer.peek_token ();
+	      }
+
+	    if (!skip_token (RIGHT_PAREN))
+	      {
+		return nullptr;
+	      }
+
+	    std::unique_ptr<AST::TuplePatternItemsRanged> items (
+	      new AST::TuplePatternItemsRanged (std::move (patterns),
+						std::move (upper_patterns)));
+	    return std::unique_ptr<AST::TuplePattern> (
+	      new AST::TuplePattern (std::move (items), paren_locus));
+	  }
+	else
+	  {
+	    // some kind of error
+	    Error error (t->get_locus (),
+			 "failed to parse tuple pattern (probably) or maybe "
+			 "grouped pattern");
+	    add_error (std::move (error));
+
+	    return nullptr;
+	  }
+      }
+    default:
+      // error
+      add_error (Error (t->get_locus (),
+			"unrecognised token %qs in grouped or tuple pattern "
+			"after first pattern",
+			t->get_token_description ()));
+
+      return nullptr;
+    }
+}
+
+/* Parses a slice pattern that can match arrays or slices. Parses the square
+ * brackets too. */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::SlicePattern>
+Parser<ManagedTokenSource>::parse_slice_pattern ()
+{
+  Location square_locus = lexer.peek_token ()->get_locus ();
+  skip_token (LEFT_SQUARE);
+
+  // parse initial pattern (required)
+  std::unique_ptr<AST::Pattern> initial_pattern = parse_pattern ();
+  if (initial_pattern == nullptr)
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "failed to parse initial pattern in slice pattern");
+      add_error (std::move (error));
+
+      return nullptr;
+    }
+
+  std::vector<std::unique_ptr<AST::Pattern>> patterns;
+  patterns.push_back (std::move (initial_pattern));
+
+  const_TokenPtr t = lexer.peek_token ();
+  while (t->get_id () == COMMA)
+    {
+      lexer.skip_token ();
+
+      // break if end bracket
+      if (lexer.peek_token ()->get_id () == RIGHT_SQUARE)
+	break;
+
+      // parse pattern (required)
+      std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
+      if (pattern == nullptr)
+	{
+	  Error error (lexer.peek_token ()->get_locus (),
+		       "failed to parse pattern in slice pattern");
+	  add_error (std::move (error));
+
+	  return nullptr;
+	}
+      patterns.push_back (std::move (pattern));
+
+      t = lexer.peek_token ();
+    }
+
+  if (!skip_token (RIGHT_SQUARE))
+    {
+      return nullptr;
+    }
+
+  return std::unique_ptr<AST::SlicePattern> (
+    new AST::SlicePattern (std::move (patterns), square_locus));
+}
+
+/* Parses an identifier pattern (pattern that binds a value matched to a
+ * variable). */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::IdentifierPattern>
+Parser<ManagedTokenSource>::parse_identifier_pattern ()
+{
+  Location locus = lexer.peek_token ()->get_locus ();
+
+  bool has_ref = false;
+  if (lexer.peek_token ()->get_id () == REF)
+    {
+      has_ref = true;
+      lexer.skip_token ();
+
+      // DEBUG
+      rust_debug ("parsed ref in identifier pattern");
+    }
+
+  bool has_mut = false;
+  if (lexer.peek_token ()->get_id () == MUT)
+    {
+      has_mut = true;
+      lexer.skip_token ();
+    }
+
+  // parse identifier (required)
+  const_TokenPtr ident_tok = expect_token (IDENTIFIER);
+  if (ident_tok == nullptr)
+    {
+      // skip somewhere?
+      return nullptr;
+    }
+  Identifier ident = ident_tok->get_str ();
+
+  // DEBUG
+  rust_debug ("parsed identifier in identifier pattern");
+
+  // parse optional pattern binding thing
+  std::unique_ptr<AST::Pattern> bind_pattern = nullptr;
+  if (lexer.peek_token ()->get_id () == PATTERN_BIND)
+    {
+      lexer.skip_token ();
+
+      // parse required pattern to bind
+      bind_pattern = parse_pattern ();
+      if (bind_pattern == nullptr)
+	{
+	  Error error (lexer.peek_token ()->get_locus (),
+		       "failed to parse pattern to bind in identifier pattern");
+	  add_error (std::move (error));
+
+	  return nullptr;
+	}
+    }
+
+  // DEBUG
+  rust_debug ("about to return identifier pattern");
+
+  return std::unique_ptr<AST::IdentifierPattern> (
+    new AST::IdentifierPattern (std::move (ident), locus, has_ref, has_mut,
+				std::move (bind_pattern)));
+}
+
+/* Parses a pattern that opens with an identifier. This includes identifier
+ * patterns, path patterns (and derivatives such as struct patterns, tuple
+ * struct patterns, and macro invocations), and ranges. */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::Pattern>
+Parser<ManagedTokenSource>::parse_ident_leading_pattern ()
+{
+  // ensure first token is actually identifier
+  const_TokenPtr initial_tok = lexer.peek_token ();
+  if (initial_tok->get_id () != IDENTIFIER)
+    {
+      return nullptr;
+    }
+
+  // save initial identifier as it may be useful (but don't skip)
+  std::string initial_ident = initial_tok->get_str ();
+
+  // parse next tokens as a PathInExpression
+  AST::PathInExpression path = parse_path_in_expression ();
+
+  // branch on next token
+  const_TokenPtr t = lexer.peek_token ();
+  switch (t->get_id ())
+    {
+    case EXCLAM:
+      return parse_macro_invocation_partial (std::move (path), AST::AttrVec ());
+      case LEFT_PAREN: {
+	// tuple struct
+	lexer.skip_token ();
+
+	// DEBUG
+	rust_debug ("parsing tuple struct pattern");
+
+	// check if empty tuple
+	if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
+	  {
+	    lexer.skip_token ();
+	    return std::unique_ptr<AST::TupleStructPattern> (
+	      new AST::TupleStructPattern (std::move (path), nullptr));
+	  }
+
+	// parse items
+	std::unique_ptr<AST::TupleStructItems> items
+	  = parse_tuple_struct_items ();
+	if (items == nullptr)
+	  {
+	    Error error (lexer.peek_token ()->get_locus (),
+			 "failed to parse tuple struct items");
+	    add_error (std::move (error));
+
+	    return nullptr;
+	  }
+
+	// DEBUG
+	rust_debug ("successfully parsed tuple struct items");
+
+	if (!skip_token (RIGHT_PAREN))
+	  {
+	    return nullptr;
+	  }
+
+	// DEBUG
+	rust_debug ("successfully parsed tuple struct pattern");
+
+	return std::unique_ptr<AST::TupleStructPattern> (
+	  new AST::TupleStructPattern (std::move (path), std::move (items)));
+      }
+      case LEFT_CURLY: {
+	// struct
+	lexer.skip_token ();
+
+	// parse elements (optional)
+	AST::StructPatternElements elems = parse_struct_pattern_elems ();
+
+	if (!skip_token (RIGHT_CURLY))
+	  {
+	    return nullptr;
+	  }
+
+	// DEBUG
+	rust_debug ("successfully parsed struct pattern");
+
+	return std::unique_ptr<AST::StructPattern> (
+	  new AST::StructPattern (std::move (path), initial_tok->get_locus (),
+				  std::move (elems)));
+      }
+    case DOT_DOT_EQ:
+      case ELLIPSIS: {
+	// range
+	bool has_ellipsis_syntax = lexer.peek_token ()->get_id () == ELLIPSIS;
+
+	lexer.skip_token ();
+
+	std::unique_ptr<AST::RangePatternBoundPath> lower_bound (
+	  new AST::RangePatternBoundPath (std::move (path)));
+	std::unique_ptr<AST::RangePatternBound> upper_bound
+	  = parse_range_pattern_bound ();
+
+	return std::unique_ptr<AST::RangePattern> (new AST::RangePattern (
+	  std::move (lower_bound), std::move (upper_bound),
+	  Linemap::unknown_location (), has_ellipsis_syntax));
+      }
+      case PATTERN_BIND: {
+	// only allow on single-segment paths
+	if (path.is_single_segment ())
+	  {
+	    // identifier with pattern bind
+	    lexer.skip_token ();
+
+	    std::unique_ptr<AST::Pattern> bind_pattern = parse_pattern ();
+	    if (bind_pattern == nullptr)
+	      {
+		Error error (
+		  t->get_locus (),
+		  "failed to parse pattern to bind to identifier pattern");
+		add_error (std::move (error));
+
+		return nullptr;
+	      }
+	    return std::unique_ptr<AST::IdentifierPattern> (
+	      new AST::IdentifierPattern (std::move (initial_ident),
+					  initial_tok->get_locus (), false,
+					  false, std::move (bind_pattern)));
+	  }
+	Error error (
+	  t->get_locus (),
+	  "failed to parse pattern bind to a path, not an identifier");
+	add_error (std::move (error));
+
+	return nullptr;
+      }
+    default:
+      // assume identifier if single segment
+      if (path.is_single_segment ())
+	{
+	  return std::unique_ptr<AST::IdentifierPattern> (
+	    new AST::IdentifierPattern (std::move (initial_ident),
+					initial_tok->get_locus ()));
+	}
+      // return path otherwise
+      return std::unique_ptr<AST::PathInExpression> (
+	new AST::PathInExpression (std::move (path)));
+    }
+}
+
+// Parses tuple struct items if they exist. Does not parse parentheses.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::TupleStructItems>
+Parser<ManagedTokenSource>::parse_tuple_struct_items ()
+{
+  std::vector<std::unique_ptr<AST::Pattern>> lower_patterns;
+
+  // DEBUG
+  rust_debug ("started parsing tuple struct items");
+
+  // check for '..' at front
+  if (lexer.peek_token ()->get_id () == DOT_DOT)
+    {
+      // only parse upper patterns
+      lexer.skip_token ();
+
+      // DEBUG
+      rust_debug ("'..' at front in tuple struct items detected");
+
+      std::vector<std::unique_ptr<AST::Pattern>> upper_patterns;
+
+      const_TokenPtr t = lexer.peek_token ();
+      while (t->get_id () == COMMA)
+	{
+	  lexer.skip_token ();
+
+	  // break if right paren
+	  if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
+	    break;
+
+	  // parse pattern, which is now required
+	  std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
+	  if (pattern == nullptr)
+	    {
+	      Error error (lexer.peek_token ()->get_locus (),
+			   "failed to parse pattern in tuple struct items");
+	      add_error (std::move (error));
+
+	      return nullptr;
+	    }
+	  upper_patterns.push_back (std::move (pattern));
+
+	  t = lexer.peek_token ();
+	}
+
+      // DEBUG
+      rust_debug (
+	"finished parsing tuple struct items ranged (upper/none only)");
+
+      return std::unique_ptr<AST::TupleStructItemsRange> (
+	new AST::TupleStructItemsRange (std::move (lower_patterns),
+					std::move (upper_patterns)));
+    }
+
+  // has at least some lower patterns
+  const_TokenPtr t = lexer.peek_token ();
+  while (t->get_id () != RIGHT_PAREN && t->get_id () != DOT_DOT)
+    {
+      // DEBUG
+      rust_debug ("about to parse pattern in tuple struct items");
+
+      // parse pattern, which is required
+      std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
+      if (pattern == nullptr)
+	{
+	  Error error (t->get_locus (),
+		       "failed to parse pattern in tuple struct items");
+	  add_error (std::move (error));
+
+	  return nullptr;
+	}
+      lower_patterns.push_back (std::move (pattern));
+
+      // DEBUG
+      rust_debug ("successfully parsed pattern in tuple struct items");
+
+      if (lexer.peek_token ()->get_id () != COMMA)
+	{
+	  // DEBUG
+	  rust_debug ("broke out of parsing patterns in tuple struct "
+		      "items as no comma");
+
+	  break;
+	}
+      lexer.skip_token ();
+      t = lexer.peek_token ();
+    }
+
+  // branch on next token
+  t = lexer.peek_token ();
+  switch (t->get_id ())
+    {
+    case RIGHT_PAREN:
+      return std::unique_ptr<AST::TupleStructItemsNoRange> (
+	new AST::TupleStructItemsNoRange (std::move (lower_patterns)));
+      case DOT_DOT: {
+	// has an upper range that must be parsed separately
+	lexer.skip_token ();
+
+	std::vector<std::unique_ptr<AST::Pattern>> upper_patterns;
+
+	t = lexer.peek_token ();
+	while (t->get_id () == COMMA)
+	  {
+	    lexer.skip_token ();
+
+	    // break if next token is right paren
+	    if (lexer.peek_token ()->get_id () == RIGHT_PAREN)
+	      break;
+
+	    // parse pattern, which is required
+	    std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
+	    if (pattern == nullptr)
+	      {
+		Error error (lexer.peek_token ()->get_locus (),
+			     "failed to parse pattern in tuple struct items");
+		add_error (std::move (error));
+
+		return nullptr;
+	      }
+	    upper_patterns.push_back (std::move (pattern));
+
+	    t = lexer.peek_token ();
+	  }
+
+	return std::unique_ptr<AST::TupleStructItemsRange> (
+	  new AST::TupleStructItemsRange (std::move (lower_patterns),
+					  std::move (upper_patterns)));
+      }
+    default:
+      // error
+      add_error (Error (t->get_locus (),
+			"unexpected token %qs in tuple struct items",
+			t->get_token_description ()));
+
+      return nullptr;
+    }
+}
+
+// Parses struct pattern elements if they exist.
+template <typename ManagedTokenSource>
+AST::StructPatternElements
+Parser<ManagedTokenSource>::parse_struct_pattern_elems ()
+{
+  std::vector<std::unique_ptr<AST::StructPatternField>> fields;
+
+  AST::AttrVec etc_attrs;
+  bool has_etc = false;
+
+  // try parsing struct pattern fields
+  const_TokenPtr t = lexer.peek_token ();
+  while (t->get_id () != RIGHT_CURLY)
+    {
+      AST::AttrVec outer_attrs = parse_outer_attributes ();
+
+      // parse etc (must be last in struct pattern, so breaks)
+      if (lexer.peek_token ()->get_id () == DOT_DOT)
+	{
+	  lexer.skip_token ();
+	  etc_attrs = std::move (outer_attrs);
+	  has_etc = true;
+	  break;
+	}
+
+      std::unique_ptr<AST::StructPatternField> field
+	= parse_struct_pattern_field_partial (std::move (outer_attrs));
+      if (field == nullptr)
+	{
+	  Error error (lexer.peek_token ()->get_locus (),
+		       "failed to parse struct pattern field");
+	  add_error (std::move (error));
+
+	  // skip after somewhere?
+	  return AST::StructPatternElements::create_empty ();
+	}
+      fields.push_back (std::move (field));
+
+      if (lexer.peek_token ()->get_id () != COMMA)
+	break;
+
+      // skip comma
+      lexer.skip_token ();
+      t = lexer.peek_token ();
+    }
+
+  if (has_etc)
+    return AST::StructPatternElements (std::move (fields),
+				       std::move (etc_attrs));
+  else
+    return AST::StructPatternElements (std::move (fields));
+}
+
+/* Parses a struct pattern field (tuple index/pattern, identifier/pattern, or
+ * identifier). */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::StructPatternField>
+Parser<ManagedTokenSource>::parse_struct_pattern_field ()
+{
+  // parse outer attributes (if they exist)
+  AST::AttrVec outer_attrs = parse_outer_attributes ();
+
+  return parse_struct_pattern_field_partial (std::move (outer_attrs));
+}
+
+/* Parses a struct pattern field (tuple index/pattern, identifier/pattern, or
+ * identifier), with outer attributes passed in. */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::StructPatternField>
+Parser<ManagedTokenSource>::parse_struct_pattern_field_partial (
+  AST::AttrVec outer_attrs)
+{
+  // branch based on next token
+  const_TokenPtr t = lexer.peek_token ();
+  switch (t->get_id ())
+    {
+      case INT_LITERAL: {
+	// tuple index
+	std::string index_str = t->get_str ();
+	int index = atoi (index_str.c_str ());
+
+	if (!skip_token (COLON))
+	  {
+	    return nullptr;
+	  }
+
+	// parse required pattern
+	std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
+	if (pattern == nullptr)
+	  {
+	    Error error (
+	      t->get_locus (),
+	      "failed to parse pattern in tuple index struct pattern field");
+	    add_error (std::move (error));
+
+	    return nullptr;
+	  }
+
+	return std::unique_ptr<AST::StructPatternFieldTuplePat> (
+	  new AST::StructPatternFieldTuplePat (index, std::move (pattern),
+					       std::move (outer_attrs),
+					       t->get_locus ()));
+      }
+    case IDENTIFIER:
+      // identifier-pattern OR only identifier
+      // branch on next token
+      switch (lexer.peek_token (1)->get_id ())
+	{
+	  case COLON: {
+	    // identifier-pattern
+	    Identifier ident = t->get_str ();
+	    lexer.skip_token ();
+
+	    skip_token (COLON);
+
+	    // parse required pattern
+	    std::unique_ptr<AST::Pattern> pattern = parse_pattern ();
+	    if (pattern == nullptr)
+	      {
+		Error error (t->get_locus (),
+			     "failed to parse pattern in struct pattern field");
+		add_error (std::move (error));
+
+		return nullptr;
+	      }
+
+	    return std::unique_ptr<AST::StructPatternFieldIdentPat> (
+	      new AST::StructPatternFieldIdentPat (std::move (ident),
+						   std::move (pattern),
+						   std::move (outer_attrs),
+						   t->get_locus ()));
+	  }
+	case COMMA:
+	  case RIGHT_CURLY: {
+	    // identifier only
+	    Identifier ident = t->get_str ();
+	    lexer.skip_token ();
+
+	    return std::unique_ptr<AST::StructPatternFieldIdent> (
+	      new AST::StructPatternFieldIdent (std::move (ident), false, false,
+						std::move (outer_attrs),
+						t->get_locus ()));
+	  }
+	default:
+	  // error
+	  add_error (Error (t->get_locus (),
+			    "unrecognised token %qs in struct pattern field",
+			    t->get_token_description ()));
+
+	  return nullptr;
+	}
+    case REF:
+      case MUT: {
+	// only identifier
+	bool has_ref = false;
+	if (t->get_id () == REF)
+	  {
+	    has_ref = true;
+	    lexer.skip_token ();
+	  }
+
+	bool has_mut = false;
+	if (lexer.peek_token ()->get_id () == MUT)
+	  {
+	    has_mut = true;
+	    lexer.skip_token ();
+	  }
+
+	const_TokenPtr ident_tok = expect_token (IDENTIFIER);
+	if (ident_tok == nullptr)
+	  {
+	    return nullptr;
+	  }
+	Identifier ident = ident_tok->get_str ();
+
+	return std::unique_ptr<AST::StructPatternFieldIdent> (
+	  new AST::StructPatternFieldIdent (std::move (ident), has_ref, has_mut,
+					    std::move (outer_attrs),
+					    t->get_locus ()));
+      }
+    default:
+      // not necessarily an error
+      return nullptr;
+    }
+}
+
+template <typename ManagedTokenSource>
+ExprOrStmt
+Parser<ManagedTokenSource>::parse_stmt_or_expr_with_block (
+  AST::AttrVec outer_attrs)
+{
+  auto expr = parse_expr_with_block (std::move (outer_attrs));
+  if (expr == nullptr)
+    return ExprOrStmt::create_error ();
+
+  auto tok = lexer.peek_token ();
+
+  // tail expr in a block expr
+  if (tok->get_id () == RIGHT_CURLY)
+    return ExprOrStmt (std::move (expr));
+
+  // internal block expr must either have semicolons followed, or evaluate to
+  // ()
+  auto locus = expr->get_locus ();
+  std::unique_ptr<AST::ExprStmtWithBlock> stmt (
+    new AST::ExprStmtWithBlock (std::move (expr), locus,
+				tok->get_id () == SEMICOLON));
+  if (tok->get_id () == SEMICOLON)
+    lexer.skip_token ();
+
+  return ExprOrStmt (std::move (stmt));
+}
+
+/* Parses a statement or expression (depending on whether a trailing semicolon
+ * exists). Useful for block expressions where it cannot be determined through
+ * lookahead whether it is a statement or expression to be parsed. */
+template <typename ManagedTokenSource>
+ExprOrStmt
+Parser<ManagedTokenSource>::parse_stmt_or_expr_without_block ()
+{
+  // quick exit for empty statement
+  const_TokenPtr t = lexer.peek_token ();
+  if (t->get_id () == SEMICOLON)
+    {
+      lexer.skip_token ();
+      std::unique_ptr<AST::EmptyStmt> stmt (
+	new AST::EmptyStmt (t->get_locus ()));
+      return ExprOrStmt (std::move (stmt));
+    }
+
+  // parse outer attributes
+  AST::AttrVec outer_attrs = parse_outer_attributes ();
+
+  // parsing this will be annoying because of the many different possibilities
+  /* best may be just to copy paste in parse_item switch, and failing that try
+   * to parse outer attributes, and then pass them in to either a let
+   * statement or (fallback) expression statement. */
+  // FIXME: think of a way to do this without such a large switch?
+
+  /* FIXME: for expressions at least, the only way that they can really be
+   * parsed properly in this way is if they don't support operators on them.
+   * They must be pratt-parsed otherwise. As such due to composability, only
+   * explicit statements will have special cases here. This should roughly
+   * correspond to "expr-with-block", but this warning is here in case it
+   * isn't the case. */
+  t = lexer.peek_token ();
+  switch (t->get_id ())
+    {
+      case LET: {
+	// let statement
+	std::unique_ptr<AST::LetStmt> stmt (
+	  parse_let_stmt (std::move (outer_attrs)));
+	return ExprOrStmt (std::move (stmt));
+      }
+    case PUB:
+    case MOD:
+    case EXTERN_TOK:
+    case USE:
+    case FN_TOK:
+    case TYPE:
+    case STRUCT_TOK:
+    case ENUM_TOK:
+    case CONST:
+    case STATIC_TOK:
+    case TRAIT:
+      case IMPL: {
+	std::unique_ptr<AST::VisItem> item (
+	  parse_vis_item (std::move (outer_attrs)));
+	return ExprOrStmt (std::move (item));
+      }
+      /* TODO: implement union keyword but not really because of
+       * context-dependence crappy hack way to parse a union written below to
+       * separate it from the good code. */
+      // case UNION:
+      case UNSAFE: { // maybe - unsafe traits are a thing
+	/* if any of these (should be all possible VisItem prefixes), parse a
+	 * VisItem - can't parse item because would require reparsing outer
+	 * attributes */
+	const_TokenPtr t2 = lexer.peek_token (1);
+	switch (t2->get_id ())
+	  {
+	    case LEFT_CURLY: {
+	      // unsafe block
+	      return parse_stmt_or_expr_with_block (std::move (outer_attrs));
+	    }
+	    case TRAIT: {
+	      // unsafe trait
+	      std::unique_ptr<AST::VisItem> item (
+		parse_vis_item (std::move (outer_attrs)));
+	      return ExprOrStmt (std::move (item));
+	    }
+	  case EXTERN_TOK:
+	    case FN_TOK: {
+	      // unsafe function
+	      std::unique_ptr<AST::VisItem> item (
+		parse_vis_item (std::move (outer_attrs)));
+	      return ExprOrStmt (std::move (item));
+	    }
+	    case IMPL: {
+	      // unsafe trait impl
+	      std::unique_ptr<AST::VisItem> item (
+		parse_vis_item (std::move (outer_attrs)));
+	      return ExprOrStmt (std::move (item));
+	    }
+	  default:
+	    add_error (Error (t2->get_locus (),
+			      "unrecognised token %qs after parsing unsafe - "
+			      "expected beginning of expression or statement",
+			      t->get_token_description ()));
+
+	    // skip somewhere?
+	    return ExprOrStmt::create_error ();
+	  }
+      }
+    case SUPER:
+    case SELF:
+    case CRATE:
+      case DOLLAR_SIGN: {
+	/* path-based thing so struct/enum or path or macro invocation of a
+	 * kind. however, the expressions are composable (i think) */
+
+	std::unique_ptr<AST::ExprWithoutBlock> expr
+	  = parse_expr_without_block ();
+
+	if (lexer.peek_token ()->get_id () == SEMICOLON)
+	  {
+	    // must be expression statement
+	    lexer.skip_token ();
+
+	    std::unique_ptr<AST::ExprStmtWithoutBlock> stmt (
+	      new AST::ExprStmtWithoutBlock (std::move (expr),
+					     t->get_locus ()));
+	    return ExprOrStmt (std::move (stmt));
+	  }
+
+	// return expression
+	return ExprOrStmt (std::move (expr));
+      }
+      /* FIXME: this is either a macro invocation or macro invocation semi.
+       * start parsing to determine which one it is. */
+      // FIXME: or this is another path-based thing - struct/enum or path
+      // itself return parse_path_based_stmt_or_expr(std::move(outer_attrs));
+      // FIXME: old code there
+    case LOOP:
+    case WHILE:
+    case FOR:
+    case IF:
+    case MATCH_TOK:
+    case LEFT_CURLY:
+      case ASYNC: {
+	return parse_stmt_or_expr_with_block (std::move (outer_attrs));
+      }
+      case LIFETIME: {
+	/* FIXME: are there any expressions without blocks that can have
+	 * lifetime as their first token? Or is loop expr the only one? */
+	// safe side for now:
+	const_TokenPtr t2 = lexer.peek_token (2);
+	if (lexer.peek_token (1)->get_id () == COLON
+	    && (t2->get_id () == LOOP || t2->get_id () == WHILE
+		|| t2->get_id () == FOR))
+	  {
+	    return parse_stmt_or_expr_with_block (std::move (outer_attrs));
+	  }
+	else
+	  {
+	    // should be expr without block
+	    std::unique_ptr<AST::ExprWithoutBlock> expr
+	      = parse_expr_without_block (std::move (outer_attrs));
+
+	    if (lexer.peek_token ()->get_id () == SEMICOLON)
+	      {
+		// must be expression statement
+		lexer.skip_token ();
+
+		std::unique_ptr<AST::ExprStmtWithoutBlock> stmt (
+		  new AST::ExprStmtWithoutBlock (std::move (expr),
+						 t->get_locus ()));
+		return ExprOrStmt (std::move (stmt));
+	      }
+
+	    // return expression
+	    return ExprOrStmt (std::move (expr));
+	  }
+      }
+    // crappy hack to do union "keyword"
+    case IDENTIFIER:
+      if (t->get_str () == "union"
+	  && lexer.peek_token (1)->get_id () == IDENTIFIER)
+	{
+	  std::unique_ptr<AST::VisItem> item (
+	    parse_vis_item (std::move (outer_attrs)));
+	  return ExprOrStmt (std::move (item));
+	  // or should this go straight to parsing union?
+	}
+      else if (t->get_str () == "macro_rules")
+	{
+	  // macro_rules! macro item
+	  std::unique_ptr<AST::MacroItem> item (
+	    parse_macro_item (std::move (outer_attrs)));
+	  return ExprOrStmt (std::move (item));
+	}
+      else if (lexer.peek_token (1)->get_id () == SCOPE_RESOLUTION
+	       || lexer.peek_token (1)->get_id () == EXCLAM
+	       || lexer.peek_token (1)->get_id () == LEFT_CURLY)
+	{
+	  /* path (probably) or macro invocation or struct or enum, so
+	   * probably a macro invocation semi decide how to parse - probably
+	   * parse path and then get macro from it */
+
+	  // FIXME: old code was good until composability was required
+	  // return parse_path_based_stmt_or_expr(std::move(outer_attrs));
+	  std::unique_ptr<AST::ExprWithoutBlock> expr
+	    = parse_expr_without_block (std::move (outer_attrs));
+
+	  if (lexer.peek_token ()->get_id () == SEMICOLON)
+	    {
+	      // must be expression statement
+	      lexer.skip_token ();
+
+	      std::unique_ptr<AST::ExprStmtWithoutBlock> stmt (
+		new AST::ExprStmtWithoutBlock (std::move (expr),
+					       t->get_locus ()));
+	      return ExprOrStmt (std::move (stmt));
+	    }
+
+	  // return expression
+	  return ExprOrStmt (std::move (expr));
+	}
+      gcc_fallthrough ();
+      // TODO: find out how to disable gcc "implicit fallthrough" warning
+      default: {
+	/* expression statement (without block) or expression itself - parse
+	 * expression then make it statement if semi afterwards */
+
+	std::unique_ptr<AST::ExprWithoutBlock> expr
+	  = parse_expr_without_block (std::move (outer_attrs));
+
+	if (lexer.peek_token ()->get_id () == SEMICOLON)
+	  {
+	    // must be expression statement
+	    lexer.skip_token ();
+
+	    std::unique_ptr<AST::ExprStmtWithoutBlock> stmt (
+	      new AST::ExprStmtWithoutBlock (std::move (expr),
+					     t->get_locus ()));
+	    return ExprOrStmt (std::move (stmt));
+	  }
+
+	// return expression
+	return ExprOrStmt (std::move (expr));
+      }
+    }
+}
+
+/* Parses a statement or expression beginning with a path (i.e. macro,
+ * struct/enum, or path expr) */
+template <typename ManagedTokenSource>
+ExprOrStmt
+Parser<ManagedTokenSource>::parse_path_based_stmt_or_expr (
+  AST::AttrVec outer_attrs)
+{
+  // attempt to parse path
+  Location stmt_or_expr_loc = lexer.peek_token ()->get_locus ();
+  AST::PathInExpression path = parse_path_in_expression ();
+
+  // branch on next token
+  const_TokenPtr t2 = lexer.peek_token ();
+  switch (t2->get_id ())
+    {
+      case EXCLAM: {
+	/* macro invocation or macro invocation semi - depends on whether
+	 * there is a final ';' */
+	// convert path in expr to simple path (as that is used in macros)
+	AST::SimplePath macro_path = path.as_simple_path ();
+	if (macro_path.is_empty ())
+	  {
+	    Error error (t2->get_locus (),
+			 "failed to convert parsed path to simple "
+			 "path (for macro invocation or semi)");
+	    add_error (std::move (error));
+
+	    return ExprOrStmt::create_error ();
+	  }
+
+	// skip exclamation mark
+	lexer.skip_token ();
+
+	const_TokenPtr t3 = lexer.peek_token ();
+	Location tok_tree_loc = t3->get_locus ();
+
+	AST::DelimType type = AST::PARENS;
+	switch (t3->get_id ())
+	  {
+	  case LEFT_PAREN:
+	    type = AST::PARENS;
+	    break;
+	  case LEFT_SQUARE:
+	    type = AST::SQUARE;
+	    break;
+	  case LEFT_CURLY:
+	    type = AST::CURLY;
+	    break;
+	  default:
+	    add_error (
+	      Error (t3->get_locus (),
+		     "unrecognised token %qs in macro invocation - (opening) "
+		     "delimiter expected",
+		     t3->get_token_description ()));
+
+	    return ExprOrStmt::create_error ();
+	  }
+	lexer.skip_token ();
+
+	// parse actual token trees
+	std::vector<std::unique_ptr<AST::TokenTree>> token_trees;
+	auto delim_open
+	  = std::unique_ptr<AST::Token> (new AST::Token (std::move (t3)));
+	token_trees.push_back (std::move (delim_open));
+
+	t3 = lexer.peek_token ();
+	// parse token trees until the initial delimiter token is found again
+	while (!token_id_matches_delims (t3->get_id (), type))
+	  {
+	    std::unique_ptr<AST::TokenTree> tree = parse_token_tree ();
+
+	    if (tree == nullptr)
+	      {
+		Error error (t3->get_locus (),
+			     "failed to parse token tree for macro "
+			     "invocation (or semi) - "
+			     "found %qs",
+			     t3->get_token_description ());
+		add_error (std::move (error));
+
+		return ExprOrStmt::create_error ();
+	      }
+
+	    token_trees.push_back (std::move (tree));
+
+	    t3 = lexer.peek_token ();
+	  }
+
+	auto delim_close
+	  = std::unique_ptr<AST::Token> (new AST::Token (std::move (t3)));
+	token_trees.push_back (std::move (delim_close));
+
+	// parse end delimiters
+	t3 = lexer.peek_token ();
+	if (token_id_matches_delims (t3->get_id (), type))
+	  {
+	    // tokens match opening delimiter, so skip.
+	    lexer.skip_token ();
+
+	    /* with curly bracketed macros, assume it is a macro invocation
+	     * unless a semicolon is explicitly put at the end. this is not
+	     * necessarily true (i.e. context-dependence) and so may have to
+	     * be fixed up via HACKs in semantic analysis (by checking whether
+	     * it is the last elem in the vector). */
+
+	    AST::DelimTokenTree delim_tok_tree (type, std::move (token_trees),
+						tok_tree_loc);
+	    AST::MacroInvocData invoc_data (std::move (macro_path),
+					    std::move (delim_tok_tree));
+
+	    if (lexer.peek_token ()->get_id () == SEMICOLON)
+	      {
+		lexer.skip_token ();
+
+		std::unique_ptr<AST::MacroInvocation> stmt (
+		  new AST::MacroInvocation (std::move (invoc_data),
+					    std::move (outer_attrs),
+					    stmt_or_expr_loc, true));
+		return ExprOrStmt (std::move (stmt));
+	      }
+
+	    // otherwise, create macro invocation
+	    std::unique_ptr<AST::MacroInvocation> expr (
+	      new AST::MacroInvocation (std::move (invoc_data),
+					std::move (outer_attrs),
+					stmt_or_expr_loc, false));
+	    return ExprOrStmt (std::move (expr));
+	  }
+	else
+	  {
+	    // tokens don't match opening delimiters, so produce error
+	    Error error (
+	      t2->get_locus (),
+	      "unexpected token %qs - expecting closing delimiter %qs (for a "
+	      "macro invocation)",
+	      t2->get_token_description (),
+	      (type == AST::PARENS ? ")" : (type == AST::SQUARE ? "]" : "}")));
+	    add_error (std::move (error));
+
+	    return ExprOrStmt::create_error ();
+	  }
+      }
+      case LEFT_CURLY: {
+	/* definitely not a block:
+	 *  path '{' ident ','
+	 *  path '{' ident ':' [anything] ','
+	 *  path '{' ident ':' [not a type]
+	 * otherwise, assume block expr and thus path */
+	bool not_a_block = lexer.peek_token (1)->get_id () == IDENTIFIER
+			   && (lexer.peek_token (2)->get_id () == COMMA
+			       || (lexer.peek_token (2)->get_id () == COLON
+				   && (lexer.peek_token (4)->get_id () == COMMA
+				       || !can_tok_start_type (
+					 lexer.peek_token (3)->get_id ()))));
+	std::unique_ptr<AST::ExprWithoutBlock> expr = nullptr;
+
+	if (not_a_block)
+	  {
+	    /* assume struct expr struct (as struct-enum disambiguation
+	     * requires name lookup) again, make statement if final ';' */
+	    expr = parse_struct_expr_struct_partial (std::move (path),
+						     std::move (outer_attrs));
+	    if (expr == nullptr)
+	      {
+		Error error (t2->get_locus (),
+			     "failed to parse struct expr struct");
+		add_error (std::move (error));
+
+		return ExprOrStmt::create_error ();
+	      }
+	  }
+	else
+	  {
+	    // assume path - make statement if final ';'
+	    // lexer.skip_token();
+
+	    // HACK: add outer attrs to path
+	    path.set_outer_attrs (std::move (outer_attrs));
+	    expr = std::unique_ptr<AST::PathInExpression> (
+	      new AST::PathInExpression (std::move (path)));
+	  }
+
+	// determine if statement if ends with semicolon
+	if (lexer.peek_token ()->get_id () == SEMICOLON)
+	  {
+	    // statement
+	    lexer.skip_token ();
+	    std::unique_ptr<AST::ExprStmtWithoutBlock> stmt (
+	      new AST::ExprStmtWithoutBlock (std::move (expr),
+					     stmt_or_expr_loc));
+	    return ExprOrStmt (std::move (stmt));
+	  }
+
+	// otherwise, expression
+	return ExprOrStmt (std::move (expr));
+      }
+      case LEFT_PAREN: {
+	/* assume struct expr tuple (as struct-enum disambiguation requires
+	 * name lookup) again, make statement if final ';' */
+	std::unique_ptr<AST::CallExpr> struct_expr
+	  = parse_struct_expr_tuple_partial (std::move (path),
+					     std::move (outer_attrs));
+	if (struct_expr == nullptr)
+	  {
+	    Error error (t2->get_locus (), "failed to parse struct expr tuple");
+	    add_error (std::move (error));
+
+	    return ExprOrStmt::create_error ();
+	  }
+
+	// determine if statement if ends with semicolon
+	if (lexer.peek_token ()->get_id () == SEMICOLON)
+	  {
+	    // statement
+	    lexer.skip_token ();
+	    std::unique_ptr<AST::ExprStmtWithoutBlock> stmt (
+	      new AST::ExprStmtWithoutBlock (std::move (struct_expr),
+					     stmt_or_expr_loc));
+	    return ExprOrStmt (std::move (stmt));
+	  }
+
+	// otherwise, expression
+	return ExprOrStmt (std::move (struct_expr));
+      }
+      default: {
+	// assume path - make statement if final ';'
+	// lexer.skip_token();
+
+	// HACK: replace outer attributes in path
+	path.set_outer_attrs (std::move (outer_attrs));
+	std::unique_ptr<AST::PathInExpression> expr (
+	  new AST::PathInExpression (std::move (path)));
+
+	if (lexer.peek_token ()->get_id () == SEMICOLON)
+	  {
+	    lexer.skip_token ();
+
+	    std::unique_ptr<AST::ExprStmtWithoutBlock> stmt (
+	      new AST::ExprStmtWithoutBlock (std::move (expr),
+					     stmt_or_expr_loc));
+	    return ExprOrStmt (std::move (stmt));
+	  }
+
+	return ExprOrStmt (std::move (expr));
+      }
+    }
+}
+
+// Parses a struct expression field.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::StructExprField>
+Parser<ManagedTokenSource>::parse_struct_expr_field ()
+{
+  const_TokenPtr t = lexer.peek_token ();
+  switch (t->get_id ())
+    {
+    case IDENTIFIER:
+      if (lexer.peek_token (1)->get_id () == COLON)
+	{
+	  // struct expr field with identifier and expr
+	  Identifier ident = t->get_str ();
+	  lexer.skip_token (1);
+
+	  // parse expression (required)
+	  std::unique_ptr<AST::Expr> expr = parse_expr ();
+	  if (expr == nullptr)
+	    {
+	      Error error (t->get_locus (),
+			   "failed to parse struct expression field with "
+			   "identifier and expression");
+	      add_error (std::move (error));
+
+	      return nullptr;
+	    }
+
+	  return std::unique_ptr<AST::StructExprFieldIdentifierValue> (
+	    new AST::StructExprFieldIdentifierValue (std::move (ident),
+						     std::move (expr),
+						     t->get_locus ()));
+	}
+      else
+	{
+	  // struct expr field with identifier only
+	  Identifier ident = t->get_str ();
+	  lexer.skip_token ();
+
+	  return std::unique_ptr<AST::StructExprFieldIdentifier> (
+	    new AST::StructExprFieldIdentifier (std::move (ident),
+						t->get_locus ()));
+	}
+      case INT_LITERAL: {
+	// parse tuple index field
+	int index = atoi (t->get_str ().c_str ());
+	lexer.skip_token ();
+
+	if (!skip_token (COLON))
+	  {
+	    // skip somewhere?
+	    return nullptr;
+	  }
+
+	// parse field expression (required)
+	std::unique_ptr<AST::Expr> expr = parse_expr ();
+	if (expr == nullptr)
+	  {
+	    Error error (t->get_locus (),
+			 "failed to parse expr in struct (or enum) expr "
+			 "field with tuple index");
+	    add_error (std::move (error));
+
+	    return nullptr;
+	  }
+
+	return std::unique_ptr<AST::StructExprFieldIndexValue> (
+	  new AST::StructExprFieldIndexValue (index, std::move (expr),
+					      t->get_locus ()));
+      }
+    case DOT_DOT:
+      /* this is a struct base and can't be parsed here, so just return
+       * nothing without erroring */
+
+      return nullptr;
+    default:
+      add_error (
+	Error (t->get_locus (),
+	       "unrecognised token %qs as first token of struct expr field - "
+	       "expected identifier or integer literal",
+	       t->get_token_description ()));
+
+      return nullptr;
+    }
+}
+
+// Parses a macro invocation or macro invocation semi.
+template <typename ManagedTokenSource>
+ExprOrStmt
+Parser<ManagedTokenSource>::parse_macro_invocation_maybe_semi (
+  AST::AttrVec outer_attrs)
+{
+  Location macro_locus = lexer.peek_token ()->get_locus ();
+  AST::SimplePath macro_path = parse_simple_path ();
+  if (macro_path.is_empty ())
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "failed to parse simple path in macro invocation or semi");
+      add_error (std::move (error));
+
+      return ExprOrStmt::create_error ();
+    }
+
+  if (!skip_token (EXCLAM))
+    {
+      return ExprOrStmt::create_error ();
+    }
+
+  const_TokenPtr t3 = lexer.peek_token ();
+  Location tok_tree_loc = t3->get_locus ();
+
+  AST::DelimType type = AST::PARENS;
+  switch (t3->get_id ())
+    {
+    case LEFT_PAREN:
+      type = AST::PARENS;
+      break;
+    case LEFT_SQUARE:
+      type = AST::SQUARE;
+      break;
+    case LEFT_CURLY:
+      type = AST::CURLY;
+      break;
+    default:
+      add_error (
+	Error (t3->get_locus (),
+	       "unrecognised token %qs in macro invocation - (opening) "
+	       "delimiter expected",
+	       t3->get_token_description ()));
+
+      return ExprOrStmt::create_error ();
+    }
+  lexer.skip_token ();
+
+  // parse actual token trees
+  std::vector<std::unique_ptr<AST::TokenTree>> token_trees;
+  auto delim_open
+    = std::unique_ptr<AST::Token> (new AST::Token (std::move (t3)));
+  token_trees.push_back (std::move (delim_open));
+
+  t3 = lexer.peek_token ();
+  // parse token trees until the initial delimiter token is found again
+  while (!token_id_matches_delims (t3->get_id (), type))
+    {
+      std::unique_ptr<AST::TokenTree> tree = parse_token_tree ();
+
+      if (tree == nullptr)
+	{
+	  Error error (t3->get_locus (),
+		       "failed to parse token tree for macro invocation (or "
+		       "semi) - found %qs",
+		       t3->get_token_description ());
+	  add_error (std::move (error));
+
+	  return ExprOrStmt::create_error ();
+	}
+
+      token_trees.push_back (std::move (tree));
+
+      t3 = lexer.peek_token ();
+    }
+  auto delim_close
+    = std::unique_ptr<AST::Token> (new AST::Token (std::move (t3)));
+  token_trees.push_back (std::move (delim_close));
+
+  // parse end delimiters
+  t3 = lexer.peek_token ();
+  if (token_id_matches_delims (t3->get_id (), type))
+    {
+      // tokens match opening delimiter, so skip.
+      lexer.skip_token ();
+
+      /* with curly bracketed macros, assume it is a macro invocation unless
+       * a semicolon is explicitly put at the end. this is not necessarily
+       * true (i.e. context-dependence) and so may have to be fixed up via
+       * HACKs in semantic analysis (by checking whether it is the last elem
+       * in the vector). */
+
+      AST::DelimTokenTree delim_tok_tree (type, std::move (token_trees),
+					  tok_tree_loc);
+      AST::MacroInvocData invoc_data (std::move (macro_path),
+				      std::move (delim_tok_tree));
+
+      if (lexer.peek_token ()->get_id () == SEMICOLON)
+	{
+	  lexer.skip_token ();
+
+	  std::unique_ptr<AST::MacroInvocation> stmt (
+	    new AST::MacroInvocation (std::move (invoc_data),
+				      std::move (outer_attrs), macro_locus,
+				      true));
+	  return ExprOrStmt (std::move (stmt));
+	}
+
+      // otherwise, create macro invocation
+      std::unique_ptr<AST::MacroInvocation> expr (
+	new AST::MacroInvocation (std::move (invoc_data),
+				  std::move (outer_attrs), macro_locus));
+      return ExprOrStmt (std::move (expr));
+    }
+  else
+    {
+      const_TokenPtr t = lexer.peek_token ();
+      // tokens don't match opening delimiters, so produce error
+      Error error (
+	t->get_locus (),
+	"unexpected token %qs - expecting closing delimiter %qs (for a "
+	"macro invocation)",
+	t->get_token_description (),
+	(type == AST::PARENS ? ")" : (type == AST::SQUARE ? "]" : "}")));
+      add_error (std::move (error));
+
+      return ExprOrStmt::create_error ();
+    }
+}
+
+// "Unexpected token" panic mode - flags gcc error at unexpected token
+template <typename ManagedTokenSource>
+void
+Parser<ManagedTokenSource>::unexpected_token (const_TokenPtr t)
+{
+  Error error (t->get_locus (), "unexpected token %qs\n",
+	       t->get_token_description ());
+  add_error (std::move (error));
+}
+
+/* Crappy "error recovery" performed after error by skipping tokens until a
+ * semi-colon is found */
+template <typename ManagedTokenSource>
+void
+Parser<ManagedTokenSource>::skip_after_semicolon ()
+{
+  const_TokenPtr t = lexer.peek_token ();
+
+  while (t->get_id () != END_OF_FILE && t->get_id () != SEMICOLON)
+    {
+      lexer.skip_token ();
+      t = lexer.peek_token ();
+    }
+
+  if (t->get_id () == SEMICOLON)
+    lexer.skip_token ();
+}
+
+/* Checks if current token has inputted id - skips it and returns true if so,
+ * diagnoses an error and returns false otherwise. */
+template <typename ManagedTokenSource>
+bool
+Parser<ManagedTokenSource>::skip_token (TokenId token_id)
+{
+  return expect_token (token_id) != const_TokenPtr ();
+}
+
+/* Checks if current token has inputted id - skips it and returns true if so,
+ * returns false otherwise without diagnosing an error */
+template <typename ManagedTokenSource>
+bool
+Parser<ManagedTokenSource>::maybe_skip_token (TokenId token_id)
+{
+  if (lexer.peek_token ()->get_id () != token_id)
+    return false;
+  else
+    return skip_token (token_id);
+}
+
+/* Checks the current token - if id is same as expected, skips and returns it,
+ * otherwise diagnoses error and returns null. */
+template <typename ManagedTokenSource>
+const_TokenPtr
+Parser<ManagedTokenSource>::expect_token (TokenId token_id)
+{
+  const_TokenPtr t = lexer.peek_token ();
+  if (t->get_id () == token_id)
+    {
+      lexer.skip_token ();
+      return t;
+    }
+  else
+    {
+      Error error (t->get_locus (), "expecting %qs but %qs found",
+		   get_token_description (token_id),
+		   t->get_token_description ());
+      add_error (std::move (error));
+
+      return const_TokenPtr ();
+    }
+}
+
+// Skips all tokens until EOF or }. Don't use.
+template <typename ManagedTokenSource>
+void
+Parser<ManagedTokenSource>::skip_after_end ()
+{
+  const_TokenPtr t = lexer.peek_token ();
+
+  while (t->get_id () != END_OF_FILE && t->get_id () != RIGHT_CURLY)
+    {
+      lexer.skip_token ();
+      t = lexer.peek_token ();
+    }
+
+  if (t->get_id () == RIGHT_CURLY)
+    {
+      lexer.skip_token ();
+    }
+}
+
+/* A slightly more aware error-handler that skips all tokens until it reaches
+ * the end of the block scope (i.e. when left curly brackets = right curly
+ * brackets). Note: assumes currently in the middle of a block. Use
+ * skip_after_next_block to skip based on the assumption that the block
+ * has not been entered yet. */
+template <typename ManagedTokenSource>
+void
+Parser<ManagedTokenSource>::skip_after_end_block ()
+{
+  const_TokenPtr t = lexer.peek_token ();
+  int curly_count = 1;
+
+  while (curly_count > 0 && t->get_id () != END_OF_FILE)
+    {
+      switch (t->get_id ())
+	{
+	case LEFT_CURLY:
+	  curly_count++;
+	  break;
+	case RIGHT_CURLY:
+	  curly_count--;
+	  break;
+	default:
+	  break;
+	}
+      lexer.skip_token ();
+      t = lexer.peek_token ();
+    }
+}
+
+/* Skips tokens until the end of the next block. i.e. assumes that the block
+ * has not been entered yet. */
+template <typename ManagedTokenSource>
+void
+Parser<ManagedTokenSource>::skip_after_next_block ()
+{
+  const_TokenPtr t = lexer.peek_token ();
+
+  // initial loop - skip until EOF if no left curlies encountered
+  while (t->get_id () != END_OF_FILE && t->get_id () != LEFT_CURLY)
+    {
+      lexer.skip_token ();
+
+      t = lexer.peek_token ();
+    }
+
+  // if next token is left, skip it and then skip after the block ends
+  if (t->get_id () == LEFT_CURLY)
+    {
+      lexer.skip_token ();
+
+      skip_after_end_block ();
+    }
+  // otherwise, do nothing as EOF
+}
+
+/* Skips all tokens until ] (the end of an attribute) - does not skip the ]
+ * (as designed for attribute body use) */
+template <typename ManagedTokenSource>
+void
+Parser<ManagedTokenSource>::skip_after_end_attribute ()
+{
+  const_TokenPtr t = lexer.peek_token ();
+
+  while (t->get_id () != RIGHT_SQUARE)
+    {
+      lexer.skip_token ();
+      t = lexer.peek_token ();
+    }
+
+  // Don't skip the RIGHT_SQUARE token
+}
+
+/* Pratt parser impl of parse_expr. FIXME: this is only provisional and
+ * probably will be changed.
+ * FIXME: this may only parse expressions without blocks as they are the only
+ * expressions to have precedence? */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::Expr>
+Parser<ManagedTokenSource>::parse_expr (int right_binding_power,
+					AST::AttrVec outer_attrs,
+					ParseRestrictions restrictions)
+{
+  const_TokenPtr current_token = lexer.peek_token ();
+  // Special hack because we are allowed to return nullptr, in that case we
+  // don't want to skip the token, since we don't actually parse it. But if
+  // null isn't allowed it indicates an error, and we want to skip past that.
+  // So return early if it is one of the tokens that ends an expression
+  // (or at least cannot start a new expression).
+  if (restrictions.expr_can_be_null)
+    {
+      TokenId id = current_token->get_id ();
+      if (id == SEMICOLON || id == RIGHT_PAREN || id == RIGHT_CURLY
+	  || id == RIGHT_SQUARE)
+	return nullptr;
+    }
+  lexer.skip_token ();
+
+  // parse null denotation (unary part of expression)
+  std::unique_ptr<AST::Expr> expr
+    = null_denotation (current_token, {}, restrictions);
+
+  if (expr == nullptr)
+    {
+      // DEBUG
+      rust_debug ("null denotation is null; returning null for parse_expr");
+      return nullptr;
+    }
+
+  // stop parsing if find lower priority token - parse higher priority first
+  while (right_binding_power < left_binding_power (lexer.peek_token ()))
+    {
+      current_token = lexer.peek_token ();
+      lexer.skip_token ();
+
+      expr = left_denotation (current_token, std::move (expr),
+			      std::move (outer_attrs), restrictions);
+
+      if (expr == nullptr)
+	{
+	  // DEBUG
+	  rust_debug ("left denotation is null; returning null for parse_expr");
+
+	  return nullptr;
+	}
+    }
+
+  return expr;
+}
+
+// Parse expression with lowest left binding power.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::Expr>
+Parser<ManagedTokenSource>::parse_expr (AST::AttrVec outer_attrs,
+					ParseRestrictions restrictions)
+{
+  return parse_expr (LBP_LOWEST, std::move (outer_attrs), restrictions);
+}
+
+/* Determines action to take when finding token at beginning of expression.
+ * FIXME: this may only apply to precedence-capable expressions (which are all
+ * expressions without blocks), so make return type ExprWithoutBlock? It would
+ * simplify stuff. */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::Expr>
+Parser<ManagedTokenSource>::null_denotation (const_TokenPtr tok,
+					     AST::AttrVec outer_attrs,
+					     ParseRestrictions restrictions)
+{
+  /* note: tok is previous character in input stream, not current one, as
+   * parse_expr skips it before passing it in */
+
+  /* as a Pratt parser (which works by decomposing expressions into a null
+   * denotation and then a left denotation), null denotations handle primaries
+   * and unary operands (but only prefix unary operands) */
+
+  switch (tok->get_id ())
+    {
+      case IDENTIFIER: {
+	// DEBUG
+	rust_debug ("beginning null denotation identifier handling");
+
+	/* best option: parse as path, then extract identifier, macro,
+	 * struct/enum, or just path info from it */
+	AST::PathInExpression path = parse_path_in_expression_pratt (tok);
+
+	// DEBUG:
+	rust_debug ("finished null denotation identifier path parsing - "
+		    "next is branching");
+
+	// branch on next token
+	const_TokenPtr t = lexer.peek_token ();
+	switch (t->get_id ())
+	  {
+	  case EXCLAM:
+	    // macro
+	    return parse_macro_invocation_partial (std::move (path),
+						   std::move (outer_attrs),
+						   restrictions);
+	    case LEFT_CURLY: {
+	      bool not_a_block
+		= lexer.peek_token (1)->get_id () == IDENTIFIER
+		  && (lexer.peek_token (2)->get_id () == COMMA
+		      || (lexer.peek_token (2)->get_id () == COLON
+			  && (lexer.peek_token (4)->get_id () == COMMA
+			      || !can_tok_start_type (
+				lexer.peek_token (3)->get_id ()))));
+
+	      /* definitely not a block:
+	       *  path '{' ident ','
+	       *  path '{' ident ':' [anything] ','
+	       *  path '{' ident ':' [not a type]
+	       * otherwise, assume block expr and thus path */
+	      // DEBUG
+	      rust_debug ("values of lookahead: '%s' '%s' '%s' '%s' ",
+			  lexer.peek_token (1)->get_token_description (),
+			  lexer.peek_token (2)->get_token_description (),
+			  lexer.peek_token (3)->get_token_description (),
+			  lexer.peek_token (4)->get_token_description ());
+
+	      rust_debug ("can be struct expr: '%s', not a block: '%s'",
+			  restrictions.can_be_struct_expr ? "true" : "false",
+			  not_a_block ? "true" : "false");
+
+	      // struct/enum expr struct
+	      if (!restrictions.can_be_struct_expr && !not_a_block)
+		{
+		  // HACK: add outer attrs to path
+		  path.set_outer_attrs (std::move (outer_attrs));
+		  return std::unique_ptr<AST::PathInExpression> (
+		    new AST::PathInExpression (std::move (path)));
+		}
+	      return parse_struct_expr_struct_partial (std::move (path),
+						       std::move (outer_attrs));
+	    }
+	  case LEFT_PAREN:
+	    // struct/enum expr tuple
+	    if (!restrictions.can_be_struct_expr)
+	      {
+		// HACK: add outer attrs to path
+		path.set_outer_attrs (std::move (outer_attrs));
+		return std::unique_ptr<AST::PathInExpression> (
+		  new AST::PathInExpression (std::move (path)));
+	      }
+	    return parse_struct_expr_tuple_partial (std::move (path),
+						    std::move (outer_attrs));
+	  default:
+	    // assume path is returned if not single segment
+	    if (path.is_single_segment ())
+	      {
+		// have to return an identifier expression or something, idk
+		/* HACK: may have to become permanent, but this is my current
+		 * identifier expression */
+		return std::unique_ptr<AST::IdentifierExpr> (
+		  new AST::IdentifierExpr (tok->get_str (), {},
+					   tok->get_locus ()));
+	      }
+	    // HACK: add outer attrs to path
+	    path.set_outer_attrs (std::move (outer_attrs));
+	    return std::unique_ptr<AST::PathInExpression> (
+	      new AST::PathInExpression (std::move (path)));
+	  }
+	gcc_unreachable ();
+      }
+      /* FIXME: delegate to parse_literal_expr instead? would have to rejig
+       * tokens and whatever. */
+      /* FIXME: could also be path expression (and hence macro expression,
+       * struct/enum expr) */
+      case LEFT_ANGLE: {
+	// qualified path
+	// HACK: add outer attrs to path
+	AST::QualifiedPathInExpression path
+	  = parse_qualified_path_in_expression (tok->get_locus ());
+	path.set_outer_attrs (std::move (outer_attrs));
+	return std::unique_ptr<AST::QualifiedPathInExpression> (
+	  new AST::QualifiedPathInExpression (std::move (path)));
+      }
+    // FIXME: for literal exprs, should outer attrs be passed in or just
+    // ignored?
+    case INT_LITERAL:
+      // we should check the range, but ignore for now
+      // encode as int?
+      return std::unique_ptr<AST::LiteralExpr> (
+	new AST::LiteralExpr (tok->get_str (), AST::Literal::INT,
+			      tok->get_type_hint (), {}, tok->get_locus ()));
+    case FLOAT_LITERAL:
+      // encode as float?
+      return std::unique_ptr<AST::LiteralExpr> (
+	new AST::LiteralExpr (tok->get_str (), AST::Literal::FLOAT,
+			      tok->get_type_hint (), {}, tok->get_locus ()));
+    case STRING_LITERAL:
+      return std::unique_ptr<AST::LiteralExpr> (
+	new AST::LiteralExpr (tok->get_str (), AST::Literal::STRING,
+			      tok->get_type_hint (), {}, tok->get_locus ()));
+    case BYTE_STRING_LITERAL:
+      return std::unique_ptr<AST::LiteralExpr> (
+	new AST::LiteralExpr (tok->get_str (), AST::Literal::BYTE_STRING,
+			      tok->get_type_hint (), {}, tok->get_locus ()));
+    case CHAR_LITERAL:
+      return std::unique_ptr<AST::LiteralExpr> (
+	new AST::LiteralExpr (tok->get_str (), AST::Literal::CHAR,
+			      tok->get_type_hint (), {}, tok->get_locus ()));
+    case BYTE_CHAR_LITERAL:
+      return std::unique_ptr<AST::LiteralExpr> (
+	new AST::LiteralExpr (tok->get_str (), AST::Literal::BYTE,
+			      tok->get_type_hint (), {}, tok->get_locus ()));
+    case TRUE_LITERAL:
+      return std::unique_ptr<AST::LiteralExpr> (
+	new AST::LiteralExpr ("true", AST::Literal::BOOL, tok->get_type_hint (),
+			      {}, tok->get_locus ()));
+    case FALSE_LITERAL:
+      return std::unique_ptr<AST::LiteralExpr> (
+	new AST::LiteralExpr ("false", AST::Literal::BOOL,
+			      tok->get_type_hint (), {}, tok->get_locus ()));
+    case LEFT_PAREN:
+      return parse_grouped_or_tuple_expr (std::move (outer_attrs),
+					  tok->get_locus ());
+
+      /*case PLUS: { // unary plus operator
+	  // invoke parse_expr recursively with appropriate priority, etc. for
+      below AST::Expr* expr = parse_expr(LBP_UNARY_PLUS);
+
+	  if (expr == nullptr)
+	      return nullptr;
+	  // can only apply to integer and float expressions
+	  if (expr->get_type() != integer_type_node || expr->get_type() !=
+      float_type_node) { rust_error_at(tok->get_locus(), "operand of unary
+      plus must be int or float but it is %s", print_type(expr->get_type()));
+      return nullptr;
+	  }
+
+	  return Tree(expr, tok->get_locus());
+      }*/
+      // Rust has no unary plus operator
+      case MINUS: { // unary minus
+	ParseRestrictions entered_from_unary;
+	entered_from_unary.entered_from_unary = true;
+	if (!restrictions.can_be_struct_expr)
+	  entered_from_unary.can_be_struct_expr = false;
+	std::unique_ptr<AST::Expr> expr
+	  = parse_expr (LBP_UNARY_MINUS, {}, entered_from_unary);
+
+	if (expr == nullptr)
+	  return nullptr;
+	// can only apply to integer and float expressions
+	/*if (expr.get_type() != integer_type_node || expr.get_type() !=
+	float_type_node) { rust_error_at(tok->get_locus(), "operand of unary
+	minus must be int or float but it is %s",
+	print_type(expr.get_type())); return Tree::error();
+	}*/
+	/* FIXME: when implemented the "get type" method on expr, ensure it is
+	 * int or float type (except unsigned int). Actually, this would
+	 * probably have to be done in semantic analysis (as type checking).
+	 */
+
+	/* FIXME: allow outer attributes on these expressions by having an
+	 * outer attrs parameter in function*/
+	return std::unique_ptr<AST::NegationExpr> (
+	  new AST::NegationExpr (std::move (expr), NegationOperator::NEGATE,
+				 std::move (outer_attrs), tok->get_locus ()));
+      }
+      case EXCLAM: { // logical or bitwise not
+	ParseRestrictions entered_from_unary;
+	entered_from_unary.entered_from_unary = true;
+	if (!restrictions.can_be_struct_expr)
+	  entered_from_unary.can_be_struct_expr = false;
+	std::unique_ptr<AST::Expr> expr
+	  = parse_expr (LBP_UNARY_EXCLAM, {}, entered_from_unary);
+
+	if (expr == nullptr)
+	  return nullptr;
+	// can only apply to boolean expressions
+	/*if (expr.get_type() != boolean_type_node) {
+	    rust_error_at(tok->get_locus(),
+	      "operand of logical not must be a boolean but it is %s",
+	      print_type(expr.get_type()));
+	    return Tree::error();
+	}*/
+	/* FIXME: type checking for boolean or integer expressions in semantic
+	 * analysis */
+
+	// FIXME: allow outer attributes on these expressions
+	return std::unique_ptr<AST::NegationExpr> (
+	  new AST::NegationExpr (std::move (expr), NegationOperator::NOT,
+				 std::move (outer_attrs), tok->get_locus ()));
+      }
+      case ASTERISK: {
+	/* pointer dereference only - HACK: as struct expressions should
+	 * always be value expressions, cannot be dereferenced */
+	ParseRestrictions entered_from_unary;
+	entered_from_unary.entered_from_unary = true;
+	entered_from_unary.can_be_struct_expr = false;
+	std::unique_ptr<AST::Expr> expr
+	  = parse_expr (LBP_UNARY_ASTERISK, {}, entered_from_unary);
+	// FIXME: allow outer attributes on expression
+	return std::unique_ptr<AST::DereferenceExpr> (
+	  new AST::DereferenceExpr (std::move (expr), std::move (outer_attrs),
+				    tok->get_locus ()));
+      }
+      case AMP: {
+	// (single) "borrow" expression - shared (mutable) or immutable
+	std::unique_ptr<AST::Expr> expr = nullptr;
+	bool is_mut_borrow = false;
+
+	/* HACK: as struct expressions should always be value expressions,
+	 * cannot be referenced */
+	ParseRestrictions entered_from_unary;
+	entered_from_unary.entered_from_unary = true;
+	entered_from_unary.can_be_struct_expr = false;
+
+	if (lexer.peek_token ()->get_id () == MUT)
+	  {
+	    lexer.skip_token ();
+	    expr = parse_expr (LBP_UNARY_AMP_MUT, {}, entered_from_unary);
+	    is_mut_borrow = true;
+	  }
+	else
+	  {
+	    expr = parse_expr (LBP_UNARY_AMP, {}, entered_from_unary);
+	  }
+
+	// FIXME: allow outer attributes on expression
+	return std::unique_ptr<AST::BorrowExpr> (
+	  new AST::BorrowExpr (std::move (expr), is_mut_borrow, false,
+			       std::move (outer_attrs), tok->get_locus ()));
+      }
+      case LOGICAL_AND: {
+	// (double) "borrow" expression - shared (mutable) or immutable
+	std::unique_ptr<AST::Expr> expr = nullptr;
+	bool is_mut_borrow = false;
+
+	ParseRestrictions entered_from_unary;
+	entered_from_unary.entered_from_unary = true;
+
+	if (lexer.peek_token ()->get_id () == MUT)
+	  {
+	    lexer.skip_token ();
+	    expr = parse_expr (LBP_UNARY_AMP_MUT, {}, entered_from_unary);
+	    is_mut_borrow = true;
+	  }
+	else
+	  {
+	    expr = parse_expr (LBP_UNARY_AMP, {}, entered_from_unary);
+	  }
+
+	// FIXME: allow outer attributes on expression
+	return std::unique_ptr<AST::BorrowExpr> (
+	  new AST::BorrowExpr (std::move (expr), is_mut_borrow, true,
+			       std::move (outer_attrs), tok->get_locus ()));
+      }
+      case SCOPE_RESOLUTION: {
+	// TODO: fix: this is for global paths, i.e. std::string::whatever
+	Error error (tok->get_locus (),
+		     "found null denotation scope resolution operator, and "
+		     "have not written handling for it");
+	add_error (std::move (error));
+
+	return nullptr;
+      }
+    case SELF:
+    case SELF_ALIAS:
+    case DOLLAR_SIGN:
+    case CRATE:
+      case SUPER: {
+	// DEBUG
+	rust_debug ("beginning null denotation "
+		    "self/self-alias/dollar/crate/super handling");
+
+	/* best option: parse as path, then extract identifier, macro,
+	 * struct/enum, or just path info from it */
+	AST::PathInExpression path = parse_path_in_expression_pratt (tok);
+
+	// DEBUG
+	rust_debug (
+	  "just finished parsing path (going to disambiguate) - peeked "
+	  "token is '%s'",
+	  lexer.peek_token ()->get_token_description ());
+
+	// HACK: always make "self" by itself a path (regardless of next
+	// tokens)
+	if (tok->get_id () == SELF && path.is_single_segment ())
+	  {
+	    // HACK: add outer attrs to path
+	    path.set_outer_attrs (std::move (outer_attrs));
+	    return std::unique_ptr<AST::PathInExpression> (
+	      new AST::PathInExpression (std::move (path)));
+	  }
+
+	// branch on next token
+	const_TokenPtr t = lexer.peek_token ();
+	switch (t->get_id ())
+	  {
+	  case EXCLAM:
+	    // macro
+	    return parse_macro_invocation_partial (std::move (path),
+						   std::move (outer_attrs));
+	    case LEFT_CURLY: {
+	      // struct/enum expr struct
+	      rust_debug ("can_be_struct_expr: %s",
+			  restrictions.can_be_struct_expr ? "true" : "false");
+
+	      bool not_a_block
+		= lexer.peek_token (1)->get_id () == IDENTIFIER
+		  && (lexer.peek_token (2)->get_id () == COMMA
+		      || (lexer.peek_token (2)->get_id () == COLON
+			  && (lexer.peek_token (4)->get_id () == COMMA
+			      || !can_tok_start_type (
+				lexer.peek_token (3)->get_id ()))));
+
+	      if (!restrictions.can_be_struct_expr && !not_a_block)
+		{
+		  // assume path is returned
+		  // HACK: add outer attributes to path
+		  path.set_outer_attrs (std::move (outer_attrs));
+		  return std::unique_ptr<AST::PathInExpression> (
+		    new AST::PathInExpression (std::move (path)));
+		}
+	      return parse_struct_expr_struct_partial (std::move (path),
+						       std::move (outer_attrs));
+	    }
+	  case LEFT_PAREN:
+	    // struct/enum expr tuple
+	    if (!restrictions.can_be_struct_expr)
+	      {
+		// assume path is returned
+		// HACK: add outer attributes to path
+		path.set_outer_attrs (std::move (outer_attrs));
+		return std::unique_ptr<AST::PathInExpression> (
+		  new AST::PathInExpression (std::move (path)));
+	      }
+	    return parse_struct_expr_tuple_partial (std::move (path),
+						    std::move (outer_attrs));
+	  default:
+	    // assume path is returned
+	    // HACK: add outer attributes to path
+	    path.set_outer_attrs (std::move (outer_attrs));
+	    return std::unique_ptr<AST::PathInExpression> (
+	      new AST::PathInExpression (std::move (path)));
+	  }
+	gcc_unreachable ();
+      }
+    case OR:
+    case PIPE:
+    case MOVE:
+      // closure expression
+      return parse_closure_expr_pratt (tok, std::move (outer_attrs));
+    case DOT_DOT:
+      // either "range to" or "range full" expressions
+      return parse_nud_range_exclusive_expr (tok, std::move (outer_attrs));
+    case DOT_DOT_EQ:
+      // range to inclusive expr
+      return parse_range_to_inclusive_expr (tok, std::move (outer_attrs));
+    case RETURN_TOK:
+      // FIXME: is this really a null denotation expression?
+      return parse_return_expr (std::move (outer_attrs), tok->get_locus ());
+    case BREAK:
+      // FIXME: is this really a null denotation expression?
+      return parse_break_expr (std::move (outer_attrs), tok->get_locus ());
+    case CONTINUE:
+      return parse_continue_expr (std::move (outer_attrs), tok->get_locus ());
+    case LEFT_CURLY:
+      // ok - this is an expression with block for once.
+      return parse_block_expr (std::move (outer_attrs), tok->get_locus ());
+    case IF:
+      // if or if let, so more lookahead to find out
+      if (lexer.peek_token (1)->get_id () == LET)
+	{
+	  // if let expr
+	  return parse_if_let_expr (std::move (outer_attrs), tok->get_locus ());
+	}
+      else
+	{
+	  // if expr
+	  return parse_if_expr (std::move (outer_attrs), tok->get_locus ());
+	}
+    case LOOP:
+      return parse_loop_expr (std::move (outer_attrs), AST::LoopLabel::error (),
+			      tok->get_locus ());
+    case WHILE:
+      return parse_while_loop_expr (std::move (outer_attrs),
+				    AST::LoopLabel::error (),
+				    tok->get_locus ());
+    case MATCH_TOK:
+      // also an expression with block
+      return parse_match_expr (std::move (outer_attrs), tok->get_locus ());
+    case LEFT_SQUARE:
+      // array definition expr (not indexing)
+      return parse_array_expr (std::move (outer_attrs), tok->get_locus ());
+    case UNSAFE:
+      return parse_unsafe_block_expr (std::move (outer_attrs),
+				      tok->get_locus ());
+    default:
+      if (!restrictions.expr_can_be_null)
+	add_error (Error (tok->get_locus (),
+			  "found unexpected token %qs in null denotation",
+			  tok->get_token_description ()));
+      return nullptr;
+    }
+}
+
+/* Called for each token that can appear in infix (between) position. Can be
+ * operators or other punctuation. Returns a function pointer to member
+ * function that implements the left denotation for the token given. */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::Expr>
+Parser<ManagedTokenSource>::left_denotation (const_TokenPtr tok,
+					     std::unique_ptr<AST::Expr> left,
+					     AST::AttrVec outer_attrs,
+					     ParseRestrictions restrictions)
+{
+  // Token passed in has already been skipped, so peek gives "next" token
+  switch (tok->get_id ())
+    {
+      // FIXME: allow for outer attributes to be applied
+      case QUESTION_MARK: {
+	Location left_locus = left->get_locus ();
+	// error propagation expression - unary postfix
+	return std::unique_ptr<AST::ErrorPropagationExpr> (
+	  new AST::ErrorPropagationExpr (std::move (left),
+					 std::move (outer_attrs), left_locus));
+      }
+    case PLUS:
+      // sum expression - binary infix
+      /*return parse_binary_plus_expr (tok, std::move (left),
+				     std::move (outer_attrs), restrictions);*/
+      return parse_arithmetic_or_logical_expr (tok, std::move (left),
+					       std::move (outer_attrs),
+					       ArithmeticOrLogicalOperator::ADD,
+					       restrictions);
+    case MINUS:
+      // difference expression - binary infix
+      /*return parse_binary_minus_expr (tok, std::move (left),
+				      std::move (outer_attrs),
+	 restrictions);*/
+      return parse_arithmetic_or_logical_expr (
+	tok, std::move (left), std::move (outer_attrs),
+	ArithmeticOrLogicalOperator::SUBTRACT, restrictions);
+    case ASTERISK:
+      // product expression - binary infix
+      /*return parse_binary_mult_expr (tok, std::move (left),
+				     std::move (outer_attrs), restrictions);*/
+      return parse_arithmetic_or_logical_expr (
+	tok, std::move (left), std::move (outer_attrs),
+	ArithmeticOrLogicalOperator::MULTIPLY, restrictions);
+    case DIV:
+      // quotient expression - binary infix
+      /*return parse_binary_div_expr (tok, std::move (left),
+				    std::move (outer_attrs), restrictions);*/
+      return parse_arithmetic_or_logical_expr (
+	tok, std::move (left), std::move (outer_attrs),
+	ArithmeticOrLogicalOperator::DIVIDE, restrictions);
+    case PERCENT:
+      // modulo expression - binary infix
+      /*return parse_binary_mod_expr (tok, std::move (left),
+				    std::move (outer_attrs), restrictions);*/
+      return parse_arithmetic_or_logical_expr (
+	tok, std::move (left), std::move (outer_attrs),
+	ArithmeticOrLogicalOperator::MODULUS, restrictions);
+    case AMP:
+      // logical or bitwise and expression - binary infix
+      /*return parse_bitwise_and_expr (tok, std::move (left),
+				     std::move (outer_attrs), restrictions);*/
+      return parse_arithmetic_or_logical_expr (
+	tok, std::move (left), std::move (outer_attrs),
+	ArithmeticOrLogicalOperator::BITWISE_AND, restrictions);
+    case PIPE:
+      // logical or bitwise or expression - binary infix
+      /*return parse_bitwise_or_expr (tok, std::move (left),
+				    std::move (outer_attrs), restrictions);*/
+      return parse_arithmetic_or_logical_expr (
+	tok, std::move (left), std::move (outer_attrs),
+	ArithmeticOrLogicalOperator::BITWISE_OR, restrictions);
+    case CARET:
+      // logical or bitwise xor expression - binary infix
+      /*return parse_bitwise_xor_expr (tok, std::move (left),
+				     std::move (outer_attrs), restrictions);*/
+      return parse_arithmetic_or_logical_expr (
+	tok, std::move (left), std::move (outer_attrs),
+	ArithmeticOrLogicalOperator::BITWISE_XOR, restrictions);
+    case LEFT_SHIFT:
+      // left shift expression - binary infix
+      /*return parse_left_shift_expr (tok, std::move (left),
+				    std::move (outer_attrs), restrictions);*/
+      return parse_arithmetic_or_logical_expr (
+	tok, std::move (left), std::move (outer_attrs),
+	ArithmeticOrLogicalOperator::LEFT_SHIFT, restrictions);
+    case RIGHT_SHIFT:
+      // right shift expression - binary infix
+      /*return parse_right_shift_expr (tok, std::move (left),
+				     std::move (outer_attrs), restrictions);*/
+      return parse_arithmetic_or_logical_expr (
+	tok, std::move (left), std::move (outer_attrs),
+	ArithmeticOrLogicalOperator::RIGHT_SHIFT, restrictions);
+    case EQUAL_EQUAL:
+      // equal to expression - binary infix (no associativity)
+      /*return parse_binary_equal_expr (tok, std::move (left),
+				      std::move (outer_attrs),
+	 restrictions);*/
+      return parse_comparison_expr (tok, std::move (left),
+				    std::move (outer_attrs),
+				    ComparisonOperator::EQUAL, restrictions);
+    case NOT_EQUAL:
+      // not equal to expression - binary infix (no associativity)
+      /*return parse_binary_not_equal_expr (tok, std::move (left),
+					  std::move (outer_attrs),
+					  restrictions);*/
+      return parse_comparison_expr (tok, std::move (left),
+				    std::move (outer_attrs),
+				    ComparisonOperator::NOT_EQUAL,
+				    restrictions);
+    case RIGHT_ANGLE:
+      // greater than expression - binary infix (no associativity)
+      /*return parse_binary_greater_than_expr (tok, std::move (left),
+					     std::move (outer_attrs),
+					     restrictions);*/
+      return parse_comparison_expr (tok, std::move (left),
+				    std::move (outer_attrs),
+				    ComparisonOperator::GREATER_THAN,
+				    restrictions);
+    case LEFT_ANGLE:
+      // less than expression - binary infix (no associativity)
+      /*return parse_binary_less_than_expr (tok, std::move (left),
+					  std::move (outer_attrs),
+					  restrictions);*/
+      return parse_comparison_expr (tok, std::move (left),
+				    std::move (outer_attrs),
+				    ComparisonOperator::LESS_THAN,
+				    restrictions);
+    case GREATER_OR_EQUAL:
+      // greater than or equal to expression - binary infix (no associativity)
+      /*return parse_binary_greater_equal_expr (tok, std::move (left),
+					      std::move (outer_attrs),
+					      restrictions);*/
+      return parse_comparison_expr (tok, std::move (left),
+				    std::move (outer_attrs),
+				    ComparisonOperator::GREATER_OR_EQUAL,
+				    restrictions);
+    case LESS_OR_EQUAL:
+      // less than or equal to expression - binary infix (no associativity)
+      /*return parse_binary_less_equal_expr (tok, std::move (left),
+					   std::move (outer_attrs),
+					   restrictions);*/
+      return parse_comparison_expr (tok, std::move (left),
+				    std::move (outer_attrs),
+				    ComparisonOperator::LESS_OR_EQUAL,
+				    restrictions);
+    case OR:
+      // lazy logical or expression - binary infix
+      return parse_lazy_or_expr (tok, std::move (left), std::move (outer_attrs),
+				 restrictions);
+    case LOGICAL_AND:
+      // lazy logical and expression - binary infix
+      return parse_lazy_and_expr (tok, std::move (left),
+				  std::move (outer_attrs), restrictions);
+    case AS:
+      /* type cast expression - kind of binary infix (RHS is actually a
+       * TypeNoBounds) */
+      return parse_type_cast_expr (tok, std::move (left),
+				   std::move (outer_attrs), restrictions);
+    case EQUAL:
+      // assignment expression - binary infix (note right-to-left
+      // associativity)
+      return parse_assig_expr (tok, std::move (left), std::move (outer_attrs),
+			       restrictions);
+    case PLUS_EQ:
+      /* plus-assignment expression - binary infix (note right-to-left
+       * associativity) */
+      /*return parse_plus_assig_expr (tok, std::move (left),
+				    std::move (outer_attrs), restrictions);*/
+      return parse_compound_assignment_expr (tok, std::move (left),
+					     std::move (outer_attrs),
+					     CompoundAssignmentOperator::ADD,
+					     restrictions);
+    case MINUS_EQ:
+      /* minus-assignment expression - binary infix (note right-to-left
+       * associativity) */
+      /*return parse_minus_assig_expr (tok, std::move (left),
+				     std::move (outer_attrs), restrictions);*/
+      return parse_compound_assignment_expr (
+	tok, std::move (left), std::move (outer_attrs),
+	CompoundAssignmentOperator::SUBTRACT, restrictions);
+    case ASTERISK_EQ:
+      /* multiply-assignment expression - binary infix (note right-to-left
+       * associativity) */
+      /*return parse_mult_assig_expr (tok, std::move (left),
+				    std::move (outer_attrs), restrictions);*/
+      return parse_compound_assignment_expr (
+	tok, std::move (left), std::move (outer_attrs),
+	CompoundAssignmentOperator::MULTIPLY, restrictions);
+    case DIV_EQ:
+      /* division-assignment expression - binary infix (note right-to-left
+       * associativity) */
+      /*return parse_div_assig_expr (tok, std::move (left),
+				   std::move (outer_attrs), restrictions);*/
+      return parse_compound_assignment_expr (tok, std::move (left),
+					     std::move (outer_attrs),
+					     CompoundAssignmentOperator::DIVIDE,
+					     restrictions);
+    case PERCENT_EQ:
+      /* modulo-assignment expression - binary infix (note right-to-left
+       * associativity) */
+      /*return parse_mod_assig_expr (tok, std::move (left),
+				   std::move (outer_attrs), restrictions);*/
+      return parse_compound_assignment_expr (
+	tok, std::move (left), std::move (outer_attrs),
+	CompoundAssignmentOperator::MODULUS, restrictions);
+    case AMP_EQ:
+      /* bitwise and-assignment expression - binary infix (note right-to-left
+       * associativity) */
+      /*return parse_and_assig_expr (tok, std::move (left),
+				   std::move (outer_attrs), restrictions);*/
+      return parse_compound_assignment_expr (
+	tok, std::move (left), std::move (outer_attrs),
+	CompoundAssignmentOperator::BITWISE_AND, restrictions);
+    case PIPE_EQ:
+      /* bitwise or-assignment expression - binary infix (note right-to-left
+       * associativity) */
+      /*return parse_or_assig_expr (tok, std::move (left),
+				  std::move (outer_attrs), restrictions);*/
+      return parse_compound_assignment_expr (
+	tok, std::move (left), std::move (outer_attrs),
+	CompoundAssignmentOperator::BITWISE_OR, restrictions);
+    case CARET_EQ:
+      /* bitwise xor-assignment expression - binary infix (note right-to-left
+       * associativity) */
+      /*return parse_xor_assig_expr (tok, std::move (left),
+				   std::move (outer_attrs), restrictions);*/
+      return parse_compound_assignment_expr (
+	tok, std::move (left), std::move (outer_attrs),
+	CompoundAssignmentOperator::BITWISE_XOR, restrictions);
+    case LEFT_SHIFT_EQ:
+      /* left shift-assignment expression - binary infix (note right-to-left
+       * associativity) */
+      /*return parse_left_shift_assig_expr (tok, std::move (left),
+					  std::move (outer_attrs),
+					  restrictions);*/
+      return parse_compound_assignment_expr (
+	tok, std::move (left), std::move (outer_attrs),
+	CompoundAssignmentOperator::LEFT_SHIFT, restrictions);
+    case RIGHT_SHIFT_EQ:
+      /* right shift-assignment expression - binary infix (note right-to-left
+       * associativity) */
+      /*return parse_right_shift_assig_expr (tok, std::move (left),
+					   std::move (outer_attrs),
+					   restrictions);*/
+      return parse_compound_assignment_expr (
+	tok, std::move (left), std::move (outer_attrs),
+	CompoundAssignmentOperator::RIGHT_SHIFT, restrictions);
+    case DOT_DOT:
+      /* range exclusive expression - binary infix (no associativity)
+       * either "range" or "range from" */
+      return parse_led_range_exclusive_expr (tok, std::move (left),
+					     std::move (outer_attrs),
+					     restrictions);
+    case DOT_DOT_EQ:
+      /* range inclusive expression - binary infix (no associativity)
+       * unambiguously RangeInclusiveExpr */
+      return parse_range_inclusive_expr (tok, std::move (left),
+					 std::move (outer_attrs), restrictions);
+    case SCOPE_RESOLUTION:
+      // path expression - binary infix? FIXME should this even be parsed
+      // here?
+      add_error (
+	Error (tok->get_locus (),
+	       "found scope resolution operator in left denotation "
+	       "function - this should probably be handled elsewhere"));
+
+      return nullptr;
+      case DOT: {
+	/* field expression or method call - relies on parentheses after next
+	 * identifier or await if token after is "await" (unary postfix) or
+	 * tuple index if token after is a decimal int literal */
+
+	const_TokenPtr next_tok = lexer.peek_token ();
+	if (next_tok->get_id () == IDENTIFIER
+	    && next_tok->get_str () == "await")
+	  {
+	    // await expression
+	    return parse_await_expr (tok, std::move (left),
+				     std::move (outer_attrs));
+	  }
+	else if (next_tok->get_id () == INT_LITERAL)
+	  {
+	    // tuple index expression - TODO check for decimal int literal
+	    return parse_tuple_index_expr (tok, std::move (left),
+					   std::move (outer_attrs),
+					   restrictions);
+	  }
+	else if (next_tok->get_id () == IDENTIFIER
+		 && lexer.peek_token (1)->get_id () != LEFT_PAREN
+		 && lexer.peek_token (1)->get_id () != SCOPE_RESOLUTION)
+	  {
+	    /* field expression (or should be) - FIXME: scope resolution right
+	     * after identifier should always be method, I'm pretty sure */
+	    return parse_field_access_expr (tok, std::move (left),
+					    std::move (outer_attrs),
+					    restrictions);
+	  }
+	else
+	  {
+	    // method call (probably)
+	    return parse_method_call_expr (tok, std::move (left),
+					   std::move (outer_attrs),
+					   restrictions);
+	  }
+      }
+    case LEFT_PAREN:
+      // function call - method call is based on dot notation first
+      return parse_function_call_expr (tok, std::move (left),
+				       std::move (outer_attrs), restrictions);
+    case LEFT_SQUARE:
+      // array or slice index expression (pseudo binary infix)
+      return parse_index_expr (tok, std::move (left), std::move (outer_attrs),
+			       restrictions);
+    case FLOAT_LITERAL:
+      /* HACK: get around lexer mis-identifying '.0' or '.1' or whatever as a
+       * float literal - TODO does this happen anymore? It shouldn't. */
+      return parse_tuple_index_expr_float (tok, std::move (left),
+					   std::move (outer_attrs),
+					   restrictions);
+    default:
+      add_error (Error (tok->get_locus (),
+			"found unexpected token %qs in left denotation",
+			tok->get_token_description ()));
+
+      return nullptr;
+    }
+}
+
+/* Returns the left binding power for the given ArithmeticOrLogicalExpr type.
+ * TODO make constexpr? Would that even do anything useful? */
+inline binding_powers
+get_lbp_for_arithmetic_or_logical_expr (
+  AST::ArithmeticOrLogicalExpr::ExprType expr_type)
+{
+  switch (expr_type)
+    {
+    case ArithmeticOrLogicalOperator::ADD:
+      return LBP_PLUS;
+    case ArithmeticOrLogicalOperator::SUBTRACT:
+      return LBP_MINUS;
+    case ArithmeticOrLogicalOperator::MULTIPLY:
+      return LBP_MUL;
+    case ArithmeticOrLogicalOperator::DIVIDE:
+      return LBP_DIV;
+    case ArithmeticOrLogicalOperator::MODULUS:
+      return LBP_MOD;
+    case ArithmeticOrLogicalOperator::BITWISE_AND:
+      return LBP_AMP;
+    case ArithmeticOrLogicalOperator::BITWISE_OR:
+      return LBP_PIPE;
+    case ArithmeticOrLogicalOperator::BITWISE_XOR:
+      return LBP_CARET;
+    case ArithmeticOrLogicalOperator::LEFT_SHIFT:
+      return LBP_L_SHIFT;
+    case ArithmeticOrLogicalOperator::RIGHT_SHIFT:
+      return LBP_R_SHIFT;
+    default:
+      // WTF? should not happen, this is an error
+      gcc_unreachable ();
+
+      return LBP_PLUS;
+    }
+}
+
+// Parses an arithmetic or logical expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+Parser<ManagedTokenSource>::parse_arithmetic_or_logical_expr (
+  const_TokenPtr, std::unique_ptr<AST::Expr> left, AST::AttrVec,
+  AST::ArithmeticOrLogicalExpr::ExprType expr_type,
+  ParseRestrictions restrictions)
+{
+  // parse RHS (as tok has already been consumed in parse_expression)
+  std::unique_ptr<AST::Expr> right
+    = parse_expr (get_lbp_for_arithmetic_or_logical_expr (expr_type),
+		  AST::AttrVec (), restrictions);
+  if (right == nullptr)
+    return nullptr;
+
+  // TODO: check types. actually, do so during semantic analysis
+  Location locus = left->get_locus ();
+
+  return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
+    new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
+				      expr_type, locus));
+}
+
+// Parses a binary addition expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+Parser<ManagedTokenSource>::parse_binary_plus_expr (
+  const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
+  AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
+{
+  // parse RHS (as tok has already been consumed in parse_expression)
+  std::unique_ptr<AST::Expr> right
+    = parse_expr (LBP_PLUS, AST::AttrVec (), restrictions);
+  if (right == nullptr)
+    return nullptr;
+
+  // TODO: check types. actually, do so during semantic analysis
+  Location locus = left->get_locus ();
+
+  return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
+    new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
+				      ArithmeticOrLogicalOperator::ADD, locus));
+}
+
+// Parses a binary subtraction expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+Parser<ManagedTokenSource>::parse_binary_minus_expr (
+  const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
+  AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
+{
+  // parse RHS (as tok has already been consumed in parse_expression)
+  std::unique_ptr<AST::Expr> right
+    = parse_expr (LBP_MINUS, AST::AttrVec (), restrictions);
+  if (right == nullptr)
+    return nullptr;
+
+  // TODO: check types. actually, do so during semantic analysis
+  Location locus = left->get_locus ();
+
+  return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
+    new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
+				      ArithmeticOrLogicalOperator::SUBTRACT,
+				      locus));
+}
+
+// Parses a binary multiplication expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+Parser<ManagedTokenSource>::parse_binary_mult_expr (
+  const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
+  AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
+{
+  // parse RHS (as tok has already been consumed in parse_expression)
+  std::unique_ptr<AST::Expr> right
+    = parse_expr (LBP_MUL, AST::AttrVec (), restrictions);
+  if (right == nullptr)
+    return nullptr;
+
+  // TODO: check types. actually, do so during semantic analysis
+  Location locus = left->get_locus ();
+
+  return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
+    new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
+				      ArithmeticOrLogicalOperator::MULTIPLY,
+				      locus));
+}
+
+// Parses a binary division expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+Parser<ManagedTokenSource>::parse_binary_div_expr (
+  const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
+  AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
+{
+  // parse RHS (as tok has already been consumed in parse_expression)
+  std::unique_ptr<AST::Expr> right
+    = parse_expr (LBP_DIV, AST::AttrVec (), restrictions);
+  if (right == nullptr)
+    return nullptr;
+
+  // TODO: check types. actually, do so during semantic analysis
+  Location locus = left->get_locus ();
+
+  return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
+    new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
+				      ArithmeticOrLogicalOperator::DIVIDE,
+				      locus));
+}
+
+// Parses a binary modulo expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+Parser<ManagedTokenSource>::parse_binary_mod_expr (
+  const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
+  AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
+{
+  // parse RHS (as tok has already been consumed in parse_expression)
+  std::unique_ptr<AST::Expr> right
+    = parse_expr (LBP_MOD, AST::AttrVec (), restrictions);
+  if (right == nullptr)
+    return nullptr;
+
+  // TODO: check types. actually, do so during semantic analysis
+  Location locus = left->get_locus ();
+
+  return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
+    new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
+				      ArithmeticOrLogicalOperator::MODULUS,
+				      locus));
+}
+
+/* Parses a binary bitwise (or eager logical) and expression (with Pratt
+ * parsing). */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+Parser<ManagedTokenSource>::parse_bitwise_and_expr (
+  const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
+  AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
+{
+  // parse RHS (as tok has already been consumed in parse_expression)
+  std::unique_ptr<AST::Expr> right
+    = parse_expr (LBP_AMP, AST::AttrVec (), restrictions);
+  if (right == nullptr)
+    return nullptr;
+
+  // TODO: check types. actually, do so during semantic analysis
+  Location locus = left->get_locus ();
+
+  return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
+    new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
+				      ArithmeticOrLogicalOperator::BITWISE_AND,
+				      locus));
+}
+
+/* Parses a binary bitwise (or eager logical) or expression (with Pratt
+ * parsing). */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+Parser<ManagedTokenSource>::parse_bitwise_or_expr (
+  const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
+  AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
+{
+  // parse RHS (as tok has already been consumed in parse_expression)
+  std::unique_ptr<AST::Expr> right
+    = parse_expr (LBP_PIPE, AST::AttrVec (), restrictions);
+  if (right == nullptr)
+    return nullptr;
+
+  // TODO: check types. actually, do so during semantic analysis
+  Location locus = left->get_locus ();
+
+  return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
+    new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
+				      ArithmeticOrLogicalOperator::BITWISE_OR,
+				      locus));
+}
+
+/* Parses a binary bitwise (or eager logical) xor expression (with Pratt
+ * parsing). */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+Parser<ManagedTokenSource>::parse_bitwise_xor_expr (
+  const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
+  AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
+{
+  // parse RHS (as tok has already been consumed in parse_expression)
+  std::unique_ptr<AST::Expr> right
+    = parse_expr (LBP_CARET, AST::AttrVec (), restrictions);
+  if (right == nullptr)
+    return nullptr;
+
+  // TODO: check types. actually, do so during semantic analysis
+  Location locus = left->get_locus ();
+
+  return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
+    new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
+				      ArithmeticOrLogicalOperator::BITWISE_XOR,
+				      locus));
+}
+
+// Parses a binary left shift expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+Parser<ManagedTokenSource>::parse_left_shift_expr (
+  const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
+  AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
+{
+  // parse RHS (as tok has already been consumed in parse_expression)
+  std::unique_ptr<AST::Expr> right
+    = parse_expr (LBP_L_SHIFT, AST::AttrVec (), restrictions);
+  if (right == nullptr)
+    return nullptr;
+
+  // TODO: check types. actually, do so during semantic analysis
+  Location locus = left->get_locus ();
+
+  return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
+    new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
+				      ArithmeticOrLogicalOperator::LEFT_SHIFT,
+				      locus));
+}
+
+// Parses a binary right shift expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+Parser<ManagedTokenSource>::parse_right_shift_expr (
+  const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
+  AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
+{
+  // parse RHS (as tok has already been consumed in parse_expression)
+  std::unique_ptr<AST::Expr> right
+    = parse_expr (LBP_R_SHIFT, AST::AttrVec (), restrictions);
+  if (right == nullptr)
+    return nullptr;
+
+  // TODO: check types. actually, do so during semantic analysis
+  Location locus = left->get_locus ();
+
+  return std::unique_ptr<AST::ArithmeticOrLogicalExpr> (
+    new AST::ArithmeticOrLogicalExpr (std::move (left), std::move (right),
+				      ArithmeticOrLogicalOperator::RIGHT_SHIFT,
+				      locus));
+}
+
+/* Returns the left binding power for the given ComparisonExpr type.
+ * TODO make constexpr? Would that even do anything useful? */
+inline binding_powers
+get_lbp_for_comparison_expr (AST::ComparisonExpr::ExprType expr_type)
+{
+  switch (expr_type)
+    {
+    case ComparisonOperator::EQUAL:
+      return LBP_EQUAL;
+    case ComparisonOperator::NOT_EQUAL:
+      return LBP_NOT_EQUAL;
+    case ComparisonOperator::GREATER_THAN:
+      return LBP_GREATER_THAN;
+    case ComparisonOperator::LESS_THAN:
+      return LBP_SMALLER_THAN;
+    case ComparisonOperator::GREATER_OR_EQUAL:
+      return LBP_GREATER_EQUAL;
+    case ComparisonOperator::LESS_OR_EQUAL:
+      return LBP_SMALLER_EQUAL;
+    default:
+      // WTF? should not happen, this is an error
+      gcc_unreachable ();
+
+      return LBP_EQUAL;
+    }
+}
+
+/* Parses a ComparisonExpr of given type and LBP. TODO find a way to only
+ * specify one and have the other looked up - e.g. specify ExprType and
+ * binding power is looked up? */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ComparisonExpr>
+Parser<ManagedTokenSource>::parse_comparison_expr (
+  const_TokenPtr, std::unique_ptr<AST::Expr> left, AST::AttrVec,
+  AST::ComparisonExpr::ExprType expr_type, ParseRestrictions restrictions)
+{
+  // parse RHS (as tok has already been consumed in parse_expression)
+  std::unique_ptr<AST::Expr> right
+    = parse_expr (get_lbp_for_comparison_expr (expr_type), AST::AttrVec (),
+		  restrictions);
+  if (right == nullptr)
+    return nullptr;
+
+  // TODO: check types. actually, do so during semantic analysis
+  Location locus = left->get_locus ();
+
+  return std::unique_ptr<AST::ComparisonExpr> (
+    new AST::ComparisonExpr (std::move (left), std::move (right), expr_type,
+			     locus));
+}
+
+// Parses a binary equal to expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ComparisonExpr>
+Parser<ManagedTokenSource>::parse_binary_equal_expr (
+  const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
+  AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
+{
+  // parse RHS (as tok has already been consumed in parse_expression)
+  std::unique_ptr<AST::Expr> right
+    = parse_expr (LBP_EQUAL, AST::AttrVec (), restrictions);
+  if (right == nullptr)
+    return nullptr;
+
+  // TODO: check types. actually, do so during semantic analysis
+  Location locus = left->get_locus ();
+
+  return std::unique_ptr<AST::ComparisonExpr> (
+    new AST::ComparisonExpr (std::move (left), std::move (right),
+			     ComparisonOperator::EQUAL, locus));
+}
+
+// Parses a binary not equal to expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ComparisonExpr>
+Parser<ManagedTokenSource>::parse_binary_not_equal_expr (
+  const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
+  AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
+{
+  // parse RHS (as tok has already been consumed in parse_expression)
+  std::unique_ptr<AST::Expr> right
+    = parse_expr (LBP_NOT_EQUAL, AST::AttrVec (), restrictions);
+  if (right == nullptr)
+    return nullptr;
+
+  // TODO: check types. actually, do so during semantic analysis
+  Location locus = left->get_locus ();
+
+  return std::unique_ptr<AST::ComparisonExpr> (
+    new AST::ComparisonExpr (std::move (left), std::move (right),
+			     ComparisonOperator::NOT_EQUAL, locus));
+}
+
+// Parses a binary greater than expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ComparisonExpr>
+Parser<ManagedTokenSource>::parse_binary_greater_than_expr (
+  const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
+  AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
+{
+  // parse RHS (as tok has already been consumed in parse_expression)
+  std::unique_ptr<AST::Expr> right
+    = parse_expr (LBP_GREATER_THAN, AST::AttrVec (), restrictions);
+  if (right == nullptr)
+    return nullptr;
+
+  // TODO: check types. actually, do so during semantic analysis
+  Location locus = left->get_locus ();
+
+  return std::unique_ptr<AST::ComparisonExpr> (
+    new AST::ComparisonExpr (std::move (left), std::move (right),
+			     ComparisonOperator::GREATER_THAN, locus));
+}
+
+// Parses a binary less than expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ComparisonExpr>
+Parser<ManagedTokenSource>::parse_binary_less_than_expr (
+  const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
+  AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
+{
+  // parse RHS (as tok has already been consumed in parse_expression)
+  std::unique_ptr<AST::Expr> right
+    = parse_expr (LBP_SMALLER_THAN, AST::AttrVec (), restrictions);
+  if (right == nullptr)
+    return nullptr;
+
+  // TODO: check types. actually, do so during semantic analysis
+  Location locus = left->get_locus ();
+
+  return std::unique_ptr<AST::ComparisonExpr> (
+    new AST::ComparisonExpr (std::move (left), std::move (right),
+			     ComparisonOperator::LESS_THAN, locus));
+}
+
+// Parses a binary greater than or equal to expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ComparisonExpr>
+Parser<ManagedTokenSource>::parse_binary_greater_equal_expr (
+  const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
+  AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
+{
+  // parse RHS (as tok has already been consumed in parse_expression)
+  std::unique_ptr<AST::Expr> right
+    = parse_expr (LBP_GREATER_EQUAL, AST::AttrVec (), restrictions);
+  if (right == nullptr)
+    return nullptr;
+
+  // TODO: check types. actually, do so during semantic analysis
+  Location locus = left->get_locus ();
+
+  return std::unique_ptr<AST::ComparisonExpr> (
+    new AST::ComparisonExpr (std::move (left), std::move (right),
+			     ComparisonOperator::GREATER_OR_EQUAL, locus));
+}
+
+// Parses a binary less than or equal to expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ComparisonExpr>
+Parser<ManagedTokenSource>::parse_binary_less_equal_expr (
+  const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
+  AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
+{
+  // parse RHS (as tok has already been consumed in parse_expression)
+  std::unique_ptr<AST::Expr> right
+    = parse_expr (LBP_SMALLER_EQUAL, AST::AttrVec (), restrictions);
+  if (right == nullptr)
+    return nullptr;
+
+  // TODO: check types. actually, do so during semantic analysis
+  Location locus = left->get_locus ();
+
+  return std::unique_ptr<AST::ComparisonExpr> (
+    new AST::ComparisonExpr (std::move (left), std::move (right),
+			     ComparisonOperator::LESS_OR_EQUAL, locus));
+}
+
+// Parses a binary lazy boolean or expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::LazyBooleanExpr>
+Parser<ManagedTokenSource>::parse_lazy_or_expr (
+  const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
+  AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
+{
+  // parse RHS (as tok has already been consumed in parse_expression)
+  std::unique_ptr<AST::Expr> right
+    = parse_expr (LBP_LOGICAL_OR, AST::AttrVec (), restrictions);
+  if (right == nullptr)
+    return nullptr;
+
+  // TODO: check types. actually, do so during semantic analysis
+  Location locus = left->get_locus ();
+
+  return std::unique_ptr<AST::LazyBooleanExpr> (
+    new AST::LazyBooleanExpr (std::move (left), std::move (right),
+			      LazyBooleanOperator::LOGICAL_OR, locus));
+}
+
+// Parses a binary lazy boolean and expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::LazyBooleanExpr>
+Parser<ManagedTokenSource>::parse_lazy_and_expr (
+  const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
+  AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
+{
+  // parse RHS (as tok has already been consumed in parse_expression)
+  std::unique_ptr<AST::Expr> right
+    = parse_expr (LBP_LOGICAL_AND, AST::AttrVec (), restrictions);
+  if (right == nullptr)
+    return nullptr;
+
+  // TODO: check types. actually, do so during semantic analysis
+  Location locus = left->get_locus ();
+
+  return std::unique_ptr<AST::LazyBooleanExpr> (
+    new AST::LazyBooleanExpr (std::move (left), std::move (right),
+			      LazyBooleanOperator::LOGICAL_AND, locus));
+}
+
+// Parses a pseudo-binary infix type cast expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::TypeCastExpr>
+Parser<ManagedTokenSource>::parse_type_cast_expr (
+  const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> expr_to_cast,
+  AST::AttrVec outer_attrs ATTRIBUTE_UNUSED,
+  ParseRestrictions restrictions ATTRIBUTE_UNUSED)
+{
+  // parse RHS (as tok has already been consumed in parse_expression)
+  std::unique_ptr<AST::TypeNoBounds> type = parse_type_no_bounds ();
+  if (type == nullptr)
+    return nullptr;
+  // FIXME: how do I get precedence put in here?
+
+  // TODO: check types. actually, do so during semantic analysis
+  Location locus = expr_to_cast->get_locus ();
+
+  return std::unique_ptr<AST::TypeCastExpr> (
+    new AST::TypeCastExpr (std::move (expr_to_cast), std::move (type), locus));
+}
+
+// Parses a binary assignment expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::AssignmentExpr>
+Parser<ManagedTokenSource>::parse_assig_expr (
+  const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
+  AST::AttrVec outer_attrs, ParseRestrictions restrictions)
+{
+  // parse RHS (as tok has already been consumed in parse_expression)
+  std::unique_ptr<AST::Expr> right
+    = parse_expr (LBP_ASSIG - 1, AST::AttrVec (), restrictions);
+  if (right == nullptr)
+    return nullptr;
+  // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
+
+  // TODO: check types. actually, do so during semantic analysis
+  Location locus = left->get_locus ();
+
+  return std::unique_ptr<AST::AssignmentExpr> (
+    new AST::AssignmentExpr (std::move (left), std::move (right),
+			     std::move (outer_attrs), locus));
+}
+
+/* Returns the left binding power for the given CompoundAssignmentExpr type.
+ * TODO make constexpr? Would that even do anything useful? */
+inline binding_powers
+get_lbp_for_compound_assignment_expr (
+  AST::CompoundAssignmentExpr::ExprType expr_type)
+{
+  switch (expr_type)
+    {
+    case CompoundAssignmentOperator::ADD:
+      return LBP_PLUS;
+    case CompoundAssignmentOperator::SUBTRACT:
+      return LBP_MINUS;
+    case CompoundAssignmentOperator::MULTIPLY:
+      return LBP_MUL;
+    case CompoundAssignmentOperator::DIVIDE:
+      return LBP_DIV;
+    case CompoundAssignmentOperator::MODULUS:
+      return LBP_MOD;
+    case CompoundAssignmentOperator::BITWISE_AND:
+      return LBP_AMP;
+    case CompoundAssignmentOperator::BITWISE_OR:
+      return LBP_PIPE;
+    case CompoundAssignmentOperator::BITWISE_XOR:
+      return LBP_CARET;
+    case CompoundAssignmentOperator::LEFT_SHIFT:
+      return LBP_L_SHIFT;
+    case CompoundAssignmentOperator::RIGHT_SHIFT:
+      return LBP_R_SHIFT;
+    default:
+      // WTF? should not happen, this is an error
+      gcc_unreachable ();
+
+      return LBP_PLUS;
+    }
+}
+
+// Parses a compound assignment expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::CompoundAssignmentExpr>
+Parser<ManagedTokenSource>::parse_compound_assignment_expr (
+  const_TokenPtr, std::unique_ptr<AST::Expr> left, AST::AttrVec,
+  AST::CompoundAssignmentExpr::ExprType expr_type,
+  ParseRestrictions restrictions)
+{
+  // parse RHS (as tok has already been consumed in parse_expression)
+  std::unique_ptr<AST::Expr> right
+    = parse_expr (get_lbp_for_compound_assignment_expr (expr_type) - 1,
+		  AST::AttrVec (), restrictions);
+  if (right == nullptr)
+    return nullptr;
+  // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
+
+  // TODO: check types. actually, do so during semantic analysis
+  Location locus = left->get_locus ();
+
+  return std::unique_ptr<AST::CompoundAssignmentExpr> (
+    new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
+				     expr_type, locus));
+}
+
+// Parses a binary add-assignment expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::CompoundAssignmentExpr>
+Parser<ManagedTokenSource>::parse_plus_assig_expr (
+  const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
+  AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
+{
+  // parse RHS (as tok has already been consumed in parse_expression)
+  std::unique_ptr<AST::Expr> right
+    = parse_expr (LBP_PLUS_ASSIG - 1, AST::AttrVec (), restrictions);
+  if (right == nullptr)
+    return nullptr;
+  // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
+
+  // TODO: check types. actually, do so during semantic analysis
+  Location locus = left->get_locus ();
+
+  return std::unique_ptr<AST::CompoundAssignmentExpr> (
+    new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
+				     CompoundAssignmentOperator::ADD, locus));
+}
+
+// Parses a binary minus-assignment expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::CompoundAssignmentExpr>
+Parser<ManagedTokenSource>::parse_minus_assig_expr (
+  const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
+  AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
+{
+  // parse RHS (as tok has already been consumed in parse_expression)
+  std::unique_ptr<AST::Expr> right
+    = parse_expr (LBP_MINUS_ASSIG - 1, AST::AttrVec (), restrictions);
+  if (right == nullptr)
+    return nullptr;
+  // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
+
+  // TODO: check types. actually, do so during semantic analysis
+  Location locus = left->get_locus ();
+
+  return std::unique_ptr<AST::CompoundAssignmentExpr> (
+    new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
+				     CompoundAssignmentOperator::SUBTRACT,
+				     locus));
+}
+
+// Parses a binary multiplication-assignment expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::CompoundAssignmentExpr>
+Parser<ManagedTokenSource>::parse_mult_assig_expr (
+  const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
+  AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
+{
+  // parse RHS (as tok has already been consumed in parse_expression)
+  std::unique_ptr<AST::Expr> right
+    = parse_expr (LBP_MULT_ASSIG - 1, AST::AttrVec (), restrictions);
+  if (right == nullptr)
+    return nullptr;
+  // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
+
+  // TODO: check types. actually, do so during semantic analysis
+  Location locus = left->get_locus ();
+
+  return std::unique_ptr<AST::CompoundAssignmentExpr> (
+    new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
+				     CompoundAssignmentOperator::MULTIPLY,
+				     locus));
+}
+
+// Parses a binary division-assignment expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::CompoundAssignmentExpr>
+Parser<ManagedTokenSource>::parse_div_assig_expr (
+  const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
+  AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
+{
+  // parse RHS (as tok has already been consumed in parse_expression)
+  std::unique_ptr<AST::Expr> right
+    = parse_expr (LBP_DIV_ASSIG - 1, AST::AttrVec (), restrictions);
+  if (right == nullptr)
+    return nullptr;
+  // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
+
+  // TODO: check types. actually, do so during semantic analysis
+  Location locus = left->get_locus ();
+
+  return std::unique_ptr<AST::CompoundAssignmentExpr> (
+    new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
+				     CompoundAssignmentOperator::DIVIDE,
+				     locus));
+}
+
+// Parses a binary modulo-assignment expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::CompoundAssignmentExpr>
+Parser<ManagedTokenSource>::parse_mod_assig_expr (
+  const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
+  AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
+{
+  // parse RHS (as tok has already been consumed in parse_expression)
+  std::unique_ptr<AST::Expr> right
+    = parse_expr (LBP_MOD_ASSIG - 1, AST::AttrVec (), restrictions);
+  if (right == nullptr)
+    return nullptr;
+  // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
+
+  // TODO: check types. actually, do so during semantic analysis
+  Location locus = left->get_locus ();
+
+  return std::unique_ptr<AST::CompoundAssignmentExpr> (
+    new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
+				     CompoundAssignmentOperator::MODULUS,
+				     locus));
+}
+
+// Parses a binary and-assignment expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::CompoundAssignmentExpr>
+Parser<ManagedTokenSource>::parse_and_assig_expr (
+  const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
+  AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
+{
+  // parse RHS (as tok has already been consumed in parse_expression)
+  std::unique_ptr<AST::Expr> right
+    = parse_expr (LBP_AMP_ASSIG - 1, AST::AttrVec (), restrictions);
+  if (right == nullptr)
+    return nullptr;
+  // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
+
+  // TODO: check types. actually, do so during semantic analysis
+  Location locus = left->get_locus ();
+
+  return std::unique_ptr<AST::CompoundAssignmentExpr> (
+    new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
+				     CompoundAssignmentOperator::BITWISE_AND,
+				     locus));
+}
+
+// Parses a binary or-assignment expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::CompoundAssignmentExpr>
+Parser<ManagedTokenSource>::parse_or_assig_expr (
+  const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
+  AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
+{
+  // parse RHS (as tok has already been consumed in parse_expression)
+  std::unique_ptr<AST::Expr> right
+    = parse_expr (LBP_PIPE_ASSIG - 1, AST::AttrVec (), restrictions);
+  if (right == nullptr)
+    return nullptr;
+  // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
+
+  // TODO: check types. actually, do so during semantic analysis
+  Location locus = left->get_locus ();
+
+  return std::unique_ptr<AST::CompoundAssignmentExpr> (
+    new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
+				     CompoundAssignmentOperator::BITWISE_OR,
+				     locus));
+}
+
+// Parses a binary xor-assignment expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::CompoundAssignmentExpr>
+Parser<ManagedTokenSource>::parse_xor_assig_expr (
+  const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
+  AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
+{
+  // parse RHS (as tok has already been consumed in parse_expression)
+  std::unique_ptr<AST::Expr> right
+    = parse_expr (LBP_CARET_ASSIG - 1, AST::AttrVec (), restrictions);
+  if (right == nullptr)
+    return nullptr;
+  // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
+
+  // TODO: check types. actually, do so during semantic analysis
+  Location locus = left->get_locus ();
+
+  return std::unique_ptr<AST::CompoundAssignmentExpr> (
+    new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
+				     CompoundAssignmentOperator::BITWISE_XOR,
+				     locus));
+}
+
+// Parses a binary left shift-assignment expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::CompoundAssignmentExpr>
+Parser<ManagedTokenSource>::parse_left_shift_assig_expr (
+  const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
+  AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
+{
+  // parse RHS (as tok has already been consumed in parse_expression)
+  std::unique_ptr<AST::Expr> right
+    = parse_expr (LBP_L_SHIFT_ASSIG - 1, AST::AttrVec (), restrictions);
+  if (right == nullptr)
+    return nullptr;
+  // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
+
+  // TODO: check types. actually, do so during semantic analysis
+  Location locus = left->get_locus ();
+
+  return std::unique_ptr<AST::CompoundAssignmentExpr> (
+    new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
+				     CompoundAssignmentOperator::LEFT_SHIFT,
+				     locus));
+}
+
+// Parses a binary right shift-assignment expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::CompoundAssignmentExpr>
+Parser<ManagedTokenSource>::parse_right_shift_assig_expr (
+  const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
+  AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
+{
+  // parse RHS (as tok has already been consumed in parse_expression)
+  std::unique_ptr<AST::Expr> right
+    = parse_expr (LBP_R_SHIFT_ASSIG - 1, AST::AttrVec (), restrictions);
+  if (right == nullptr)
+    return nullptr;
+  // FIXME: ensure right-associativity for this - 'LBP - 1' may do this?
+
+  // TODO: check types. actually, do so during semantic analysis
+  Location locus = left->get_locus ();
+
+  return std::unique_ptr<AST::CompoundAssignmentExpr> (
+    new AST::CompoundAssignmentExpr (std::move (left), std::move (right),
+				     CompoundAssignmentOperator::RIGHT_SHIFT,
+				     locus));
+}
+
+// Parses a postfix unary await expression (with Pratt parsing).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::AwaitExpr>
+Parser<ManagedTokenSource>::parse_await_expr (
+  const_TokenPtr tok, std::unique_ptr<AST::Expr> expr_to_await,
+  AST::AttrVec outer_attrs)
+{
+  /* skip "await" identifier (as "." has already been consumed in
+   * parse_expression) this assumes that the identifier was already identified
+   * as await */
+  if (!skip_token (IDENTIFIER))
+    {
+      Error error (tok->get_locus (), "failed to skip %<await%> in await expr "
+				      "- this is probably a deep issue");
+      add_error (std::move (error));
+
+      // skip somewhere?
+      return nullptr;
+    }
+
+  // TODO: check inside async block in semantic analysis
+  Location locus = expr_to_await->get_locus ();
+
+  return std::unique_ptr<AST::AwaitExpr> (
+    new AST::AwaitExpr (std::move (expr_to_await), std::move (outer_attrs),
+			locus));
+}
+
+/* Parses an exclusive range ('..') in left denotation position (i.e.
+ * RangeFromExpr or RangeFromToExpr). */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::RangeExpr>
+Parser<ManagedTokenSource>::parse_led_range_exclusive_expr (
+  const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
+  AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
+{
+  // FIXME: this probably parses expressions accidently or whatever
+  // try parsing RHS (as tok has already been consumed in parse_expression)
+  // Can be nullptr, in which case it is a RangeFromExpr, otherwise a
+  // RangeFromToExpr.
+  restrictions.expr_can_be_null = true;
+  std::unique_ptr<AST::Expr> right
+    = parse_expr (LBP_DOT_DOT, AST::AttrVec (), restrictions);
+
+  Location locus = left->get_locus ();
+
+  if (right == nullptr)
+    {
+      // range from expr
+      return std::unique_ptr<AST::RangeFromExpr> (
+	new AST::RangeFromExpr (std::move (left), locus));
+    }
+  else
+    {
+      return std::unique_ptr<AST::RangeFromToExpr> (
+	new AST::RangeFromToExpr (std::move (left), std::move (right), locus));
+    }
+  // FIXME: make non-associative
+}
+
+/* Parses an exclusive range ('..') in null denotation position (i.e.
+ * RangeToExpr or RangeFullExpr). */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::RangeExpr>
+Parser<ManagedTokenSource>::parse_nud_range_exclusive_expr (
+  const_TokenPtr tok, AST::AttrVec outer_attrs ATTRIBUTE_UNUSED)
+{
+  // FIXME: this probably parses expressions accidently or whatever
+  // try parsing RHS (as tok has already been consumed in parse_expression)
+  std::unique_ptr<AST::Expr> right = parse_expr (LBP_DOT_DOT, AST::AttrVec ());
+
+  Location locus = tok->get_locus ();
+
+  if (right == nullptr)
+    {
+      // range from expr
+      return std::unique_ptr<AST::RangeFullExpr> (
+	new AST::RangeFullExpr (locus));
+    }
+  else
+    {
+      return std::unique_ptr<AST::RangeToExpr> (
+	new AST::RangeToExpr (std::move (right), locus));
+    }
+  // FIXME: make non-associative
+}
+
+// Parses a full binary range inclusive expression.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::RangeFromToInclExpr>
+Parser<ManagedTokenSource>::parse_range_inclusive_expr (
+  const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> left,
+  AST::AttrVec outer_attrs ATTRIBUTE_UNUSED, ParseRestrictions restrictions)
+{
+  // parse RHS (as tok has already been consumed in parse_expression)
+  std::unique_ptr<AST::Expr> right
+    = parse_expr (LBP_DOT_DOT_EQ, AST::AttrVec (), restrictions);
+  if (right == nullptr)
+    return nullptr;
+  // FIXME: make non-associative
+
+  // TODO: check types. actually, do so during semantic analysis
+  Location locus = left->get_locus ();
+
+  return std::unique_ptr<AST::RangeFromToInclExpr> (
+    new AST::RangeFromToInclExpr (std::move (left), std::move (right), locus));
+}
+
+// Parses an inclusive range-to prefix unary expression.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::RangeToInclExpr>
+Parser<ManagedTokenSource>::parse_range_to_inclusive_expr (
+  const_TokenPtr tok, AST::AttrVec outer_attrs ATTRIBUTE_UNUSED)
+{
+  // parse RHS (as tok has already been consumed in parse_expression)
+  std::unique_ptr<AST::Expr> right = parse_expr (LBP_DOT_DOT_EQ);
+  if (right == nullptr)
+    return nullptr;
+  // FIXME: make non-associative
+
+  // TODO: check types. actually, do so during semantic analysis
+
+  return std::unique_ptr<AST::RangeToInclExpr> (
+    new AST::RangeToInclExpr (std::move (right), tok->get_locus ()));
+}
+
+// Parses a pseudo-binary infix tuple index expression.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::TupleIndexExpr>
+Parser<ManagedTokenSource>::parse_tuple_index_expr (
+  const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> tuple_expr,
+  AST::AttrVec outer_attrs, ParseRestrictions restrictions ATTRIBUTE_UNUSED)
+{
+  // parse int literal (as token already skipped)
+  const_TokenPtr index_tok = expect_token (INT_LITERAL);
+  if (index_tok == nullptr)
+    {
+      return nullptr;
+    }
+  std::string index = index_tok->get_str ();
+
+  // convert to integer
+  if (!index_tok->is_pure_decimal ())
+    {
+      Error error (index_tok->get_locus (),
+		   "tuple index should be a pure decimal literal");
+      add_error (std::move (error));
+    }
+  int index_int = atoi (index.c_str ());
+
+  Location locus = tuple_expr->get_locus ();
+
+  return std::unique_ptr<AST::TupleIndexExpr> (
+    new AST::TupleIndexExpr (std::move (tuple_expr), index_int,
+			     std::move (outer_attrs), locus));
+}
+
+// Parses a pseudo-binary infix array (or slice) index expression.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ArrayIndexExpr>
+Parser<ManagedTokenSource>::parse_index_expr (
+  const_TokenPtr, std::unique_ptr<AST::Expr> array_expr,
+  AST::AttrVec outer_attrs, ParseRestrictions)
+{
+  // parse RHS (as tok has already been consumed in parse_expression)
+  /*std::unique_ptr<AST::Expr> index_expr
+    = parse_expr (LBP_ARRAY_REF, AST::AttrVec (),
+    restrictions);*/
+  // TODO: conceptually, should treat [] as brackets, so just parse all expr
+  std::unique_ptr<AST::Expr> index_expr = parse_expr ();
+  if (index_expr == nullptr)
+    return nullptr;
+
+  // skip ']' at end of array
+  if (!skip_token (RIGHT_SQUARE))
+    {
+      // skip somewhere?
+      return nullptr;
+    }
+
+  // TODO: check types. actually, do so during semantic analysis
+  Location locus = array_expr->get_locus ();
+
+  return std::unique_ptr<AST::ArrayIndexExpr> (
+    new AST::ArrayIndexExpr (std::move (array_expr), std::move (index_expr),
+			     std::move (outer_attrs), locus));
+}
+
+// Parses a pseudo-binary infix struct field access expression.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::FieldAccessExpr>
+Parser<ManagedTokenSource>::parse_field_access_expr (
+  const_TokenPtr tok ATTRIBUTE_UNUSED, std::unique_ptr<AST::Expr> struct_expr,
+  AST::AttrVec outer_attrs, ParseRestrictions restrictions ATTRIBUTE_UNUSED)
+{
+  /* get field name identifier (assume that this is a field access expr and
+   * not await, for instance) */
+  const_TokenPtr ident_tok = expect_token (IDENTIFIER);
+  if (ident_tok == nullptr)
+    return nullptr;
+
+  Identifier ident = ident_tok->get_str ();
+
+  Location locus = struct_expr->get_locus ();
+
+  // TODO: check types. actually, do so during semantic analysis
+  return std::unique_ptr<AST::FieldAccessExpr> (
+    new AST::FieldAccessExpr (std::move (struct_expr), std::move (ident),
+			      std::move (outer_attrs), locus));
+}
+
+// Parses a pseudo-binary infix method call expression.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::MethodCallExpr>
+Parser<ManagedTokenSource>::parse_method_call_expr (
+  const_TokenPtr tok, std::unique_ptr<AST::Expr> receiver_expr,
+  AST::AttrVec outer_attrs, ParseRestrictions)
+{
+  // parse path expr segment
+  AST::PathExprSegment segment = parse_path_expr_segment ();
+  if (segment.is_error ())
+    {
+      Error error (tok->get_locus (),
+		   "failed to parse path expr segment of method call expr");
+      add_error (std::move (error));
+
+      return nullptr;
+    }
+
+  // skip left parentheses
+  if (!skip_token (LEFT_PAREN))
+    {
+      return nullptr;
+    }
+
+  // parse method params (if they exist)
+  std::vector<std::unique_ptr<AST::Expr>> params;
+
+  const_TokenPtr t = lexer.peek_token ();
+  while (t->get_id () != RIGHT_PAREN)
+    {
+      std::unique_ptr<AST::Expr> param = parse_expr ();
+      if (param == nullptr)
+	{
+	  Error error (t->get_locus (),
+		       "failed to parse method param in method call");
+	  add_error (std::move (error));
+
+	  return nullptr;
+	}
+      params.push_back (std::move (param));
+
+      if (lexer.peek_token ()->get_id () != COMMA)
+	break;
+
+      lexer.skip_token ();
+      t = lexer.peek_token ();
+    }
+
+  // skip right paren
+  if (!skip_token (RIGHT_PAREN))
+    {
+      return nullptr;
+    }
+
+  // TODO: check types. actually do so in semantic analysis pass.
+  Location locus = receiver_expr->get_locus ();
+
+  return std::unique_ptr<AST::MethodCallExpr> (
+    new AST::MethodCallExpr (std::move (receiver_expr), std::move (segment),
+			     std::move (params), std::move (outer_attrs),
+			     locus));
+}
+
+// Parses a pseudo-binary infix function call expression.
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::CallExpr>
+Parser<ManagedTokenSource>::parse_function_call_expr (
+  const_TokenPtr, std::unique_ptr<AST::Expr> function_expr,
+  AST::AttrVec outer_attrs, ParseRestrictions)
+{
+  // parse function params (if they exist)
+  std::vector<std::unique_ptr<AST::Expr>> params;
+
+  const_TokenPtr t = lexer.peek_token ();
+  while (t->get_id () != RIGHT_PAREN)
+    {
+      std::unique_ptr<AST::Expr> param = parse_expr ();
+      if (param == nullptr)
+	{
+	  Error error (t->get_locus (),
+		       "failed to parse function param in function call");
+	  add_error (std::move (error));
+
+	  return nullptr;
+	}
+      params.push_back (std::move (param));
+
+      if (lexer.peek_token ()->get_id () != COMMA)
+	break;
+
+      lexer.skip_token ();
+      t = lexer.peek_token ();
+    }
+
+  // skip ')' at end of param list
+  if (!skip_token (RIGHT_PAREN))
+    {
+      // skip somewhere?
+      return nullptr;
+    }
+
+  // TODO: check types. actually, do so during semantic analysis
+  Location locus = function_expr->get_locus ();
+
+  return std::unique_ptr<AST::CallExpr> (
+    new AST::CallExpr (std::move (function_expr), std::move (params),
+		       std::move (outer_attrs), locus));
+}
+
+/* Parses a macro invocation with a path in expression already parsed (but not
+ * '!' token). */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::MacroInvocation>
+Parser<ManagedTokenSource>::parse_macro_invocation_partial (
+  AST::PathInExpression path, AST::AttrVec outer_attrs,
+  ParseRestrictions restrictions)
+{
+  // macro invocation
+  if (!skip_token (EXCLAM))
+    {
+      return nullptr;
+    }
+
+  // convert PathInExpression to SimplePath - if this isn't possible, error
+  AST::SimplePath converted_path = path.as_simple_path ();
+  if (converted_path.is_empty ())
+    {
+      Error error (lexer.peek_token ()->get_locus (),
+		   "failed to parse simple path in macro invocation");
+      add_error (std::move (error));
+
+      return nullptr;
+    }
+
+  AST::DelimTokenTree tok_tree = parse_delim_token_tree ();
+
+  rust_debug ("successfully parsed macro invocation (via partial)");
+
+  Location macro_locus = converted_path.get_locus ();
+
+  return std::unique_ptr<AST::MacroInvocation> (new AST::MacroInvocation (
+    AST::MacroInvocData (std::move (converted_path), std::move (tok_tree)),
+    std::move (outer_attrs), macro_locus, restrictions.expr_can_be_stmt));
+}
+
+/* Parses a struct expr struct with a path in expression already parsed (but
+ * not
+ * '{' token). */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::StructExprStruct>
+Parser<ManagedTokenSource>::parse_struct_expr_struct_partial (
+  AST::PathInExpression path, AST::AttrVec outer_attrs)
+{
+  // assume struct expr struct (as struct-enum disambiguation requires name
+  // lookup) again, make statement if final ';'
+  if (!skip_token (LEFT_CURLY))
+    {
+      return nullptr;
+    }
+
+  // parse inner attributes
+  AST::AttrVec inner_attrs = parse_inner_attributes ();
+
+  // branch based on next token
+  const_TokenPtr t = lexer.peek_token ();
+  Location path_locus = path.get_locus ();
+  switch (t->get_id ())
+    {
+    case RIGHT_CURLY:
+      // struct with no body
+      lexer.skip_token ();
+
+      return std::unique_ptr<AST::StructExprStruct> (
+	new AST::StructExprStruct (std::move (path), std::move (inner_attrs),
+				   std::move (outer_attrs), path_locus));
+    case DOT_DOT:
+      /* technically this would give a struct base-only struct, but this
+       * algorithm should work too. As such, AST type not happening. */
+    case IDENTIFIER:
+      case INT_LITERAL: {
+	// struct with struct expr fields
+
+	// parse struct expr fields
+	std::vector<std::unique_ptr<AST::StructExprField>> fields;
+
+	while (t->get_id () != RIGHT_CURLY && t->get_id () != DOT_DOT)
+	  {
+	    std::unique_ptr<AST::StructExprField> field
+	      = parse_struct_expr_field ();
+	    if (field == nullptr)
+	      {
+		Error error (t->get_locus (),
+			     "failed to parse struct (or enum) expr field");
+		add_error (std::move (error));
+
+		return nullptr;
+	      }
+
+	    // DEBUG:
+	    rust_debug ("struct/enum expr field validated to not be null");
+
+	    fields.push_back (std::move (field));
+
+	    // DEBUG:
+	    rust_debug ("struct/enum expr field pushed back");
+
+	    if (lexer.peek_token ()->get_id () != COMMA)
+	      {
+		// DEBUG:
+		rust_debug ("lack of comma detected in struct/enum expr "
+			    "fields - break");
+		break;
+	      }
+	    lexer.skip_token ();
+
+	    // DEBUG:
+	    rust_debug ("struct/enum expr fields comma skipped ");
+
+	    t = lexer.peek_token ();
+	  }
+
+	// DEBUG:
+	rust_debug ("struct/enum expr about to parse struct base ");
+
+	// parse struct base if it exists
+	AST::StructBase struct_base = AST::StructBase::error ();
+	if (lexer.peek_token ()->get_id () == DOT_DOT)
+	  {
+	    Location dot_dot_location = lexer.peek_token ()->get_locus ();
+	    lexer.skip_token ();
+
+	    // parse required struct base expr
+	    std::unique_ptr<AST::Expr> base_expr = parse_expr ();
+	    if (base_expr == nullptr)
+	      {
+		Error error (lexer.peek_token ()->get_locus (),
+			     "failed to parse struct base expression in struct "
+			     "expression");
+		add_error (std::move (error));
+
+		return nullptr;
+	      }
+
+	    // DEBUG:
+	    rust_debug ("struct/enum expr - parsed and validated base expr");
+
+	    struct_base
+	      = AST::StructBase (std::move (base_expr), dot_dot_location);
+
+	    // DEBUG:
+	    rust_debug ("assigned struct base to new struct base ");
+	  }
+
+	if (!skip_token (RIGHT_CURLY))
+	  {
+	    return nullptr;
+	  }
+
+	// DEBUG:
+	rust_debug (
+	  "struct/enum expr skipped right curly - done and ready to return");
+
+	return std::unique_ptr<AST::StructExprStructFields> (
+	  new AST::StructExprStructFields (std::move (path), std::move (fields),
+					   path_locus, std::move (struct_base),
+					   std::move (inner_attrs),
+					   std::move (outer_attrs)));
+      }
+    default:
+      add_error (
+	Error (t->get_locus (),
+	       "unrecognised token %qs in struct (or enum) expression - "
+	       "expected %<}%>, identifier, integer literal, or %<..%>",
+	       t->get_token_description ()));
+
+      return nullptr;
+    }
+}
+
+/* Parses a struct expr tuple with a path in expression already parsed (but
+ * not
+ * '(' token).
+ * FIXME: this currently outputs a call expr, as they cannot be disambiguated.
+ * A better solution would be to just get this to call that function directly.
+ * */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::CallExpr>
+Parser<ManagedTokenSource>::parse_struct_expr_tuple_partial (
+  AST::PathInExpression path, AST::AttrVec outer_attrs)
+{
+  if (!skip_token (LEFT_PAREN))
+    {
+      return nullptr;
+    }
+
+  AST::AttrVec inner_attrs = parse_inner_attributes ();
+
+  std::vector<std::unique_ptr<AST::Expr>> exprs;
+
+  const_TokenPtr t = lexer.peek_token ();
+  while (t->get_id () != RIGHT_PAREN)
+    {
+      // parse expression (required)
+      std::unique_ptr<AST::Expr> expr = parse_expr ();
+      if (expr == nullptr)
+	{
+	  Error error (t->get_locus (), "failed to parse expression in "
+					"struct (or enum) expression tuple");
+	  add_error (std::move (error));
+
+	  return nullptr;
+	}
+      exprs.push_back (std::move (expr));
+
+      if (lexer.peek_token ()->get_id () != COMMA)
+	break;
+
+      lexer.skip_token ();
+
+      t = lexer.peek_token ();
+    }
+
+  if (!skip_token (RIGHT_PAREN))
+    {
+      return nullptr;
+    }
+
+  Location path_locus = path.get_locus ();
+
+  auto pathExpr = std::unique_ptr<AST::PathInExpression> (
+    new AST::PathInExpression (std::move (path)));
+
+  return std::unique_ptr<AST::CallExpr> (
+    new AST::CallExpr (std::move (pathExpr), std::move (exprs),
+		       std::move (outer_attrs), path_locus));
+}
+
+/* Parses a path in expression with the first token passed as a parameter (as
+ * it is skipped in token stream). Note that this only parses segment-first
+ * paths, not global ones. */
+template <typename ManagedTokenSource>
+AST::PathInExpression
+Parser<ManagedTokenSource>::parse_path_in_expression_pratt (const_TokenPtr tok)
+{
+  // HACK-y way of making up for pratt-parsing consuming first token
+
+  // DEBUG
+  rust_debug ("current peek token when starting path pratt parse: '%s'",
+	      lexer.peek_token ()->get_token_description ());
+
+  // create segment vector
+  std::vector<AST::PathExprSegment> segments;
+
+  std::string initial_str;
+
+  switch (tok->get_id ())
+    {
+    case IDENTIFIER:
+      initial_str = tok->get_str ();
+      break;
+    case SUPER:
+      initial_str = "super";
+      break;
+    case SELF:
+      initial_str = "self";
+      break;
+    case SELF_ALIAS:
+      initial_str = "Self";
+      break;
+    case CRATE:
+      initial_str = "crate";
+      break;
+    case DOLLAR_SIGN:
+      if (lexer.peek_token ()->get_id () == CRATE)
+	{
+	  initial_str = "$crate";
+	  break;
+	}
+      gcc_fallthrough ();
+    default:
+      add_error (Error (tok->get_locus (),
+			"unrecognised token %qs in path in expression",
+			tok->get_token_description ()));
+
+      return AST::PathInExpression::create_error ();
+    }
+
+  // parse required initial segment
+  AST::PathExprSegment initial_segment (initial_str, tok->get_locus ());
+  // parse generic args (and turbofish), if they exist
+  /* use lookahead to determine if they actually exist (don't want to
+   * accidently parse over next ident segment) */
+  if (lexer.peek_token ()->get_id () == SCOPE_RESOLUTION
+      && lexer.peek_token (1)->get_id () == LEFT_ANGLE)
+    {
+      // skip scope resolution
+      lexer.skip_token ();
+
+      AST::GenericArgs generic_args = parse_path_generic_args ();
+
+      initial_segment
+	= AST::PathExprSegment (AST::PathIdentSegment (initial_str,
+						       tok->get_locus ()),
+				tok->get_locus (), std::move (generic_args));
+    }
+  if (initial_segment.is_error ())
+    {
+      // skip after somewhere?
+      // don't necessarily throw error but yeah
+
+      // DEBUG
+      rust_debug ("initial segment is error - returning null");
+
+      return AST::PathInExpression::create_error ();
+    }
+  segments.push_back (std::move (initial_segment));
+
+  // parse optional segments (as long as scope resolution operator exists)
+  const_TokenPtr t = lexer.peek_token ();
+  while (t->get_id () == SCOPE_RESOLUTION)
+    {
+      // skip scope resolution operator
+      lexer.skip_token ();
+
+      // parse the actual segment - it is an error if it doesn't exist now
+      AST::PathExprSegment segment = parse_path_expr_segment ();
+      if (segment.is_error ())
+	{
+	  // skip after somewhere?
+	  Error error (t->get_locus (),
+		       "could not parse path expression segment");
+	  add_error (std::move (error));
+
+	  return AST::PathInExpression::create_error ();
+	}
+
+      segments.push_back (std::move (segment));
+
+      t = lexer.peek_token ();
+    }
+
+  // DEBUG:
+  rust_debug (
+    "current token (just about to return path to null denotation): '%s'",
+    lexer.peek_token ()->get_token_description ());
+
+  return AST::PathInExpression (std::move (segments), {}, tok->get_locus (),
+				false);
+}
+
+// Parses a closure expression with pratt parsing (from null denotation).
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::ClosureExpr>
+Parser<ManagedTokenSource>::parse_closure_expr_pratt (const_TokenPtr tok,
+						      AST::AttrVec outer_attrs)
+{
+  // TODO: does this need pratt parsing (for precedence)? probably not, but
+  // idk
+  Location locus = tok->get_locus ();
+  bool has_move = false;
+  if (tok->get_id () == MOVE)
+    {
+      has_move = true;
+      tok = lexer.peek_token ();
+      lexer.skip_token ();
+      // skip token and reassign
+    }
+
+  // handle parameter list
+  std::vector<AST::ClosureParam> params;
+
+  switch (tok->get_id ())
+    {
+    case OR:
+      // no parameters, don't skip token
+      break;
+      case PIPE: {
+	// actually may have parameters
+	// don't skip token
+	const_TokenPtr t = lexer.peek_token ();
+	while (t->get_id () != PIPE)
+	  {
+	    AST::ClosureParam param = parse_closure_param ();
+	    if (param.is_error ())
+	      {
+		// TODO is this really an error?
+		Error error (t->get_locus (), "could not parse closure param");
+		add_error (std::move (error));
+
+		return nullptr;
+	      }
+	    params.push_back (std::move (param));
+
+	    if (lexer.peek_token ()->get_id () != COMMA)
+	      {
+		// not an error but means param list is done
+		break;
+	      }
+	    // skip comma
+	    lexer.skip_token ();
+
+	    t = lexer.peek_token ();
+	  }
+
+	if (!skip_token (PIPE))
+	  {
+	    return nullptr;
+	  }
+	break;
+      }
+    default:
+      add_error (Error (tok->get_locus (),
+			"unexpected token %qs in closure expression - expected "
+			"%<|%> or %<||%>",
+			tok->get_token_description ()));
+
+      // skip somewhere?
+      return nullptr;
+    }
+
+  // again branch based on next token
+  tok = lexer.peek_token ();
+  if (tok->get_id () == RETURN_TYPE)
+    {
+      // must be return type closure with block expr
+
+      // skip "return type" token
+      lexer.skip_token ();
+
+      // parse actual type, which is required
+      std::unique_ptr<AST::TypeNoBounds> type = parse_type_no_bounds ();
+      if (type == nullptr)
+	{
+	  // error
+	  Error error (tok->get_locus (), "failed to parse type for closure");
+	  add_error (std::move (error));
+
+	  // skip somewhere?
+	  return nullptr;
+	}
+
+      // parse block expr, which is required
+      std::unique_ptr<AST::BlockExpr> block = parse_block_expr ();
+      if (block == nullptr)
+	{
+	  // error
+	  Error error (lexer.peek_token ()->get_locus (),
+		       "failed to parse block expr in closure");
+	  add_error (std::move (error));
+
+	  // skip somewhere?
+	  return nullptr;
+	}
+
+      return std::unique_ptr<AST::ClosureExprInnerTyped> (
+	new AST::ClosureExprInnerTyped (std::move (type), std::move (block),
+					std::move (params), locus, has_move,
+					std::move (outer_attrs)));
+    }
+  else
+    {
+      // must be expr-only closure
+
+      // parse expr, which is required
+      std::unique_ptr<AST::Expr> expr = parse_expr ();
+      if (expr == nullptr)
+	{
+	  Error error (tok->get_locus (),
+		       "failed to parse expression in closure");
+	  add_error (std::move (error));
+
+	  // skip somewhere?
+	  return nullptr;
+	}
+
+      return std::unique_ptr<AST::ClosureExprInner> (
+	new AST::ClosureExprInner (std::move (expr), std::move (params), locus,
+				   has_move, std::move (outer_attrs)));
+    }
+}
+
+/* Parses a tuple index expression (pratt-parsed) from a 'float' token as a
+ * result of lexer misidentification. */
+template <typename ManagedTokenSource>
+std::unique_ptr<AST::TupleIndexExpr>
+Parser<ManagedTokenSource>::parse_tuple_index_expr_float (
+  const_TokenPtr tok, std::unique_ptr<AST::Expr> tuple_expr,
+  AST::AttrVec outer_attrs, ParseRestrictions restrictions ATTRIBUTE_UNUSED)
+{
+  // only works on float literals
+  if (tok->get_id () != FLOAT_LITERAL)
+    return nullptr;
+
+  // DEBUG:
+  rust_debug ("exact string form of float: '%s'", tok->get_str ().c_str ());
+
+  // get float string and remove dot and initial 0
+  std::string index_str = tok->get_str ();
+  index_str.erase (index_str.begin ());
+
+  // get int from string
+  int index = atoi (index_str.c_str ());
+
+  Location locus = tuple_expr->get_locus ();
+
+  return std::unique_ptr<AST::TupleIndexExpr> (
+    new AST::TupleIndexExpr (std::move (tuple_expr), index,
+			     std::move (outer_attrs), locus));
+}
+
+// Returns true if the next token is END, ELSE, or EOF;
+template <typename ManagedTokenSource>
+bool
+Parser<ManagedTokenSource>::done_end_or_else ()
+{
+  const_TokenPtr t = lexer.peek_token ();
+  return (t->get_id () == RIGHT_CURLY || t->get_id () == ELSE
+	  || t->get_id () == END_OF_FILE);
+}
+
+// Returns true if the next token is END or EOF.
+template <typename ManagedTokenSource>
+bool
+Parser<ManagedTokenSource>::done_end ()
+{
+  const_TokenPtr t = lexer.peek_token ();
+  return (t->get_id () == RIGHT_CURLY || t->get_id () == END_OF_FILE);
+}
+
+// Dumps lexer output to stderr.
+template <typename ManagedTokenSource>
+void
+Parser<ManagedTokenSource>::debug_dump_lex_output (std::ostream &out)
+{
+  /* TODO: a better implementation of "lexer dump" (as in dump what was
+   * actually tokenised) would actually be to "write" a token to a file every
+   * time skip_token() here was called. This would reflect the parser
+   * modifications to the token stream, such as fixing the template angle
+   * brackets. */
+
+  const_TokenPtr tok = lexer.peek_token ();
+
+  while (true)
+    {
+      if (tok->get_id () == Rust::END_OF_FILE)
+	break;
+
+      bool has_text = tok->get_id () == Rust::IDENTIFIER
+		      || tok->get_id () == Rust::INT_LITERAL
+		      || tok->get_id () == Rust::FLOAT_LITERAL
+		      || tok->get_id () == Rust::STRING_LITERAL
+		      || tok->get_id () == Rust::CHAR_LITERAL
+		      || tok->get_id () == Rust::BYTE_STRING_LITERAL
+		      || tok->get_id () == Rust::BYTE_CHAR_LITERAL;
+
+      Location loc = tok->get_locus ();
+
+      out << "<id=";
+      out << tok->token_id_to_str ();
+      out << has_text ? (std::string (", text=") + tok->get_str ()
+			 + std::string (", typehint=")
+			 + std::string (tok->get_type_hint_str ()))
+		      : "";
+      out << lexer.get_line_map ()->to_string (loc);
+
+      lexer.skip_token ();
+      tok = lexer.peek_token ();
+    }
+}
+
+// Parses crate and dumps AST to stderr, recursively.
+template <typename ManagedTokenSource>
+void
+Parser<ManagedTokenSource>::debug_dump_ast_output (AST::Crate &crate,
+						   std::ostream &out)
+{
+  out << crate.as_string ();
+}
+} // namespace Rust
diff --git a/gcc/rust/parse/rust-parse.cc b/gcc/rust/parse/rust-parse.cc
new file mode 100644
index 00000000000..f1e2caa258b
--- /dev/null
+++ b/gcc/rust/parse/rust-parse.cc
@@ -0,0 +1,328 @@
+/* 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/>. */
+
+#include "rust-parse.h"
+#include "rust-linemap.h"
+#include "rust-diagnostics.h"
+
+namespace Rust {
+
+std::string
+extract_module_path (const AST::AttrVec &inner_attrs,
+		     const AST::AttrVec &outer_attrs, const std::string &name)
+{
+  AST::Attribute path_attr = AST::Attribute::create_empty ();
+  for (const auto &attr : inner_attrs)
+    {
+      if (attr.get_path ().as_string () == "path")
+	{
+	  path_attr = attr;
+	  break;
+	}
+    }
+
+  // Here, we found a path attribute, but it has no associated string. This is
+  // invalid
+  if (!path_attr.is_empty () && !path_attr.has_attr_input ())
+    {
+      rust_error_at (
+	path_attr.get_locus (),
+	// Split the format string so that -Wformat-diag does not complain...
+	"path attributes must contain a filename: '%s'", "#![path = \"file\"]");
+      return name;
+    }
+
+  for (const auto &attr : outer_attrs)
+    {
+      if (attr.get_path ().as_string () == "path")
+	{
+	  path_attr = attr;
+	  break;
+	}
+    }
+
+  // We didn't find a path attribute. This is not an error, there simply isn't
+  // one present
+  if (path_attr.is_empty ())
+    return name;
+
+  // Here, we found a path attribute, but it has no associated string. This is
+  // invalid
+  if (!path_attr.has_attr_input ())
+    {
+      rust_error_at (
+	path_attr.get_locus (),
+	// Split the format string so that -Wformat-diag does not complain...
+	"path attributes must contain a filename: '%s'", "#[path = \"file\"]");
+      return name;
+    }
+
+  auto path_value = path_attr.get_attr_input ().as_string ();
+
+  // At this point, the 'path' is of the following format: '= "<file.rs>"'
+  // We need to remove the equal sign and only keep the actual filename.
+  // In order to do this, we can simply go through the string until we find
+  // a character that is not an equal sign or whitespace
+  auto filename_begin = path_value.find_first_not_of ("=\t ");
+
+  auto path = path_value.substr (filename_begin);
+
+  // On windows, the path might mix '/' and '\' separators. Replace the
+  // UNIX-like separators by MSDOS separators to make sure the path will resolve
+  // properly.
+  //
+  // Source: rustc compiler
+  // (https://github.com/rust-lang/rust/blob/9863bf51a52b8e61bcad312f81b5193d53099f9f/compiler/rustc_expand/src/module.rs#L174)
+#if defined(HAVE_DOS_BASED_FILE_SYSTEM)
+  path.replace ('/', '\\');
+#endif /* HAVE_DOS_BASED_FILE_SYSTEM */
+
+  return path;
+}
+
+template <typename T>
+static bool
+contains (std::vector<T> &vec, T elm)
+{
+  return std::find (vec.begin (), vec.end (), elm) != vec.end ();
+}
+
+/**
+ * Avoid UB by calling .front() and .back() on empty containers...
+ */
+
+template <typename T>
+static const T *
+get_back_ptr (const std::vector<std::unique_ptr<T>> &values)
+{
+  if (values.empty ())
+    return nullptr;
+
+  return values.back ().get ();
+}
+
+template <typename T>
+static const T *
+get_front_ptr (const std::vector<std::unique_ptr<T>> &values)
+{
+  if (values.empty ())
+    return nullptr;
+
+  return values.front ().get ();
+}
+
+static bool
+peculiar_fragment_match_compatible_fragment (
+  const AST::MacroFragSpec &last_spec, const AST::MacroFragSpec &spec,
+  Location match_locus)
+{
+  static std::unordered_map<AST::MacroFragSpec::Kind,
+			    std::vector<AST::MacroFragSpec::Kind>>
+    fragment_follow_set
+    = {{AST::MacroFragSpec::PATH, {AST::MacroFragSpec::BLOCK}},
+       {AST::MacroFragSpec::TY, {AST::MacroFragSpec::BLOCK}},
+       {AST::MacroFragSpec::VIS,
+	{AST::MacroFragSpec::IDENT, AST::MacroFragSpec::TY,
+	 AST::MacroFragSpec::PATH}}};
+
+  auto is_valid
+    = contains (fragment_follow_set[last_spec.get_kind ()], spec.get_kind ());
+
+  if (!is_valid)
+    rust_error_at (
+      match_locus,
+      "fragment specifier %<%s%> is not allowed after %<%s%> fragments",
+      spec.as_string ().c_str (), last_spec.as_string ().c_str ());
+
+  return is_valid;
+}
+
+static bool
+peculiar_fragment_match_compatible (const AST::MacroMatchFragment &last_match,
+				    const AST::MacroMatch &match)
+{
+  static std::unordered_map<AST::MacroFragSpec::Kind, std::vector<TokenId>>
+    follow_set
+    = {{AST::MacroFragSpec::EXPR, {MATCH_ARROW, COMMA, SEMICOLON}},
+       {AST::MacroFragSpec::STMT, {MATCH_ARROW, COMMA, SEMICOLON}},
+       {AST::MacroFragSpec::PAT, {MATCH_ARROW, COMMA, EQUAL, PIPE, IF, IN}},
+       {AST::MacroFragSpec::PATH,
+	{MATCH_ARROW, COMMA, EQUAL, PIPE, SEMICOLON, COLON, RIGHT_ANGLE,
+	 RIGHT_SHIFT, LEFT_SQUARE, LEFT_CURLY, AS, WHERE}},
+       {AST::MacroFragSpec::TY,
+	{MATCH_ARROW, COMMA, EQUAL, PIPE, SEMICOLON, COLON, RIGHT_ANGLE,
+	 RIGHT_SHIFT, LEFT_SQUARE, LEFT_CURLY, AS, WHERE}},
+       {AST::MacroFragSpec::VIS,
+	{
+	  COMMA,
+	  IDENTIFIER /* FIXME: Other than `priv` */,
+	  LEFT_PAREN,
+	  LEFT_SQUARE,
+	  EXCLAM,
+	  ASTERISK,
+	  AMP,
+	  LOGICAL_AND,
+	  QUESTION_MARK,
+	  LIFETIME,
+	  LEFT_ANGLE,
+	  LEFT_SHIFT,
+	  SUPER,
+	  SELF,
+	  SELF_ALIAS,
+	  EXTERN_TOK,
+	  CRATE,
+	  UNDERSCORE,
+	  FOR,
+	  IMPL,
+	  FN_TOK,
+	  UNSAFE,
+	  TYPEOF,
+	  DYN
+	  // FIXME: Add Non kw identifiers
+	  // FIXME: Add $crate as valid
+	}}};
+
+  Location error_locus = match.get_match_locus ();
+  std::string kind_str = "fragment";
+  auto &allowed_toks = follow_set[last_match.get_frag_spec ().get_kind ()];
+
+  // There are two behaviors to handle here: If the follow-up match is a token,
+  // we want to check if it is allowed.
+  // If it is a fragment, repetition or matcher then we know that it will be
+  // an error.
+  // For repetitions and matchers we want to extract a proper location to report
+  // the error.
+  switch (match.get_macro_match_type ())
+    {
+      case AST::MacroMatch::Tok: {
+	auto tok = static_cast<const AST::Token *> (&match);
+	if (contains (allowed_toks, tok->get_id ()))
+	  return true;
+	kind_str = "token `"
+		   + std::string (get_token_description (tok->get_id ())) + "`";
+	error_locus = tok->get_match_locus ();
+	break;
+      }
+      break;
+      case AST::MacroMatch::Repetition: {
+	auto repetition
+	  = static_cast<const AST::MacroMatchRepetition *> (&match);
+	auto &matches = repetition->get_matches ();
+	auto first_frag = get_front_ptr (matches);
+	if (first_frag)
+	  return peculiar_fragment_match_compatible (last_match, *first_frag);
+	break;
+      }
+      case AST::MacroMatch::Matcher: {
+	auto matcher = static_cast<const AST::MacroMatcher *> (&match);
+	auto first_token = matcher->get_delim_type ();
+	TokenId delim_id;
+	switch (first_token)
+	  {
+	  case AST::PARENS:
+	    delim_id = LEFT_PAREN;
+	    break;
+	  case AST::SQUARE:
+	    delim_id = LEFT_SQUARE;
+	    break;
+	  case AST::CURLY:
+	    delim_id = LEFT_CURLY;
+	    break;
+	  default:
+	    gcc_unreachable ();
+	    break;
+	  }
+	if (contains (allowed_toks, delim_id))
+	  return true;
+	kind_str = "token `" + std::string (get_token_description (delim_id))
+		   + "` at start of matcher";
+	error_locus = matcher->get_match_locus ();
+	break;
+      }
+      case AST::MacroMatch::Fragment: {
+	auto last_spec = last_match.get_frag_spec ();
+	auto fragment = static_cast<const AST::MacroMatchFragment *> (&match);
+	if (last_spec.has_follow_set_fragment_restrictions ())
+	  return peculiar_fragment_match_compatible_fragment (
+	    last_spec, fragment->get_frag_spec (), match.get_match_locus ());
+      }
+      break;
+    }
+
+  rust_error_at (error_locus, "%s is not allowed after %<%s%> fragment",
+		 kind_str.c_str (),
+		 last_match.get_frag_spec ().as_string ().c_str ());
+  auto allowed_toks_str
+    = "`" + std::string (get_token_description (allowed_toks[0])) + "`";
+  for (size_t i = 1; i < allowed_toks.size (); i++)
+    allowed_toks_str
+      += ", `" + std::string (get_token_description (allowed_toks[i])) + "`";
+
+  rust_inform (error_locus, "allowed tokens are %s", allowed_toks_str.c_str ());
+
+  return false;
+}
+
+bool
+is_match_compatible (const AST::MacroMatch &last_match,
+		     const AST::MacroMatch &match)
+{
+  const AST::MacroMatch *new_last = nullptr;
+
+  // We want to "extract" the concerning matches. In cases such as matchers and
+  // repetitions, we actually store multiple matchers, but are only concerned
+  // about the follow-set ambiguities of certain elements.
+  // There are some cases where we can short-circuit the algorithm: There will
+  // never be restrictions on token literals, or on certain fragments which do
+  // not have a set of follow-restrictions.
+
+  switch (last_match.get_macro_match_type ())
+    {
+      // This is our main stop condition: When we are finally looking at the
+      // last match (or its actual last component), and it is a fragment, it
+      // may contain some follow up restrictions.
+      case AST::MacroMatch::Fragment: {
+	auto fragment
+	  = static_cast<const AST::MacroMatchFragment *> (&last_match);
+	if (fragment->get_frag_spec ().has_follow_set_restrictions ())
+	  return peculiar_fragment_match_compatible (*fragment, match);
+	else
+	  return true;
+      }
+      case AST::MacroMatch::Repetition: {
+	// A repetition on the left hand side means we want to make sure the
+	// last match of the repetition is compatible with the new match
+	auto repetition
+	  = static_cast<const AST::MacroMatchRepetition *> (&last_match);
+	new_last = get_back_ptr (repetition->get_matches ());
+	// If there are no matches in the matcher, then it can be followed by
+	// anything
+	if (!new_last)
+	  return true;
+	break;
+      }
+    case AST::MacroMatch::Matcher:
+    case AST::MacroMatch::Tok:
+      return true;
+    }
+
+  rust_assert (new_last);
+
+  // We check recursively until we find a terminating condition
+  // FIXME: Does expansion depth/limit matter here?
+  return is_match_compatible (*new_last, match);
+}
+} // namespace Rust
diff --git a/gcc/rust/parse/rust-parse.h b/gcc/rust/parse/rust-parse.h
new file mode 100644
index 00000000000..e4c5a2c5c9f
--- /dev/null
+++ b/gcc/rust/parse/rust-parse.h
@@ -0,0 +1,732 @@
+/* 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_PARSE_H
+#define RUST_PARSE_H
+
+#include "rust-lex.h"
+#include "rust-ast-full.h"
+#include "rust-diagnostics.h"
+
+namespace Rust {
+/* HACK: used to resolve the expression-or-statement problem at the end of a
+ * block by allowing either to be returned (technically). Tagged union would
+ * probably take up the same amount of space. */
+struct ExprOrStmt
+{
+  std::unique_ptr<AST::Expr> expr;
+  std::unique_ptr<AST::Stmt> stmt;
+
+  /* I was going to resist the urge to make this a real class and make it POD,
+   * but construction in steps is too difficult. So it'll just also have a
+   * constructor. */
+
+  // expression constructor
+  ExprOrStmt (std::unique_ptr<AST::Expr> expr) : expr (std::move (expr)) {}
+
+  // statement constructor
+  ExprOrStmt (std::unique_ptr<AST::Stmt> stmt) : stmt (std::move (stmt)) {}
+
+  // macro constructor
+  ExprOrStmt (std::unique_ptr<AST::MacroInvocation> macro)
+    : expr (std::move (macro))
+  {}
+
+  // Returns whether this object is in an error state.
+  bool is_error () const
+  {
+    return (expr == nullptr && stmt == nullptr)
+	   || (expr != nullptr && stmt != nullptr);
+  }
+
+  // Returns an error state object.
+  static ExprOrStmt create_error () { return ExprOrStmt (nullptr, nullptr); }
+
+  ~ExprOrStmt () = default;
+
+  /* no copy constructors/assignment as simple object like this shouldn't
+   * require it */
+
+  // move constructors
+  ExprOrStmt (ExprOrStmt &&other) = default;
+  ExprOrStmt &operator= (ExprOrStmt &&other) = default;
+
+private:
+  // private constructor only used for creating error state expr or stmt objects
+  ExprOrStmt (AST::Expr *expr, AST::Stmt *stmt) : expr (expr), stmt (stmt) {}
+
+  // make this work: have a disambiguation specifically for known statements
+  // (i.e. ';' and 'let'). then, have a special "parse expr or stmt" function
+  // that returns this type. inside it, it parses an expression, and then
+  // determines whether to return expr or stmt via whether the next token is a
+  // semicolon. should be able to disambiguate inside that function between
+  // stmts with blocks and without blocks.
+};
+
+/* Restrictions on parsing used to signal that certain ambiguous grammar
+ * features should be parsed in a certain way. */
+struct ParseRestrictions
+{
+  bool can_be_struct_expr = true;
+  /* Whether the expression was entered from a unary expression - prevents stuff
+   * like struct exprs being parsed from a dereference. */
+  bool entered_from_unary = false;
+  bool expr_can_be_null = false;
+  bool expr_can_be_stmt = false;
+  bool consume_semi = true;
+};
+
+// Parser implementation for gccrs.
+// TODO: if updated to C++20, ManagedTokenSource would be useful as a concept
+template <typename ManagedTokenSource> class Parser
+{
+public:
+  /**
+   * Consume a token, reporting an error if it isn't the next token
+   *
+   * @param t ID of the token to consume
+   *
+   * @return true if the token was next, false if it wasn't found
+   */
+  bool skip_token (TokenId t);
+
+  /**
+   * Same as `skip_token` but allows for failure without necessarily reporting
+   * an error
+   *
+   * @param t ID of the token to consume
+   *
+   * @return true if the token was next, false if it wasn't found
+   */
+  bool maybe_skip_token (TokenId t);
+
+  std::unique_ptr<AST::Expr>
+  parse_expr (AST::AttrVec outer_attrs = AST::AttrVec (),
+	      ParseRestrictions restrictions = ParseRestrictions ());
+
+  std::unique_ptr<AST::LiteralExpr> parse_literal_expr (AST::AttrVec outer_attrs
+							= AST::AttrVec ());
+
+  std::unique_ptr<AST::BlockExpr>
+  parse_block_expr (AST::AttrVec outer_attrs = AST::AttrVec (),
+		    Location pratt_parsed_loc = Linemap::unknown_location ());
+
+  std::unique_ptr<AST::Item> parse_item (bool called_from_statement);
+  std::unique_ptr<AST::Pattern> parse_pattern ();
+
+  /**
+   * Parse a statement
+   *
+   * Statement : ';'
+   *    | Item
+   *    | LetStatement
+   *    | ExpressionStatement
+   *    | MacroInvocationSemi
+   */
+  std::unique_ptr<AST::Stmt> parse_stmt (ParseRestrictions restrictions
+					 = ParseRestrictions ());
+  std::unique_ptr<AST::Type> parse_type (bool save_errors = true);
+  std::unique_ptr<AST::ExternalItem> parse_external_item ();
+  std::unique_ptr<AST::TraitItem> parse_trait_item ();
+  std::unique_ptr<AST::InherentImplItem> parse_inherent_impl_item ();
+  std::unique_ptr<AST::TraitImplItem> parse_trait_impl_item ();
+  AST::PathInExpression parse_path_in_expression ();
+  std::vector<std::unique_ptr<AST::LifetimeParam> > parse_lifetime_params ();
+  AST::Visibility parse_visibility ();
+  std::unique_ptr<AST::IdentifierPattern> parse_identifier_pattern ();
+  std::unique_ptr<AST::TokenTree> parse_token_tree ();
+  AST::Attribute parse_attribute_body ();
+  AST::AttrVec parse_inner_attributes ();
+
+private:
+  void skip_after_semicolon ();
+  void skip_after_end ();
+  void skip_after_end_block ();
+  void skip_after_next_block ();
+  void skip_after_end_attribute ();
+
+  const_TokenPtr expect_token (TokenId t);
+  void unexpected_token (const_TokenPtr t);
+  bool skip_generics_right_angle ();
+
+  void parse_statement_seq (bool (Parser::*done) ());
+
+  // AST-related stuff - maybe move or something?
+  AST::Attribute parse_inner_attribute ();
+  AST::AttrVec parse_outer_attributes ();
+  AST::Attribute parse_outer_attribute ();
+  std::unique_ptr<AST::AttrInput> parse_attr_input ();
+  AST::Attribute parse_doc_comment ();
+
+  // Path-related
+  AST::SimplePath parse_simple_path ();
+  AST::SimplePathSegment parse_simple_path_segment ();
+  AST::TypePath parse_type_path ();
+  std::unique_ptr<AST::TypePathSegment> parse_type_path_segment ();
+  AST::PathIdentSegment parse_path_ident_segment ();
+  AST::GenericArg parse_generic_arg ();
+  AST::GenericArgs parse_path_generic_args ();
+  AST::GenericArgsBinding parse_generic_args_binding ();
+  AST::TypePathFunction parse_type_path_function (Location locus);
+  AST::PathExprSegment parse_path_expr_segment ();
+  AST::QualifiedPathInExpression
+  // When given a pratt_parsed_loc, use it as the location of the
+  // first token parsed in the expression (the parsing of that first
+  // token should be skipped).
+  parse_qualified_path_in_expression (Location pratt_parsed_loc
+				      = Linemap::unknown_location ());
+  AST::QualifiedPathType
+  parse_qualified_path_type (Location pratt_parsed_loc
+			     = Linemap::unknown_location ());
+  AST::QualifiedPathInType parse_qualified_path_in_type ();
+
+  // Token tree or macro related
+  AST::DelimTokenTree parse_delim_token_tree ();
+  std::unique_ptr<AST::MacroRulesDefinition>
+  parse_macro_rules_def (AST::AttrVec outer_attrs);
+  std::unique_ptr<AST::MacroInvocation>
+  parse_macro_invocation_semi (AST::AttrVec outer_attrs);
+  std::unique_ptr<AST::MacroInvocation>
+  parse_macro_invocation (AST::AttrVec outer_attrs);
+  AST::MacroRule parse_macro_rule ();
+  AST::MacroMatcher parse_macro_matcher ();
+  std::unique_ptr<AST::MacroMatch> parse_macro_match ();
+  std::unique_ptr<AST::MacroMatchFragment> parse_macro_match_fragment ();
+  std::unique_ptr<AST::MacroMatchRepetition> parse_macro_match_repetition ();
+
+  // Top-level item-related
+  std::unique_ptr<AST::VisItem> parse_vis_item (AST::AttrVec outer_attrs);
+  std::unique_ptr<AST::MacroItem> parse_macro_item (AST::AttrVec outer_attrs);
+
+  // VisItem subclass-related
+  std::unique_ptr<AST::Module> parse_module (AST::Visibility vis,
+					     AST::AttrVec outer_attrs);
+  std::unique_ptr<AST::ExternCrate>
+  parse_extern_crate (AST::Visibility vis, AST::AttrVec outer_attrs);
+  std::unique_ptr<AST::UseDeclaration>
+  parse_use_decl (AST::Visibility vis, AST::AttrVec outer_attrs);
+  std::unique_ptr<AST::UseTree> parse_use_tree ();
+  std::unique_ptr<AST::Function> parse_function (AST::Visibility vis,
+						 AST::AttrVec outer_attrs);
+  AST::FunctionQualifiers parse_function_qualifiers ();
+  std::vector<std::unique_ptr<AST::GenericParam> >
+  parse_generic_params_in_angles ();
+  template <typename EndTokenPred>
+  std::vector<std::unique_ptr<AST::GenericParam> >
+  parse_generic_params (EndTokenPred is_end_token);
+  template <typename EndTokenPred>
+  std::unique_ptr<AST::GenericParam>
+  parse_generic_param (EndTokenPred is_end_token);
+
+  template <typename EndTokenPred>
+  std::vector<std::unique_ptr<AST::LifetimeParam> >
+  parse_lifetime_params (EndTokenPred is_end_token);
+  std::vector<AST::LifetimeParam> parse_lifetime_params_objs ();
+  template <typename EndTokenPred>
+  std::vector<AST::LifetimeParam>
+  parse_lifetime_params_objs (EndTokenPred is_end_token);
+  template <typename ParseFunction, typename EndTokenPred>
+  auto parse_non_ptr_sequence (
+    ParseFunction parsing_function, EndTokenPred is_end_token,
+    std::string error_msg = "failed to parse generic param in generic params")
+    -> std::vector<decltype (parsing_function ())>;
+  AST::LifetimeParam parse_lifetime_param ();
+  std::vector<std::unique_ptr<AST::TypeParam> > parse_type_params ();
+  template <typename EndTokenPred>
+  std::vector<std::unique_ptr<AST::TypeParam> >
+  parse_type_params (EndTokenPred is_end_token);
+  std::unique_ptr<AST::TypeParam> parse_type_param ();
+  template <typename EndTokenPred>
+  std::vector<AST::FunctionParam>
+  parse_function_params (EndTokenPred is_end_token);
+  AST::FunctionParam parse_function_param ();
+  std::unique_ptr<AST::Type> parse_function_return_type ();
+  AST::WhereClause parse_where_clause ();
+  std::unique_ptr<AST::WhereClauseItem> parse_where_clause_item ();
+  std::unique_ptr<AST::LifetimeWhereClauseItem>
+  parse_lifetime_where_clause_item ();
+  std::unique_ptr<AST::TypeBoundWhereClauseItem>
+  parse_type_bound_where_clause_item ();
+  std::vector<AST::LifetimeParam> parse_for_lifetimes ();
+  template <typename EndTokenPred>
+  std::vector<std::unique_ptr<AST::TypeParamBound> >
+  parse_type_param_bounds (EndTokenPred is_end_token);
+  std::vector<std::unique_ptr<AST::TypeParamBound> > parse_type_param_bounds ();
+  std::unique_ptr<AST::TypeParamBound> parse_type_param_bound ();
+  std::unique_ptr<AST::TraitBound> parse_trait_bound ();
+  std::vector<AST::Lifetime> parse_lifetime_bounds ();
+  template <typename EndTokenPred>
+  std::vector<AST::Lifetime> parse_lifetime_bounds (EndTokenPred is_end_token);
+  AST::Lifetime parse_lifetime ();
+  std::unique_ptr<AST::TypeAlias> parse_type_alias (AST::Visibility vis,
+						    AST::AttrVec outer_attrs);
+  std::unique_ptr<AST::Struct> parse_struct (AST::Visibility vis,
+					     AST::AttrVec outer_attrs);
+  std::vector<AST::StructField> parse_struct_fields ();
+  template <typename EndTokenPred>
+  std::vector<AST::StructField> parse_struct_fields (EndTokenPred is_end_token);
+  AST::StructField parse_struct_field ();
+  std::vector<AST::TupleField> parse_tuple_fields ();
+  AST::TupleField parse_tuple_field ();
+  std::unique_ptr<AST::Enum> parse_enum (AST::Visibility vis,
+					 AST::AttrVec outer_attrs);
+  std::vector<std::unique_ptr<AST::EnumItem> > parse_enum_items ();
+  template <typename EndTokenPred>
+  std::vector<std::unique_ptr<AST::EnumItem> >
+  parse_enum_items (EndTokenPred is_end_token);
+  std::unique_ptr<AST::EnumItem> parse_enum_item ();
+  std::unique_ptr<AST::Union> parse_union (AST::Visibility vis,
+					   AST::AttrVec outer_attrs);
+  std::unique_ptr<AST::ConstantItem>
+  parse_const_item (AST::Visibility vis, AST::AttrVec outer_attrs);
+  std::unique_ptr<AST::StaticItem> parse_static_item (AST::Visibility vis,
+						      AST::AttrVec outer_attrs);
+  std::unique_ptr<AST::Trait> parse_trait (AST::Visibility vis,
+					   AST::AttrVec outer_attrs);
+  std::unique_ptr<AST::TraitItemType>
+  parse_trait_type (AST::AttrVec outer_attrs);
+  std::unique_ptr<AST::TraitItemConst>
+  parse_trait_const (AST::AttrVec outer_attrs);
+  AST::SelfParam parse_self_param ();
+  std::unique_ptr<AST::Impl> parse_impl (AST::Visibility vis,
+					 AST::AttrVec outer_attrs);
+  std::unique_ptr<AST::InherentImplItem>
+  parse_inherent_impl_function_or_method (AST::Visibility vis,
+					  AST::AttrVec outer_attrs);
+  std::unique_ptr<AST::TraitImplItem>
+  parse_trait_impl_function_or_method (AST::Visibility vis,
+				       AST::AttrVec outer_attrs);
+  std::unique_ptr<AST::ExternBlock>
+  parse_extern_block (AST::Visibility vis, AST::AttrVec outer_attrs);
+  AST::NamedFunctionParam parse_named_function_param (AST::AttrVec outer_attrs
+						      = AST::AttrVec ());
+  AST::Method parse_method ();
+
+  // Expression-related (Pratt parsed)
+  std::unique_ptr<AST::Expr>
+  parse_expr (int right_binding_power,
+	      AST::AttrVec outer_attrs = AST::AttrVec (),
+	      ParseRestrictions restrictions = ParseRestrictions ());
+  std::unique_ptr<AST::Expr>
+  null_denotation (const_TokenPtr t, AST::AttrVec outer_attrs = AST::AttrVec (),
+		   ParseRestrictions restrictions = ParseRestrictions ());
+  std::unique_ptr<AST::Expr>
+  left_denotation (const_TokenPtr t, std::unique_ptr<AST::Expr> left,
+		   AST::AttrVec outer_attrs = AST::AttrVec (),
+		   ParseRestrictions restrictions = ParseRestrictions ());
+  std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+  parse_arithmetic_or_logical_expr (
+    const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+    AST::AttrVec outer_attrs, AST::ArithmeticOrLogicalExpr::ExprType expr_type,
+    ParseRestrictions restrictions = ParseRestrictions ());
+  std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+  parse_binary_plus_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+			  AST::AttrVec outer_attrs,
+			  ParseRestrictions restrictions
+			  = ParseRestrictions ());
+  std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+  parse_binary_minus_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+			   AST::AttrVec outer_attrs,
+			   ParseRestrictions restrictions
+			   = ParseRestrictions ());
+  std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+  parse_binary_mult_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+			  AST::AttrVec outer_attrs,
+			  ParseRestrictions restrictions
+			  = ParseRestrictions ());
+  std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+  parse_binary_div_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+			 AST::AttrVec outer_attrs,
+			 ParseRestrictions restrictions = ParseRestrictions ());
+  std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+  parse_binary_mod_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+			 AST::AttrVec outer_attrs,
+			 ParseRestrictions restrictions = ParseRestrictions ());
+  std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+  parse_bitwise_and_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+			  AST::AttrVec outer_attrs,
+			  ParseRestrictions restrictions
+			  = ParseRestrictions ());
+  std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+  parse_bitwise_or_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+			 AST::AttrVec outer_attrs,
+			 ParseRestrictions restrictions = ParseRestrictions ());
+  std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+  parse_bitwise_xor_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+			  AST::AttrVec outer_attrs,
+			  ParseRestrictions restrictions
+			  = ParseRestrictions ());
+  std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+  parse_left_shift_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+			 AST::AttrVec outer_attrs,
+			 ParseRestrictions restrictions = ParseRestrictions ());
+  std::unique_ptr<AST::ArithmeticOrLogicalExpr>
+  parse_right_shift_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+			  AST::AttrVec outer_attrs,
+			  ParseRestrictions restrictions
+			  = ParseRestrictions ());
+  std::unique_ptr<AST::ComparisonExpr>
+  parse_comparison_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+			 AST::AttrVec outer_attrs,
+			 AST::ComparisonExpr::ExprType expr_type,
+			 ParseRestrictions restrictions = ParseRestrictions ());
+  std::unique_ptr<AST::ComparisonExpr>
+  parse_binary_equal_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+			   AST::AttrVec outer_attrs,
+			   ParseRestrictions restrictions
+			   = ParseRestrictions ());
+  std::unique_ptr<AST::ComparisonExpr> parse_binary_not_equal_expr (
+    const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+    AST::AttrVec outer_attrs,
+    ParseRestrictions restrictions = ParseRestrictions ());
+  std::unique_ptr<AST::ComparisonExpr> parse_binary_greater_than_expr (
+    const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+    AST::AttrVec outer_attrs,
+    ParseRestrictions restrictions = ParseRestrictions ());
+  std::unique_ptr<AST::ComparisonExpr> parse_binary_less_than_expr (
+    const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+    AST::AttrVec outer_attrs,
+    ParseRestrictions restrictions = ParseRestrictions ());
+  std::unique_ptr<AST::ComparisonExpr> parse_binary_greater_equal_expr (
+    const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+    AST::AttrVec outer_attrs,
+    ParseRestrictions restrictions = ParseRestrictions ());
+  std::unique_ptr<AST::ComparisonExpr> parse_binary_less_equal_expr (
+    const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+    AST::AttrVec outer_attrs,
+    ParseRestrictions restrictions = ParseRestrictions ());
+  std::unique_ptr<AST::LazyBooleanExpr>
+  parse_lazy_or_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+		      AST::AttrVec outer_attrs,
+		      ParseRestrictions restrictions = ParseRestrictions ());
+  std::unique_ptr<AST::LazyBooleanExpr>
+  parse_lazy_and_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+		       AST::AttrVec outer_attrs,
+		       ParseRestrictions restrictions = ParseRestrictions ());
+  std::unique_ptr<AST::TypeCastExpr>
+  parse_type_cast_expr (const_TokenPtr tok,
+			std::unique_ptr<AST::Expr> expr_to_cast,
+			AST::AttrVec outer_attrs,
+			ParseRestrictions restrictions = ParseRestrictions ());
+  std::unique_ptr<AST::AssignmentExpr>
+  parse_assig_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+		    AST::AttrVec outer_attrs,
+		    ParseRestrictions restrictions = ParseRestrictions ());
+  std::unique_ptr<AST::CompoundAssignmentExpr> parse_compound_assignment_expr (
+    const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+    AST::AttrVec outer_attrs, AST::CompoundAssignmentExpr::ExprType expr_type,
+    ParseRestrictions restrictions = ParseRestrictions ());
+  std::unique_ptr<AST::CompoundAssignmentExpr>
+  parse_plus_assig_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+			 AST::AttrVec outer_attrs,
+			 ParseRestrictions restrictions = ParseRestrictions ());
+  std::unique_ptr<AST::CompoundAssignmentExpr>
+  parse_minus_assig_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+			  AST::AttrVec outer_attrs,
+			  ParseRestrictions restrictions
+			  = ParseRestrictions ());
+  std::unique_ptr<AST::CompoundAssignmentExpr>
+  parse_mult_assig_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+			 AST::AttrVec outer_attrs,
+			 ParseRestrictions restrictions = ParseRestrictions ());
+  std::unique_ptr<AST::CompoundAssignmentExpr>
+  parse_div_assig_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+			AST::AttrVec outer_attrs,
+			ParseRestrictions restrictions = ParseRestrictions ());
+  std::unique_ptr<AST::CompoundAssignmentExpr>
+  parse_mod_assig_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+			AST::AttrVec outer_attrs,
+			ParseRestrictions restrictions = ParseRestrictions ());
+  std::unique_ptr<AST::CompoundAssignmentExpr>
+  parse_and_assig_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+			AST::AttrVec outer_attrs,
+			ParseRestrictions restrictions = ParseRestrictions ());
+  std::unique_ptr<AST::CompoundAssignmentExpr>
+  parse_or_assig_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+		       AST::AttrVec outer_attrs,
+		       ParseRestrictions restrictions = ParseRestrictions ());
+  std::unique_ptr<AST::CompoundAssignmentExpr>
+  parse_xor_assig_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+			AST::AttrVec outer_attrs,
+			ParseRestrictions restrictions = ParseRestrictions ());
+  std::unique_ptr<AST::CompoundAssignmentExpr> parse_left_shift_assig_expr (
+    const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+    AST::AttrVec outer_attrs,
+    ParseRestrictions restrictions = ParseRestrictions ());
+  std::unique_ptr<AST::CompoundAssignmentExpr> parse_right_shift_assig_expr (
+    const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+    AST::AttrVec outer_attrs,
+    ParseRestrictions restrictions = ParseRestrictions ());
+  std::unique_ptr<AST::AwaitExpr>
+  parse_await_expr (const_TokenPtr tok,
+		    std::unique_ptr<AST::Expr> expr_to_await,
+		    AST::AttrVec outer_attrs);
+  std::unique_ptr<AST::MethodCallExpr> parse_method_call_expr (
+    const_TokenPtr tok, std::unique_ptr<AST::Expr> receiver_expr,
+    AST::AttrVec outer_attrs,
+    ParseRestrictions restrictions = ParseRestrictions ());
+  std::unique_ptr<AST::CallExpr> parse_function_call_expr (
+    const_TokenPtr tok, std::unique_ptr<AST::Expr> function_expr,
+    AST::AttrVec outer_attrs,
+    ParseRestrictions restrictions = ParseRestrictions ());
+  std::unique_ptr<AST::RangeExpr> parse_led_range_exclusive_expr (
+    const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+    AST::AttrVec outer_attrs,
+    ParseRestrictions restrictions = ParseRestrictions ());
+  std::unique_ptr<AST::RangeExpr>
+  parse_nud_range_exclusive_expr (const_TokenPtr tok, AST::AttrVec outer_attrs);
+  std::unique_ptr<AST::RangeFromToInclExpr> parse_range_inclusive_expr (
+    const_TokenPtr tok, std::unique_ptr<AST::Expr> left,
+    AST::AttrVec outer_attrs,
+    ParseRestrictions restrictions = ParseRestrictions ());
+  std::unique_ptr<AST::RangeToInclExpr>
+  parse_range_to_inclusive_expr (const_TokenPtr tok, AST::AttrVec outer_attrs);
+  std::unique_ptr<AST::TupleIndexExpr> parse_tuple_index_expr (
+    const_TokenPtr tok, std::unique_ptr<AST::Expr> tuple_expr,
+    AST::AttrVec outer_attrs,
+    ParseRestrictions restrictions = ParseRestrictions ());
+  std::unique_ptr<AST::FieldAccessExpr> parse_field_access_expr (
+    const_TokenPtr tok, std::unique_ptr<AST::Expr> struct_expr,
+    AST::AttrVec outer_attrs,
+    ParseRestrictions restrictions = ParseRestrictions ());
+  std::unique_ptr<AST::ArrayIndexExpr>
+  parse_index_expr (const_TokenPtr tok, std::unique_ptr<AST::Expr> array_expr,
+		    AST::AttrVec outer_attrs,
+		    ParseRestrictions restrictions = ParseRestrictions ());
+  std::unique_ptr<AST::MacroInvocation> parse_macro_invocation_partial (
+    AST::PathInExpression path, AST::AttrVec outer_attrs,
+    ParseRestrictions restrictions = ParseRestrictions ());
+  std::unique_ptr<AST::StructExprStruct>
+  parse_struct_expr_struct_partial (AST::PathInExpression path,
+				    AST::AttrVec outer_attrs);
+  std::unique_ptr<AST::CallExpr>
+  parse_struct_expr_tuple_partial (AST::PathInExpression path,
+				   AST::AttrVec outer_attrs);
+  AST::PathInExpression parse_path_in_expression_pratt (const_TokenPtr tok);
+  std::unique_ptr<AST::ClosureExpr>
+  parse_closure_expr_pratt (const_TokenPtr tok,
+			    AST::AttrVec outer_attrs = AST::AttrVec ());
+  std::unique_ptr<AST::TupleIndexExpr> parse_tuple_index_expr_float (
+    const_TokenPtr tok, std::unique_ptr<AST::Expr> tuple_expr,
+    AST::AttrVec outer_attrs,
+    ParseRestrictions restrictions = ParseRestrictions ());
+
+  // Expression-related (non-Pratt parsed)
+  std::unique_ptr<AST::ExprWithBlock>
+  parse_expr_with_block (AST::AttrVec outer_attrs);
+  std::unique_ptr<AST::ExprWithoutBlock>
+  parse_expr_without_block (AST::AttrVec outer_attrs = AST::AttrVec (),
+			    ParseRestrictions restrictions
+			    = ParseRestrictions ());
+  // When given a pratt_parsed_loc, use it as the location of the
+  // first token parsed in the expression (the parsing of that first
+  // token should be skipped).
+  std::unique_ptr<AST::IfExpr>
+  parse_if_expr (AST::AttrVec outer_attrs = AST::AttrVec (),
+		 Location pratt_parsed_loc = Linemap::unknown_location ());
+  std::unique_ptr<AST::IfLetExpr>
+  parse_if_let_expr (AST::AttrVec outer_attrs = AST::AttrVec (),
+		     Location pratt_parsed_loc = Linemap::unknown_location ());
+  std::unique_ptr<AST::LoopExpr>
+  parse_loop_expr (AST::AttrVec outer_attrs = AST::AttrVec (),
+		   AST::LoopLabel label = AST::LoopLabel::error (),
+		   Location pratt_parsed_loc = Linemap::unknown_location ());
+  std::unique_ptr<AST::WhileLoopExpr>
+  parse_while_loop_expr (AST::AttrVec outer_attrs = AST::AttrVec (),
+			 AST::LoopLabel label = AST::LoopLabel::error (),
+			 Location pratt_parsed_loc
+			 = Linemap::unknown_location ());
+  std::unique_ptr<AST::WhileLetLoopExpr>
+  parse_while_let_loop_expr (AST::AttrVec outer_attrs = AST::AttrVec (),
+			     AST::LoopLabel label = AST::LoopLabel::error ());
+  std::unique_ptr<AST::ForLoopExpr>
+  parse_for_loop_expr (AST::AttrVec outer_attrs = AST::AttrVec (),
+		       AST::LoopLabel label = AST::LoopLabel::error ());
+  std::unique_ptr<AST::MatchExpr>
+  parse_match_expr (AST::AttrVec outer_attrs = AST::AttrVec (),
+		    Location pratt_parsed_loc = Linemap::unknown_location ());
+  AST::MatchArm parse_match_arm ();
+  std::vector<std::unique_ptr<AST::Pattern> >
+  parse_match_arm_patterns (TokenId end_token_id);
+  std::unique_ptr<AST::BaseLoopExpr>
+  parse_labelled_loop_expr (AST::AttrVec outer_attrs = AST::AttrVec ());
+  AST::LoopLabel parse_loop_label ();
+  std::unique_ptr<AST::AsyncBlockExpr>
+  parse_async_block_expr (AST::AttrVec outer_attrs = AST::AttrVec ());
+  std::unique_ptr<AST::GroupedExpr> parse_grouped_expr (AST::AttrVec outer_attrs
+							= AST::AttrVec ());
+  std::unique_ptr<AST::ClosureExpr> parse_closure_expr (AST::AttrVec outer_attrs
+							= AST::AttrVec ());
+  AST::ClosureParam parse_closure_param ();
+
+  // When given a pratt_parsed_loc, use it as the location of the
+  // first token parsed in the expression (the parsing of that first
+  // token should be skipped).
+  std::unique_ptr<AST::ReturnExpr>
+  parse_return_expr (AST::AttrVec outer_attrs = AST::AttrVec (),
+		     Location pratt_parsed_loc = Linemap::unknown_location ());
+  std::unique_ptr<AST::BreakExpr>
+  parse_break_expr (AST::AttrVec outer_attrs = AST::AttrVec (),
+		    Location pratt_parsed_loc = Linemap::unknown_location ());
+  std::unique_ptr<AST::ContinueExpr>
+  parse_continue_expr (AST::AttrVec outer_attrs = AST::AttrVec (),
+		       Location pratt_parsed_loc
+		       = Linemap::unknown_location ());
+  std::unique_ptr<AST::UnsafeBlockExpr>
+  parse_unsafe_block_expr (AST::AttrVec outer_attrs = AST::AttrVec (),
+			   Location pratt_parsed_loc
+			   = Linemap::unknown_location ());
+  std::unique_ptr<AST::ArrayExpr>
+  parse_array_expr (AST::AttrVec outer_attrs = AST::AttrVec (),
+		    Location pratt_parsed_loc = Linemap::unknown_location ());
+  std::unique_ptr<AST::ExprWithoutBlock>
+  parse_grouped_or_tuple_expr (AST::AttrVec outer_attrs = AST::AttrVec (),
+			       Location pratt_parsed_loc
+			       = Linemap::unknown_location ());
+  std::unique_ptr<AST::StructExprField> parse_struct_expr_field ();
+  bool will_be_expr_with_block ();
+
+  // Type-related
+  std::unique_ptr<AST::TypeNoBounds> parse_type_no_bounds ();
+  std::unique_ptr<AST::TypeNoBounds> parse_slice_or_array_type ();
+  std::unique_ptr<AST::RawPointerType> parse_raw_pointer_type ();
+  std::unique_ptr<AST::ReferenceType> parse_reference_type ();
+  std::unique_ptr<AST::BareFunctionType>
+  parse_bare_function_type (std::vector<AST::LifetimeParam> for_lifetimes);
+  std::unique_ptr<AST::Type> parse_paren_prefixed_type ();
+  std::unique_ptr<AST::TypeNoBounds> parse_paren_prefixed_type_no_bounds ();
+  std::unique_ptr<AST::Type> parse_for_prefixed_type ();
+  AST::MaybeNamedParam parse_maybe_named_param (AST::AttrVec outer_attrs);
+
+  // Statement-related
+
+  /**
+   *Parse a let-statement
+   * LetStatement :
+   * 	OuterAttribute*
+   * 		'let' PatternNoTopAlt ( ':' Type )? ('=' Expression )? ';'
+   *
+   * @param allow_no_semi Allow parsing a let-statement without expecting a
+   * 		semicolon to follow it
+   */
+  std::unique_ptr<AST::LetStmt> parse_let_stmt (AST::AttrVec outer_attrs,
+						ParseRestrictions restrictions
+						= ParseRestrictions ());
+  std::unique_ptr<AST::ExprStmt> parse_expr_stmt (AST::AttrVec outer_attrs,
+						  ParseRestrictions restrictions
+						  = ParseRestrictions ());
+  std::unique_ptr<AST::ExprStmtWithBlock>
+  parse_expr_stmt_with_block (AST::AttrVec outer_attrs);
+  std::unique_ptr<AST::ExprStmtWithoutBlock>
+  parse_expr_stmt_without_block (AST::AttrVec outer_attrs,
+				 ParseRestrictions restrictions
+				 = ParseRestrictions ());
+  ExprOrStmt parse_stmt_or_expr_without_block ();
+  ExprOrStmt parse_stmt_or_expr_with_block (AST::AttrVec outer_attrs);
+  ExprOrStmt parse_macro_invocation_maybe_semi (AST::AttrVec outer_attrs);
+  ExprOrStmt parse_path_based_stmt_or_expr (AST::AttrVec outer_attrs);
+
+  // Pattern-related
+  std::unique_ptr<AST::Pattern> parse_literal_or_range_pattern ();
+  std::unique_ptr<AST::RangePatternBound> parse_range_pattern_bound ();
+  std::unique_ptr<AST::ReferencePattern> parse_reference_pattern ();
+  std::unique_ptr<AST::Pattern> parse_grouped_or_tuple_pattern ();
+  std::unique_ptr<AST::SlicePattern> parse_slice_pattern ();
+  std::unique_ptr<AST::Pattern> parse_ident_leading_pattern ();
+  std::unique_ptr<AST::TupleStructItems> parse_tuple_struct_items ();
+  AST::StructPatternElements parse_struct_pattern_elems ();
+  std::unique_ptr<AST::StructPatternField> parse_struct_pattern_field ();
+  std::unique_ptr<AST::StructPatternField>
+  parse_struct_pattern_field_partial (AST::AttrVec outer_attrs);
+
+  int left_binding_power (const_TokenPtr token);
+
+  bool done_end ();
+  bool done_end_or_else ();
+  bool done_end_of_file ();
+
+  void add_error (Error error) { error_table.push_back (std::move (error)); }
+
+public:
+  // Construct parser with specified "managed" token source.
+  Parser (ManagedTokenSource &tokenSource) : lexer (tokenSource) {}
+
+  // Parse items without parsing an entire crate. This function is the main
+  // parsing loop of AST::Crate::parse_crate().
+  std::vector<std::unique_ptr<AST::Item> > parse_items ();
+
+  // Main entry point for parser.
+  std::unique_ptr<AST::Crate> parse_crate ();
+
+  // Dumps all lexer output.
+  void debug_dump_lex_output (std::ostream &out);
+  void debug_dump_ast_output (AST::Crate &crate, std::ostream &out);
+
+  // Returns whether any parsing errors have occurred.
+  bool has_errors () const { return !error_table.empty (); }
+  // Remove all parsing errors from the table
+  void clear_errors () { error_table.clear (); }
+
+  // Get a reference to the list of errors encountered
+  std::vector<Error> &get_errors () { return error_table; }
+
+  const ManagedTokenSource &get_token_source () const { return lexer; }
+
+  const_TokenPtr peek_current_token () { return lexer.peek_token (0); }
+
+private:
+  // The token source (usually lexer) associated with the parser.
+  ManagedTokenSource &lexer;
+  // The error list.
+  std::vector<Error> error_table;
+  // The names of inline modules while parsing.
+  std::vector<std::string> inline_module_stack;
+
+  class InlineModuleStackScope
+  {
+  private:
+    Parser &parser;
+
+  public:
+    InlineModuleStackScope (Parser &parser, std::string name) : parser (parser)
+    {
+      parser.inline_module_stack.emplace_back (std::move (name));
+    }
+    ~InlineModuleStackScope () { parser.inline_module_stack.pop_back (); }
+  };
+};
+
+std::string
+extract_module_path (const AST::AttrVec &inner_attrs,
+		     const AST::AttrVec &outer_attrs, const std::string &name);
+
+/**
+ * Check if a MacroMatch is allowed to follow the last parsed MacroMatch.
+ *
+ * @param last_match Last matcher parsed before the current match
+ * @param match Current matcher to check
+ *
+ * @return true if the follow-up is valid, false otherwise
+ */
+bool
+is_match_compatible (const AST::MacroMatch &last_match,
+		     const AST::MacroMatch &current_match);
+} // namespace Rust
+
+// as now template, include implementations of all methods
+#include "rust-parse-impl.h"
+
+#endif // RUST_PARSE_H
-- 
2.25.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [PATCH Rust front-end v2 11/37] gccrs: Add expansion pass for the Rust front-end
  2022-08-24 11:59 Rust frontend patches v2 herron.philip
                   ` (9 preceding siblings ...)
  2022-08-24 11:59 ` [PATCH Rust front-end v2 10/37] gccrs: Add Parser " herron.philip
@ 2022-08-24 11:59 ` herron.philip
  2022-08-24 11:59 ` [PATCH Rust front-end v2 12/37] gccrs: Add name resolution pass to " herron.philip
                   ` (26 subsequent siblings)
  37 siblings, 0 replies; 59+ messages in thread
From: herron.philip @ 2022-08-24 11:59 UTC (permalink / raw)
  To: gcc-patches; +Cc: gcc-rust, Arthur Cohen, Philip Herron, The Other

From: Arthur Cohen <arthur.cohen@embecosm.com>

Arthur TODO

Co-authored-by: Philip Herron <philip.herron@embecosm.com>
Co-authored-by: The Other <simplytheother@gmail.com>
---
 gcc/rust/expand/rust-attribute-visitor.cc    | 3445 ++++++++++++++++++
 gcc/rust/expand/rust-attribute-visitor.h     |  316 ++
 gcc/rust/expand/rust-macro-builtins.cc       |  484 +++
 gcc/rust/expand/rust-macro-builtins.h        |  107 +
 gcc/rust/expand/rust-macro-expand.cc         | 1012 +++++
 gcc/rust/expand/rust-macro-expand.h          |  366 ++
 gcc/rust/expand/rust-macro-invoc-lexer.cc    |   29 +
 gcc/rust/expand/rust-macro-invoc-lexer.h     |   64 +
 gcc/rust/expand/rust-macro-substitute-ctx.cc |  312 ++
 gcc/rust/expand/rust-macro-substitute-ctx.h  |   93 +
 10 files changed, 6228 insertions(+)
 create mode 100644 gcc/rust/expand/rust-attribute-visitor.cc
 create mode 100644 gcc/rust/expand/rust-attribute-visitor.h
 create mode 100644 gcc/rust/expand/rust-macro-builtins.cc
 create mode 100644 gcc/rust/expand/rust-macro-builtins.h
 create mode 100644 gcc/rust/expand/rust-macro-expand.cc
 create mode 100644 gcc/rust/expand/rust-macro-expand.h
 create mode 100644 gcc/rust/expand/rust-macro-invoc-lexer.cc
 create mode 100644 gcc/rust/expand/rust-macro-invoc-lexer.h
 create mode 100644 gcc/rust/expand/rust-macro-substitute-ctx.cc
 create mode 100644 gcc/rust/expand/rust-macro-substitute-ctx.h

diff --git a/gcc/rust/expand/rust-attribute-visitor.cc b/gcc/rust/expand/rust-attribute-visitor.cc
new file mode 100644
index 00000000000..8016f9430eb
--- /dev/null
+++ b/gcc/rust/expand/rust-attribute-visitor.cc
@@ -0,0 +1,3445 @@
+// 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/>.
+
+#include "rust-attribute-visitor.h"
+#include "rust-session-manager.h"
+
+namespace Rust {
+
+// Visitor used to expand attributes.
+void
+AttrVisitor::expand_struct_fields (std::vector<AST::StructField> &fields)
+{
+  for (auto it = fields.begin (); it != fields.end ();)
+    {
+      auto &field = *it;
+
+      auto &field_attrs = field.get_outer_attrs ();
+      expander.expand_cfg_attrs (field_attrs);
+      if (expander.fails_cfg_with_expand (field_attrs))
+	{
+	  it = fields.erase (it);
+	  continue;
+	}
+
+      expander.push_context (MacroExpander::ContextType::TYPE);
+
+      // expand sub-types of type, but can't strip type itself
+      auto &type = field.get_field_type ();
+      type->accept_vis (*this);
+
+      maybe_expand_type (type);
+
+      if (type->is_marked_for_strip ())
+	rust_error_at (type->get_locus (),
+		       "cannot strip type in this position");
+
+      expander.pop_context ();
+
+      // if nothing else happens, increment
+      ++it;
+    }
+}
+
+void
+AttrVisitor::expand_tuple_fields (std::vector<AST::TupleField> &fields)
+{
+  for (auto it = fields.begin (); it != fields.end ();)
+    {
+      auto &field = *it;
+
+      auto &field_attrs = field.get_outer_attrs ();
+      expander.expand_cfg_attrs (field_attrs);
+      if (expander.fails_cfg_with_expand (field_attrs))
+	{
+	  it = fields.erase (it);
+	  continue;
+	}
+
+      // expand sub-types of type, but can't strip type itself
+      auto &type = field.get_field_type ();
+      type->accept_vis (*this);
+      if (type->is_marked_for_strip ())
+	rust_error_at (type->get_locus (),
+		       "cannot strip type in this position");
+
+      // if nothing else happens, increment
+      ++it;
+    }
+}
+
+void
+AttrVisitor::expand_function_params (std::vector<AST::FunctionParam> &params)
+{
+  expander.push_context (MacroExpander::ContextType::TYPE);
+
+  for (auto it = params.begin (); it != params.end ();)
+    {
+      auto &param = *it;
+
+      auto &param_attrs = param.get_outer_attrs ();
+      expander.expand_cfg_attrs (param_attrs);
+      if (expander.fails_cfg_with_expand (param_attrs))
+	{
+	  it = params.erase (it);
+	  continue;
+	}
+
+      // TODO: should an unwanted strip lead to break out of loop?
+      auto &pattern = param.get_pattern ();
+      pattern->accept_vis (*this);
+      if (pattern->is_marked_for_strip ())
+	rust_error_at (pattern->get_locus (),
+		       "cannot strip pattern in this position");
+
+      auto &type = param.get_type ();
+      type->accept_vis (*this);
+
+      maybe_expand_type (type);
+
+      if (type->is_marked_for_strip ())
+	rust_error_at (type->get_locus (),
+		       "cannot strip type in this position");
+
+      // increment
+      ++it;
+    }
+
+  expander.pop_context ();
+}
+
+void
+AttrVisitor::expand_generic_args (AST::GenericArgs &args)
+{
+  // lifetime args can't be expanded
+  // FIXME: Can we have macro invocations for lifetimes?
+
+  expander.push_context (MacroExpander::ContextType::TYPE);
+
+  // expand type args - strip sub-types only
+  for (auto &arg : args.get_generic_args ())
+    {
+      switch (arg.get_kind ())
+	{
+	  case AST::GenericArg::Kind::Type: {
+	    auto &type = arg.get_type ();
+	    type->accept_vis (*this);
+	    maybe_expand_type (type);
+
+	    if (type->is_marked_for_strip ())
+	      rust_error_at (type->get_locus (),
+			     "cannot strip type in this position");
+	    break;
+	  }
+	  case AST::GenericArg::Kind::Const: {
+	    auto &expr = arg.get_expression ();
+	    expr->accept_vis (*this);
+	    maybe_expand_expr (expr);
+
+	    if (expr->is_marked_for_strip ())
+	      rust_error_at (expr->get_locus (),
+			     "cannot strip expression in this position");
+	    break;
+	  }
+	default:
+	  break;
+	  // FIXME: Figure out what to do here if there is ambiguity. Since the
+	  // resolver comes after the expansion, we need to figure out a way to
+	  // strip ambiguous values here
+	  // TODO: Arthur: Probably add a `mark_as_strip` method to `GenericArg`
+	  // or something. This would clean up this whole thing
+	}
+    }
+
+  expander.pop_context ();
+
+  // FIXME: Can we have macro invocations in generic type bindings?
+  // expand binding args - strip sub-types only
+  for (auto &binding : args.get_binding_args ())
+    {
+      auto &type = binding.get_type ();
+      type->accept_vis (*this);
+
+      if (type->is_marked_for_strip ())
+	rust_error_at (type->get_locus (),
+		       "cannot strip type in this position");
+    }
+}
+
+void
+AttrVisitor::expand_qualified_path_type (AST::QualifiedPathType &path_type)
+{
+  expander.push_context (MacroExpander::ContextType::TYPE);
+
+  auto &type = path_type.get_type ();
+  type->accept_vis (*this);
+
+  maybe_expand_type (type);
+
+  expander.pop_context ();
+
+  if (type->is_marked_for_strip ())
+    rust_error_at (type->get_locus (), "cannot strip type in this position");
+
+  if (path_type.has_as_clause ())
+    {
+      auto &type_path = path_type.get_as_type_path ();
+      visit (type_path);
+      if (type_path.is_marked_for_strip ())
+	rust_error_at (type_path.get_locus (),
+		       "cannot strip type path in this position");
+    }
+}
+
+void
+AttrVisitor::AttrVisitor::expand_closure_params (
+  std::vector<AST::ClosureParam> &params)
+{
+  for (auto it = params.begin (); it != params.end ();)
+    {
+      auto &param = *it;
+
+      auto &param_attrs = param.get_outer_attrs ();
+      expander.expand_cfg_attrs (param_attrs);
+      if (expander.fails_cfg_with_expand (param_attrs))
+	{
+	  it = params.erase (it);
+	  continue;
+	}
+
+      auto &pattern = param.get_pattern ();
+      pattern->accept_vis (*this);
+      if (pattern->is_marked_for_strip ())
+	rust_error_at (pattern->get_locus (),
+		       "cannot strip pattern in this position");
+
+      if (param.has_type_given ())
+	{
+	  expander.push_context (MacroExpander::ContextType::TYPE);
+	  auto &type = param.get_type ();
+	  type->accept_vis (*this);
+
+	  maybe_expand_type (type);
+
+	  if (type->is_marked_for_strip ())
+	    rust_error_at (type->get_locus (),
+			   "cannot strip type in this position");
+
+	  expander.pop_context ();
+	}
+
+      // increment if found nothing else so far
+      ++it;
+    }
+}
+
+void
+AttrVisitor::expand_self_param (AST::SelfParam &self_param)
+{
+  if (self_param.has_type ())
+    {
+      expander.push_context (MacroExpander::ContextType::TYPE);
+      auto &type = self_param.get_type ();
+      type->accept_vis (*this);
+
+      maybe_expand_type (type);
+
+      if (type->is_marked_for_strip ())
+	rust_error_at (type->get_locus (),
+		       "cannot strip type in this position");
+
+      expander.pop_context ();
+    }
+  /* TODO: maybe check for invariants being violated - e.g. both type and
+   * lifetime? */
+}
+
+void
+AttrVisitor::expand_where_clause (AST::WhereClause &where_clause)
+{
+  // items cannot be stripped conceptually, so just accept visitor
+  for (auto &item : where_clause.get_items ())
+    item->accept_vis (*this);
+}
+
+void
+AttrVisitor::expand_trait_function_decl (AST::TraitFunctionDecl &decl)
+{
+  // just expand sub-stuff - can't actually strip generic params themselves
+  for (auto &param : decl.get_generic_params ())
+    param->accept_vis (*this);
+
+  /* strip function parameters if required - this is specifically
+   * allowed by spec */
+  expand_function_params (decl.get_function_params ());
+
+  if (decl.has_return_type ())
+    {
+      expander.push_context (MacroExpander::ContextType::TYPE);
+
+      auto &return_type = decl.get_return_type ();
+      return_type->accept_vis (*this);
+
+      maybe_expand_type (return_type);
+
+      if (return_type->is_marked_for_strip ())
+	rust_error_at (return_type->get_locus (),
+		       "cannot strip type in this position");
+
+      expander.pop_context ();
+    }
+
+  if (decl.has_where_clause ())
+    expand_where_clause (decl.get_where_clause ());
+}
+
+void
+AttrVisitor::expand_trait_method_decl (AST::TraitMethodDecl &decl)
+{
+  // just expand sub-stuff - can't actually strip generic params themselves
+  for (auto &param : decl.get_generic_params ())
+    param->accept_vis (*this);
+
+  /* assuming you can't strip self param - wouldn't be a method
+   * anymore. spec allows outer attrs on self param, but doesn't
+   * specify whether cfg is used. */
+  expand_self_param (decl.get_self_param ());
+
+  /* strip function parameters if required - this is specifically
+   * allowed by spec */
+  expand_function_params (decl.get_function_params ());
+
+  if (decl.has_return_type ())
+    {
+      expander.push_context (MacroExpander::ContextType::TYPE);
+
+      auto &return_type = decl.get_return_type ();
+      return_type->accept_vis (*this);
+
+      maybe_expand_type (return_type);
+
+      if (return_type->is_marked_for_strip ())
+	rust_error_at (return_type->get_locus (),
+		       "cannot strip type in this position");
+
+      expander.pop_context ();
+    }
+
+  if (decl.has_where_clause ())
+    expand_where_clause (decl.get_where_clause ());
+}
+
+void
+AttrVisitor::visit (AST::Token &)
+{
+  // shouldn't require?
+}
+void
+AttrVisitor::visit (AST::DelimTokenTree &)
+{
+  // shouldn't require?
+}
+void
+AttrVisitor::visit (AST::AttrInputMetaItemContainer &)
+{
+  // shouldn't require?
+}
+void
+AttrVisitor::visit (AST::IdentifierExpr &ident_expr)
+{
+  // strip test based on outer attrs
+  expander.expand_cfg_attrs (ident_expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (ident_expr.get_outer_attrs ()))
+    {
+      ident_expr.mark_for_strip ();
+      return;
+    }
+}
+void
+AttrVisitor::visit (AST::Lifetime &)
+{
+  // shouldn't require?
+}
+void
+AttrVisitor::visit (AST::LifetimeParam &)
+{
+  // supposedly does not require - cfg does nothing
+}
+void
+AttrVisitor::visit (AST::ConstGenericParam &)
+{
+  // likewise
+}
+
+void
+AttrVisitor::visit (AST::MacroInvocation &macro_invoc)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (macro_invoc.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (macro_invoc.get_outer_attrs ()))
+    {
+      macro_invoc.mark_for_strip ();
+      return;
+    }
+
+  // can't strip simple path
+
+  // I don't think any macro token trees can be stripped in any way
+
+  // TODO: maybe have cfg! macro stripping behaviour here?
+  expander.expand_invoc (macro_invoc, macro_invoc.has_semicolon ());
+}
+
+void
+AttrVisitor::visit (AST::PathInExpression &path)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (path.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (path.get_outer_attrs ()))
+    {
+      path.mark_for_strip ();
+      return;
+    }
+
+  for (auto &segment : path.get_segments ())
+    {
+      if (segment.has_generic_args ())
+	expand_generic_args (segment.get_generic_args ());
+    }
+}
+void
+AttrVisitor::visit (AST::TypePathSegment &)
+{
+  // shouldn't require
+}
+void
+AttrVisitor::visit (AST::TypePathSegmentGeneric &segment)
+{
+  // TODO: strip inside generic args
+
+  if (!segment.has_generic_args ())
+    return;
+
+  expand_generic_args (segment.get_generic_args ());
+}
+void
+AttrVisitor::visit (AST::TypePathSegmentFunction &segment)
+{
+  auto &type_path_function = segment.get_type_path_function ();
+
+  for (auto &type : type_path_function.get_params ())
+    {
+      type->accept_vis (*this);
+      if (type->is_marked_for_strip ())
+	rust_error_at (type->get_locus (),
+		       "cannot strip type in this position");
+    }
+
+  if (type_path_function.has_return_type ())
+    {
+      expander.push_context (MacroExpander::ContextType::TYPE);
+
+      auto &return_type = type_path_function.get_return_type ();
+      return_type->accept_vis (*this);
+
+      maybe_expand_type (return_type);
+
+      if (return_type->is_marked_for_strip ())
+	rust_error_at (return_type->get_locus (),
+		       "cannot strip type in this position");
+
+      expander.pop_context ();
+    }
+}
+void
+AttrVisitor::visit (AST::TypePath &path)
+{
+  // this shouldn't strip any segments, but can strip inside them
+  for (auto &segment : path.get_segments ())
+    segment->accept_vis (*this);
+}
+void
+AttrVisitor::visit (AST::QualifiedPathInExpression &path)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (path.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (path.get_outer_attrs ()))
+    {
+      path.mark_for_strip ();
+      return;
+    }
+
+  expand_qualified_path_type (path.get_qualified_path_type ());
+
+  for (auto &segment : path.get_segments ())
+    {
+      if (segment.has_generic_args ())
+	expand_generic_args (segment.get_generic_args ());
+    }
+}
+void
+AttrVisitor::visit (AST::QualifiedPathInType &path)
+{
+  expand_qualified_path_type (path.get_qualified_path_type ());
+
+  // this shouldn't strip any segments, but can strip inside them
+  for (auto &segment : path.get_segments ())
+    segment->accept_vis (*this);
+}
+
+void
+AttrVisitor::visit (AST::LiteralExpr &expr)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+}
+void
+AttrVisitor::visit (AST::AttrInputLiteral &)
+{
+  // shouldn't require?
+}
+void
+AttrVisitor::visit (AST::MetaItemLitExpr &)
+{
+  // shouldn't require?
+}
+void
+AttrVisitor::visit (AST::MetaItemPathLit &)
+{
+  // shouldn't require?
+}
+void
+AttrVisitor::visit (AST::BorrowExpr &expr)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  /* strip any internal sub-expressions - expression itself isn't
+   * allowed to have external attributes in this position so can't be
+   * stripped. */
+  auto &borrowed_expr = expr.get_borrowed_expr ();
+  borrowed_expr->accept_vis (*this);
+  if (borrowed_expr->is_marked_for_strip ())
+    rust_error_at (borrowed_expr->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::DereferenceExpr &expr)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  /* strip any internal sub-expressions - expression itself isn't
+   * allowed to have external attributes in this position so can't be
+   * stripped. */
+  auto &dereferenced_expr = expr.get_dereferenced_expr ();
+  dereferenced_expr->accept_vis (*this);
+  if (dereferenced_expr->is_marked_for_strip ())
+    rust_error_at (dereferenced_expr->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::ErrorPropagationExpr &expr)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  /* strip any internal sub-expressions - expression itself isn't
+   * allowed to have external attributes in this position so can't be
+   * stripped. */
+  auto &propagating_expr = expr.get_propagating_expr ();
+  propagating_expr->accept_vis (*this);
+  if (propagating_expr->is_marked_for_strip ())
+    rust_error_at (propagating_expr->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::NegationExpr &expr)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  /* strip any internal sub-expressions - expression itself isn't
+   * allowed to have external attributes in this position so can't be
+   * stripped. */
+  auto &negated_expr = expr.get_negated_expr ();
+  negated_expr->accept_vis (*this);
+  if (negated_expr->is_marked_for_strip ())
+    rust_error_at (negated_expr->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::ArithmeticOrLogicalExpr &expr)
+{
+  /* outer attributes never allowed before these. while cannot strip
+   * two direct descendant expressions, can strip ones below that */
+
+  /* should have no possibility for outer attrs as would be parsed
+   * with outer expr */
+  auto &l_expr = expr.get_left_expr ();
+  l_expr->accept_vis (*this);
+  maybe_expand_expr (l_expr);
+
+  /* should syntactically not have outer attributes, though this may
+   * not have worked in practice */
+  auto &r_expr = expr.get_right_expr ();
+  r_expr->accept_vis (*this);
+  maybe_expand_expr (r_expr);
+
+  // ensure that they are not marked for strip
+  if (expr.get_left_expr ()->is_marked_for_strip ())
+    rust_error_at (expr.get_left_expr ()->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes are never allowed "
+		   "before binary op exprs");
+  if (expr.get_right_expr ()->is_marked_for_strip ())
+    rust_error_at (expr.get_right_expr ()->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::ComparisonExpr &expr)
+{
+  /* outer attributes never allowed before these. while cannot strip
+   * two direct descendant expressions, can strip ones below that */
+
+  /* should have no possibility for outer attrs as would be parsed
+   * with outer expr */
+  auto &l_expr = expr.get_left_expr ();
+  l_expr->accept_vis (*this);
+  maybe_expand_expr (l_expr);
+
+  /* should syntactically not have outer attributes, though this may
+   * not have worked in practice */
+  auto &r_expr = expr.get_right_expr ();
+  r_expr->accept_vis (*this);
+  maybe_expand_expr (r_expr);
+
+  // ensure that they are not marked for strip
+  if (expr.get_left_expr ()->is_marked_for_strip ())
+    rust_error_at (expr.get_left_expr ()->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes are never allowed "
+		   "before binary op exprs");
+  if (expr.get_right_expr ()->is_marked_for_strip ())
+    rust_error_at (expr.get_right_expr ()->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::LazyBooleanExpr &expr)
+{
+  /* outer attributes never allowed before these. while cannot strip
+   * two direct descendant expressions, can strip ones below that */
+
+  /* should have no possibility for outer attrs as would be parsed
+   * with outer expr */
+  auto &l_expr = expr.get_left_expr ();
+  l_expr->accept_vis (*this);
+  maybe_expand_expr (l_expr);
+
+  /* should syntactically not have outer attributes, though this may
+   * not have worked in practice */
+  auto &r_expr = expr.get_right_expr ();
+  r_expr->accept_vis (*this);
+  maybe_expand_expr (r_expr);
+
+  // ensure that they are not marked for strip
+  if (expr.get_left_expr ()->is_marked_for_strip ())
+    rust_error_at (expr.get_left_expr ()->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes are never allowed "
+		   "before binary op exprs");
+  if (expr.get_right_expr ()->is_marked_for_strip ())
+    rust_error_at (expr.get_right_expr ()->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::TypeCastExpr &expr)
+{
+  /* outer attributes never allowed before these. while cannot strip
+   * direct descendant expression, can strip ones below that */
+
+  auto &casted_expr = expr.get_casted_expr ();
+  /* should have no possibility for outer attrs as would be parsed
+   * with outer expr */
+  casted_expr->accept_vis (*this);
+
+  // ensure that they are not marked for strip
+  if (casted_expr->is_marked_for_strip ())
+    rust_error_at (casted_expr->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes are never allowed before cast exprs");
+
+  // TODO: strip sub-types of type
+  auto &type = expr.get_type_to_cast_to ();
+  type->accept_vis (*this);
+  if (type->is_marked_for_strip ())
+    rust_error_at (type->get_locus (), "cannot strip type in this position");
+}
+void
+AttrVisitor::visit (AST::AssignmentExpr &expr)
+{
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  /* should have no possibility for outer attrs as would be parsed
+   * with outer expr */
+  auto &l_expr = expr.get_left_expr ();
+  l_expr->accept_vis (*this);
+  maybe_expand_expr (l_expr);
+
+  /* should syntactically not have outer attributes, though this may
+   * not have worked in practice */
+  auto &r_expr = expr.get_right_expr ();
+  r_expr->accept_vis (*this);
+  maybe_expand_expr (r_expr);
+
+  // ensure that they are not marked for strip
+  if (expr.get_left_expr ()->is_marked_for_strip ())
+    rust_error_at (expr.get_left_expr ()->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes are never allowed "
+		   "before binary op exprs");
+  if (expr.get_right_expr ()->is_marked_for_strip ())
+    rust_error_at (expr.get_right_expr ()->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::CompoundAssignmentExpr &expr)
+{
+  /* outer attributes never allowed before these. while cannot strip
+   * two direct descendant expressions, can strip ones below that */
+
+  /* should have no possibility for outer attrs as would be parsed
+   * with outer expr */
+  auto &l_expr = expr.get_left_expr ();
+  l_expr->accept_vis (*this);
+  maybe_expand_expr (l_expr);
+
+  /* should syntactically not have outer attributes, though this may
+   * not have worked in practice */
+  auto &r_expr = expr.get_right_expr ();
+  r_expr->accept_vis (*this);
+  maybe_expand_expr (r_expr);
+
+  // ensure that they are not marked for strip
+  if (expr.get_left_expr ()->is_marked_for_strip ())
+    rust_error_at (expr.get_left_expr ()->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes are never allowed "
+		   "before binary op exprs");
+  if (expr.get_right_expr ()->is_marked_for_strip ())
+    rust_error_at (expr.get_right_expr ()->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::GroupedExpr &expr)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  /* strip test based on inner attrs - spec says these are inner
+   * attributes, not outer attributes of inner expr */
+  expander.expand_cfg_attrs (expr.get_inner_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_inner_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  /* strip any internal sub-expressions - expression itself isn't
+   * allowed to have external attributes in this position so can't be
+   * stripped. */
+  auto &inner_expr = expr.get_expr_in_parens ();
+  inner_expr->accept_vis (*this);
+  if (inner_expr->is_marked_for_strip ())
+    rust_error_at (inner_expr->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::ArrayElemsValues &elems)
+{
+  /* apparently outer attributes are allowed in "elements of array
+   * expressions" according to spec */
+  expand_pointer_allow_strip (elems.get_values ());
+}
+void
+AttrVisitor::visit (AST::ArrayElemsCopied &elems)
+{
+  /* apparently outer attributes are allowed in "elements of array
+   * expressions" according to spec. on the other hand, it would not
+   * make conceptual sense to be able to remove either expression. As
+   * such, not implementing. TODO clear up the ambiguity here */
+
+  // only intend stripping for internal sub-expressions
+  auto &copied_expr = elems.get_elem_to_copy ();
+  copied_expr->accept_vis (*this);
+  if (copied_expr->is_marked_for_strip ())
+    rust_error_at (copied_expr->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+
+  auto &copy_count = elems.get_num_copies ();
+  copy_count->accept_vis (*this);
+  if (copy_count->is_marked_for_strip ())
+    rust_error_at (copy_count->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::ArrayExpr &expr)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  /* strip test based on inner attrs - spec says there are separate
+   * inner attributes, not just outer attributes of inner exprs */
+  expander.expand_cfg_attrs (expr.get_inner_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_inner_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  /* assuming you can't strip away the ArrayElems type, but can strip
+   * internal expressions and whatever */
+  expr.get_array_elems ()->accept_vis (*this);
+}
+void
+AttrVisitor::visit (AST::ArrayIndexExpr &expr)
+{
+  /* it is unclear whether outer attributes are supposed to be
+   * allowed, but conceptually it wouldn't make much sense, but
+   * having expansion code anyway. TODO */
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  /* strip any internal sub-expressions - expression itself isn't
+   * allowed to have external attributes in this position so can't be
+   * stripped. */
+  auto &array_expr = expr.get_array_expr ();
+  array_expr->accept_vis (*this);
+  if (array_expr->is_marked_for_strip ())
+    rust_error_at (array_expr->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+
+  auto &index_expr = expr.get_index_expr ();
+  index_expr->accept_vis (*this);
+  if (index_expr->is_marked_for_strip ())
+    rust_error_at (index_expr->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::TupleExpr &expr)
+{
+  /* according to spec, outer attributes are allowed on "elements of
+   * tuple expressions" */
+
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  /* strip test based on inner attrs - spec says these are inner
+   * attributes, not outer attributes of inner expr */
+  expander.expand_cfg_attrs (expr.get_inner_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_inner_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  /* apparently outer attributes are allowed in "elements of tuple
+   * expressions" according to spec */
+  expand_pointer_allow_strip (expr.get_tuple_elems ());
+}
+void
+AttrVisitor::visit (AST::TupleIndexExpr &expr)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  /* wouldn't strip this directly (as outer attrs should be
+   * associated with this level), but any sub-expressions would be
+   * stripped. Thus, no need to erase when strip check called. */
+  auto &tuple_expr = expr.get_tuple_expr ();
+  tuple_expr->accept_vis (*this);
+  if (tuple_expr->is_marked_for_strip ())
+    rust_error_at (tuple_expr->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::StructExprStruct &expr)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  /* strip test based on inner attrs - spec says these are inner
+   * attributes, not outer attributes of inner expr */
+  expander.expand_cfg_attrs (expr.get_inner_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_inner_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  // strip sub-exprs of path
+  auto &struct_name = expr.get_struct_name ();
+  visit (struct_name);
+  if (struct_name.is_marked_for_strip ())
+    rust_error_at (struct_name.get_locus (),
+		   "cannot strip path in this position");
+}
+void
+AttrVisitor::visit (AST::StructExprFieldIdentifier &)
+{
+  // as no attrs (at moment, at least), no stripping possible
+}
+void
+AttrVisitor::visit (AST::StructExprFieldIdentifierValue &field)
+{
+  /* as no attrs possible (at moment, at least), only sub-expression
+   * stripping is possible */
+  auto &value = field.get_value ();
+  value->accept_vis (*this);
+  if (value->is_marked_for_strip ())
+    rust_error_at (value->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::StructExprFieldIndexValue &field)
+{
+  /* as no attrs possible (at moment, at least), only sub-expression
+   * stripping is possible */
+  auto &value = field.get_value ();
+  value->accept_vis (*this);
+  if (value->is_marked_for_strip ())
+    rust_error_at (value->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::StructExprStructFields &expr)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  /* strip test based on inner attrs - spec says these are inner
+   * attributes, not outer attributes of inner expr */
+  expander.expand_cfg_attrs (expr.get_inner_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_inner_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  // strip sub-exprs of path
+  auto &struct_name = expr.get_struct_name ();
+  visit (struct_name);
+  if (struct_name.is_marked_for_strip ())
+    rust_error_at (struct_name.get_locus (),
+		   "cannot strip path in this position");
+
+  /* spec does not specify whether expressions are allowed to be
+   * stripped at top level of struct fields, but I wouldn't think
+   * that they would be, so operating under the assumption that only
+   * sub-expressions can be stripped. */
+  for (auto &field : expr.get_fields ())
+    {
+      field->accept_vis (*this);
+      // shouldn't strip in this
+    }
+
+  /* struct base presumably can't be stripped, as the '..' is before
+   * the expression. as such, can only strip sub-expressions. */
+  if (expr.has_struct_base ())
+    {
+      auto &base_struct_expr = expr.get_struct_base ().get_base_struct ();
+      base_struct_expr->accept_vis (*this);
+      if (base_struct_expr->is_marked_for_strip ())
+	rust_error_at (base_struct_expr->get_locus (),
+		       "cannot strip expression in this position - outer "
+		       "attributes not allowed");
+    }
+}
+void
+AttrVisitor::visit (AST::StructExprStructBase &expr)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  /* strip test based on inner attrs - spec says these are inner
+   * attributes, not outer attributes of inner expr */
+  expander.expand_cfg_attrs (expr.get_inner_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_inner_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  // strip sub-exprs of path
+  auto &struct_name = expr.get_struct_name ();
+  visit (struct_name);
+  if (struct_name.is_marked_for_strip ())
+    rust_error_at (struct_name.get_locus (),
+		   "cannot strip path in this position");
+
+  /* struct base presumably can't be stripped, as the '..' is before
+   * the expression. as such, can only strip sub-expressions. */
+  rust_assert (!expr.get_struct_base ().is_invalid ());
+  auto &base_struct_expr = expr.get_struct_base ().get_base_struct ();
+  base_struct_expr->accept_vis (*this);
+  if (base_struct_expr->is_marked_for_strip ())
+    rust_error_at (base_struct_expr->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::CallExpr &expr)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  /* should not be outer attrs on "function" expression - outer attrs
+   * should be associated with call expr as a whole. only sub-expr
+   * expansion is possible. */
+  auto &function = expr.get_function_expr ();
+  function->accept_vis (*this);
+  if (function->is_marked_for_strip ())
+    rust_error_at (function->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+
+  /* spec says outer attributes are specifically allowed for elements
+   * of call expressions, so full stripping possible */
+  // FIXME: Arthur: Figure out how to refactor this - This is similar to
+  // expanding items in the crate or stmts in blocks
+  expand_pointer_allow_strip (expr.get_params ());
+  auto &params = expr.get_params ();
+  for (auto it = params.begin (); it != params.end ();)
+    {
+      auto &stmt = *it;
+
+      stmt->accept_vis (*this);
+
+      auto final_fragment = expand_macro_fragment_recursive ();
+      if (final_fragment.should_expand ())
+	{
+	  // Remove the current expanded invocation
+	  it = params.erase (it);
+	  for (auto &node : final_fragment.get_nodes ())
+	    {
+	      it = params.insert (it, node.take_expr ());
+	      it++;
+	    }
+	}
+      else if (stmt->is_marked_for_strip ())
+	it = params.erase (it);
+      else
+	it++;
+    }
+}
+void
+AttrVisitor::visit (AST::MethodCallExpr &expr)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  /* should not be outer attrs on "receiver" expression - outer attrs
+   * should be associated with call expr as a whole. only sub-expr
+   * expansion is possible. */
+  auto &receiver = expr.get_receiver_expr ();
+  receiver->accept_vis (*this);
+  if (receiver->is_marked_for_strip ())
+    rust_error_at (receiver->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+
+  auto &method_name = expr.get_method_name ();
+  if (method_name.has_generic_args ())
+    expand_generic_args (method_name.get_generic_args ());
+
+  /* spec says outer attributes are specifically allowed for elements
+   * of method call expressions, so full stripping possible */
+  expand_pointer_allow_strip (expr.get_params ());
+}
+void
+AttrVisitor::visit (AST::FieldAccessExpr &expr)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  /* should not be outer attrs on "receiver" expression - outer attrs
+   * should be associated with field expr as a whole. only sub-expr
+   * expansion is possible. */
+  auto &receiver = expr.get_receiver_expr ();
+  receiver->accept_vis (*this);
+  if (receiver->is_marked_for_strip ())
+    rust_error_at (receiver->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::ClosureExprInner &expr)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  /* strip closure parameters if required - this is specifically
+   * allowed by spec */
+  expand_closure_params (expr.get_params ());
+
+  // can't strip expression itself, but can strip sub-expressions
+  auto &definition_expr = expr.get_definition_expr ();
+  definition_expr->accept_vis (*this);
+  if (definition_expr->is_marked_for_strip ())
+    rust_error_at (definition_expr->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+}
+
+void
+AttrVisitor::visit (AST::BlockExpr &expr)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  /* strip test based on inner attrs - spec says there are inner
+   * attributes, not just outer attributes of inner stmts */
+  expander.expand_cfg_attrs (expr.get_inner_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_inner_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  std::function<std::unique_ptr<AST::Stmt> (AST::SingleASTNode)> extractor
+    = [] (AST::SingleASTNode node) { return node.take_stmt (); };
+
+  expand_macro_children (MacroExpander::BLOCK, expr.get_statements (),
+			 extractor);
+
+  expander.push_context (MacroExpander::BLOCK);
+
+  // strip tail expression if exists - can actually fully remove it
+  if (expr.has_tail_expr ())
+    {
+      auto &tail_expr = expr.get_tail_expr ();
+
+      tail_expr->accept_vis (*this);
+      maybe_expand_expr (tail_expr);
+
+      if (tail_expr->is_marked_for_strip ())
+	expr.strip_tail_expr ();
+    }
+  expander.pop_context ();
+}
+
+void
+AttrVisitor::visit (AST::ClosureExprInnerTyped &expr)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  /* strip closure parameters if required - this is specifically
+   * allowed by spec */
+  expand_closure_params (expr.get_params ());
+
+  expander.push_context (MacroExpander::ContextType::TYPE);
+
+  // can't strip return type, but can strip sub-types
+  auto &type = expr.get_return_type ();
+  type->accept_vis (*this);
+
+  maybe_expand_type (type);
+
+  if (type->is_marked_for_strip ())
+    rust_error_at (type->get_locus (), "cannot strip type in this position");
+
+  expander.pop_context ();
+
+  // can't strip expression itself, but can strip sub-expressions
+  auto &definition_block = expr.get_definition_block ();
+  definition_block->accept_vis (*this);
+  if (definition_block->is_marked_for_strip ())
+    rust_error_at (definition_block->get_locus (),
+		   "cannot strip block expression in this position - outer "
+		   "attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::ContinueExpr &expr)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+}
+void
+AttrVisitor::visit (AST::BreakExpr &expr)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  /* spec does not say that you can have outer attributes on
+   * expression, so assuming you can't. stripping for sub-expressions
+   * is the only thing that can be done */
+  if (expr.has_break_expr ())
+    {
+      auto &break_expr = expr.get_break_expr ();
+
+      break_expr->accept_vis (*this);
+
+      if (break_expr->is_marked_for_strip ())
+	rust_error_at (break_expr->get_locus (),
+		       "cannot strip expression in this position - outer "
+		       "attributes not allowed");
+    }
+}
+void
+AttrVisitor::visit (AST::RangeFromToExpr &expr)
+{
+  /* outer attributes never allowed before these. while cannot strip
+   * two direct descendant expressions, can strip ones below that */
+
+  /* should have no possibility for outer attrs as would be parsed
+   * with outer expr */
+  expr.get_from_expr ()->accept_vis (*this);
+  /* should syntactically not have outer attributes, though this may
+   * not have worked in practice */
+  expr.get_to_expr ()->accept_vis (*this);
+
+  // ensure that they are not marked for strip
+  if (expr.get_from_expr ()->is_marked_for_strip ())
+    rust_error_at (expr.get_from_expr ()->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes are never allowed "
+		   "before range exprs");
+  if (expr.get_to_expr ()->is_marked_for_strip ())
+    rust_error_at (expr.get_to_expr ()->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::RangeFromExpr &expr)
+{
+  /* outer attributes never allowed before these. while cannot strip
+   * direct descendant expression, can strip ones below that */
+
+  /* should have no possibility for outer attrs as would be parsed
+   * with outer expr */
+  auto &from_expr = expr.get_from_expr ();
+
+  from_expr->accept_vis (*this);
+
+  if (from_expr->is_marked_for_strip ())
+    rust_error_at (from_expr->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes are never allowed before range exprs");
+}
+void
+AttrVisitor::visit (AST::RangeToExpr &expr)
+{
+  /* outer attributes never allowed before these. while cannot strip
+   * direct descendant expression, can strip ones below that */
+
+  /* should syntactically not have outer attributes, though this may
+   * not have worked in practice */
+  auto &to_expr = expr.get_to_expr ();
+
+  to_expr->accept_vis (*this);
+
+  if (to_expr->is_marked_for_strip ())
+    rust_error_at (to_expr->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::RangeFullExpr &)
+{
+  // outer attributes never allowed before these, so no stripping
+}
+void
+AttrVisitor::visit (AST::RangeFromToInclExpr &expr)
+{
+  /* outer attributes never allowed before these. while cannot strip
+   * two direct descendant expressions, can strip ones below that */
+
+  /* should have no possibility for outer attrs as would be parsed
+   * with outer expr */
+  expr.get_from_expr ()->accept_vis (*this);
+  /* should syntactically not have outer attributes, though this may
+   * not have worked in practice */
+  expr.get_to_expr ()->accept_vis (*this);
+
+  // ensure that they are not marked for strip
+  if (expr.get_from_expr ()->is_marked_for_strip ())
+    rust_error_at (expr.get_from_expr ()->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes are never allowed "
+		   "before range exprs");
+  if (expr.get_to_expr ()->is_marked_for_strip ())
+    rust_error_at (expr.get_to_expr ()->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::RangeToInclExpr &expr)
+{
+  /* outer attributes never allowed before these. while cannot strip
+   * direct descendant expression, can strip ones below that */
+
+  /* should syntactically not have outer attributes, though this may
+   * not have worked in practice */
+  auto &to_expr = expr.get_to_expr ();
+
+  to_expr->accept_vis (*this);
+
+  if (to_expr->is_marked_for_strip ())
+    rust_error_at (to_expr->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::ReturnExpr &expr)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  /* spec does not say that you can have outer attributes on
+   * expression, so assuming you can't. stripping for sub-expressions
+   * is the only thing that can be done */
+  if (expr.has_returned_expr ())
+    {
+      auto &returned_expr = expr.get_returned_expr ();
+
+      returned_expr->accept_vis (*this);
+
+      if (returned_expr->is_marked_for_strip ())
+	rust_error_at (returned_expr->get_locus (),
+		       "cannot strip expression in this position - outer "
+		       "attributes not allowed");
+    }
+  /* TODO: conceptually, you would maybe be able to remove a returned
+   * expr - e.g. if you had conditional compilation returning void or
+   * returning a type. On the other hand, I think that function
+   * return type cannot be conditionally compiled, so I assumed you
+   * can't do this either. */
+}
+void
+AttrVisitor::visit (AST::UnsafeBlockExpr &expr)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  // can't strip block itself, but can strip sub-expressions
+  auto &block_expr = expr.get_block_expr ();
+  block_expr->accept_vis (*this);
+  if (block_expr->is_marked_for_strip ())
+    rust_error_at (block_expr->get_locus (),
+		   "cannot strip block expression in this position - outer "
+		   "attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::LoopExpr &expr)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  // can't strip block itself, but can strip sub-expressions
+  auto &loop_block = expr.get_loop_block ();
+  loop_block->accept_vis (*this);
+  if (loop_block->is_marked_for_strip ())
+    rust_error_at (loop_block->get_locus (),
+		   "cannot strip block expression in this position - outer "
+		   "attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::WhileLoopExpr &expr)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  // can't strip predicate expr itself, but can strip sub-expressions
+  auto &predicate_expr = expr.get_predicate_expr ();
+  predicate_expr->accept_vis (*this);
+  if (predicate_expr->is_marked_for_strip ())
+    rust_error_at (predicate_expr->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+
+  // can't strip block itself, but can strip sub-expressions
+  auto &loop_block = expr.get_loop_block ();
+  loop_block->accept_vis (*this);
+  if (loop_block->is_marked_for_strip ())
+    rust_error_at (loop_block->get_locus (),
+		   "cannot strip block expression in this position - outer "
+		   "attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::WhileLetLoopExpr &expr)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  for (auto &pattern : expr.get_patterns ())
+    {
+      pattern->accept_vis (*this);
+      if (pattern->is_marked_for_strip ())
+	rust_error_at (pattern->get_locus (),
+		       "cannot strip pattern in this position");
+    }
+
+  // can't strip scrutinee expr itself, but can strip sub-expressions
+  auto &scrutinee_expr = expr.get_scrutinee_expr ();
+  scrutinee_expr->accept_vis (*this);
+  if (scrutinee_expr->is_marked_for_strip ())
+    rust_error_at (scrutinee_expr->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+
+  // can't strip block itself, but can strip sub-expressions
+  auto &loop_block = expr.get_loop_block ();
+  loop_block->accept_vis (*this);
+  if (loop_block->is_marked_for_strip ())
+    rust_error_at (loop_block->get_locus (),
+		   "cannot strip block expression in this position - outer "
+		   "attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::ForLoopExpr &expr)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  // strip sub-patterns of pattern
+  auto &pattern = expr.get_pattern ();
+  pattern->accept_vis (*this);
+  if (pattern->is_marked_for_strip ())
+    rust_error_at (pattern->get_locus (),
+		   "cannot strip pattern in this position");
+
+  // can't strip scrutinee expr itself, but can strip sub-expressions
+  auto &iterator_expr = expr.get_iterator_expr ();
+  iterator_expr->accept_vis (*this);
+  if (iterator_expr->is_marked_for_strip ())
+    rust_error_at (iterator_expr->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+
+  // can't strip block itself, but can strip sub-expressions
+  auto &loop_block = expr.get_loop_block ();
+  loop_block->accept_vis (*this);
+  if (loop_block->is_marked_for_strip ())
+    rust_error_at (loop_block->get_locus (),
+		   "cannot strip block expression in this position - outer "
+		   "attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::IfExpr &expr)
+{
+  // rust playground test shows that IfExpr does support outer attrs, at least
+  // when used as statement
+
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  // can't strip condition expr itself, but can strip sub-expressions
+  auto &condition_expr = expr.get_condition_expr ();
+  condition_expr->accept_vis (*this);
+  maybe_expand_expr (condition_expr);
+  if (condition_expr->is_marked_for_strip ())
+    rust_error_at (condition_expr->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+
+  // can't strip if block itself, but can strip sub-expressions
+  auto &if_block = expr.get_if_block ();
+  if_block->accept_vis (*this);
+  if (if_block->is_marked_for_strip ())
+    rust_error_at (if_block->get_locus (),
+		   "cannot strip block expression in this position - outer "
+		   "attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::IfExprConseqElse &expr)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  // can't strip condition expr itself, but can strip sub-expressions
+  auto &condition_expr = expr.get_condition_expr ();
+  condition_expr->accept_vis (*this);
+  maybe_expand_expr (condition_expr);
+  if (condition_expr->is_marked_for_strip ())
+    rust_error_at (condition_expr->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+
+  // can't strip if block itself, but can strip sub-expressions
+  auto &if_block = expr.get_if_block ();
+  if_block->accept_vis (*this);
+  if (if_block->is_marked_for_strip ())
+    rust_error_at (if_block->get_locus (),
+		   "cannot strip block expression in this position - outer "
+		   "attributes not allowed");
+
+  // can't strip else block itself, but can strip sub-expressions
+  auto &else_block = expr.get_else_block ();
+  else_block->accept_vis (*this);
+  if (else_block->is_marked_for_strip ())
+    rust_error_at (else_block->get_locus (),
+		   "cannot strip block expression in this position - outer "
+		   "attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::IfExprConseqIf &expr)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  // can't strip condition expr itself, but can strip sub-expressions
+  auto &condition_expr = expr.get_condition_expr ();
+  condition_expr->accept_vis (*this);
+  maybe_expand_expr (condition_expr);
+  if (condition_expr->is_marked_for_strip ())
+    rust_error_at (condition_expr->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+
+  // can't strip if block itself, but can strip sub-expressions
+  auto &if_block = expr.get_if_block ();
+  if_block->accept_vis (*this);
+  if (if_block->is_marked_for_strip ())
+    rust_error_at (if_block->get_locus (),
+		   "cannot strip block expression in this position - outer "
+		   "attributes not allowed");
+
+  // can't strip if expr itself, but can strip sub-expressions
+  auto &conseq_if_expr = expr.get_conseq_if_expr ();
+  conseq_if_expr->accept_vis (*this);
+  if (conseq_if_expr->is_marked_for_strip ())
+    rust_error_at (conseq_if_expr->get_locus (),
+		   "cannot strip consequent if expression in this "
+		   "position - outer attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::IfExprConseqIfLet &expr)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  // can't strip condition expr itself, but can strip sub-expressions
+  auto &condition_expr = expr.get_condition_expr ();
+  condition_expr->accept_vis (*this);
+  maybe_expand_expr (condition_expr);
+  if (condition_expr->is_marked_for_strip ())
+    rust_error_at (condition_expr->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+
+  // can't strip if block itself, but can strip sub-expressions
+  auto &if_block = expr.get_if_block ();
+  if_block->accept_vis (*this);
+  if (if_block->is_marked_for_strip ())
+    rust_error_at (if_block->get_locus (),
+		   "cannot strip block expression in this position - outer "
+		   "attributes not allowed");
+
+  // can't strip if let expr itself, but can strip sub-expressions
+  auto &conseq_if_let_expr = expr.get_conseq_if_let_expr ();
+  conseq_if_let_expr->accept_vis (*this);
+  if (conseq_if_let_expr->is_marked_for_strip ())
+    rust_error_at (conseq_if_let_expr->get_locus (),
+		   "cannot strip consequent if let expression in this "
+		   "position - outer attributes not "
+		   "allowed");
+}
+void
+AttrVisitor::visit (AST::IfLetExpr &expr)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  for (auto &pattern : expr.get_patterns ())
+    {
+      pattern->accept_vis (*this);
+      if (pattern->is_marked_for_strip ())
+	rust_error_at (pattern->get_locus (),
+		       "cannot strip pattern in this position");
+    }
+
+  // can't strip value expr itself, but can strip sub-expressions
+  auto &value_expr = expr.get_value_expr ();
+  value_expr->accept_vis (*this);
+  if (value_expr->is_marked_for_strip ())
+    rust_error_at (value_expr->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+
+  // can't strip if block itself, but can strip sub-expressions
+  auto &if_block = expr.get_if_block ();
+  if_block->accept_vis (*this);
+  if (if_block->is_marked_for_strip ())
+    rust_error_at (if_block->get_locus (),
+		   "cannot strip block expression in this position - outer "
+		   "attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::IfLetExprConseqElse &expr)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  for (auto &pattern : expr.get_patterns ())
+    {
+      pattern->accept_vis (*this);
+      if (pattern->is_marked_for_strip ())
+	rust_error_at (pattern->get_locus (),
+		       "cannot strip pattern in this position");
+    }
+
+  // can't strip value expr itself, but can strip sub-expressions
+  auto &value_expr = expr.get_value_expr ();
+  value_expr->accept_vis (*this);
+  if (value_expr->is_marked_for_strip ())
+    rust_error_at (value_expr->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+
+  // can't strip if block itself, but can strip sub-expressions
+  auto &if_block = expr.get_if_block ();
+  if_block->accept_vis (*this);
+  if (if_block->is_marked_for_strip ())
+    rust_error_at (if_block->get_locus (),
+		   "cannot strip block expression in this position - outer "
+		   "attributes not allowed");
+
+  // can't strip else block itself, but can strip sub-expressions
+  auto &else_block = expr.get_else_block ();
+  else_block->accept_vis (*this);
+  if (else_block->is_marked_for_strip ())
+    rust_error_at (else_block->get_locus (),
+		   "cannot strip block expression in this position - outer "
+		   "attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::IfLetExprConseqIf &expr)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  for (auto &pattern : expr.get_patterns ())
+    {
+      pattern->accept_vis (*this);
+      if (pattern->is_marked_for_strip ())
+	rust_error_at (pattern->get_locus (),
+		       "cannot strip pattern in this position");
+    }
+
+  // can't strip value expr itself, but can strip sub-expressions
+  auto &value_expr = expr.get_value_expr ();
+  value_expr->accept_vis (*this);
+  if (value_expr->is_marked_for_strip ())
+    rust_error_at (value_expr->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+
+  // can't strip if block itself, but can strip sub-expressions
+  auto &if_block = expr.get_if_block ();
+  if_block->accept_vis (*this);
+  if (if_block->is_marked_for_strip ())
+    rust_error_at (if_block->get_locus (),
+		   "cannot strip block expression in this position - outer "
+		   "attributes not allowed");
+
+  // can't strip if expr itself, but can strip sub-expressions
+  auto &conseq_if_expr = expr.get_conseq_if_expr ();
+  conseq_if_expr->accept_vis (*this);
+  if (conseq_if_expr->is_marked_for_strip ())
+    rust_error_at (conseq_if_expr->get_locus (),
+		   "cannot strip consequent if expression in this "
+		   "position - outer attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::IfLetExprConseqIfLet &expr)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  for (auto &pattern : expr.get_patterns ())
+    {
+      pattern->accept_vis (*this);
+      if (pattern->is_marked_for_strip ())
+	rust_error_at (pattern->get_locus (),
+		       "cannot strip pattern in this position");
+    }
+
+  // can't strip value expr itself, but can strip sub-expressions
+  auto &value_expr = expr.get_value_expr ();
+  value_expr->accept_vis (*this);
+  if (value_expr->is_marked_for_strip ())
+    rust_error_at (value_expr->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+
+  // can't strip if block itself, but can strip sub-expressions
+  auto &if_block = expr.get_if_block ();
+  if_block->accept_vis (*this);
+  if (if_block->is_marked_for_strip ())
+    rust_error_at (if_block->get_locus (),
+		   "cannot strip block expression in this position - outer "
+		   "attributes not allowed");
+
+  // can't strip if let expr itself, but can strip sub-expressions
+  auto &conseq_if_let_expr = expr.get_conseq_if_let_expr ();
+  conseq_if_let_expr->accept_vis (*this);
+  if (conseq_if_let_expr->is_marked_for_strip ())
+    rust_error_at (conseq_if_let_expr->get_locus (),
+		   "cannot strip consequent if let expression in this "
+		   "position - outer attributes not "
+		   "allowed");
+}
+void
+AttrVisitor::visit (AST::MatchExpr &expr)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  // inner attr strip test
+  expander.expand_cfg_attrs (expr.get_inner_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_inner_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  // can't strip scrutinee expr itself, but can strip sub-expressions
+  auto &scrutinee_expr = expr.get_scrutinee_expr ();
+  scrutinee_expr->accept_vis (*this);
+  if (scrutinee_expr->is_marked_for_strip ())
+    rust_error_at (scrutinee_expr->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+
+  // strip match cases
+  auto &match_cases = expr.get_match_cases ();
+  for (auto it = match_cases.begin (); it != match_cases.end ();)
+    {
+      auto &match_case = *it;
+
+      // strip match case based on outer attributes in match arm
+      auto &match_arm = match_case.get_arm ();
+      expander.expand_cfg_attrs (match_arm.get_outer_attrs ());
+      if (expander.fails_cfg_with_expand (match_arm.get_outer_attrs ()))
+	{
+	  // strip match case
+	  it = match_cases.erase (it);
+	  continue;
+	}
+
+      for (auto &pattern : match_arm.get_patterns ())
+	{
+	  pattern->accept_vis (*this);
+	  if (pattern->is_marked_for_strip ())
+	    rust_error_at (pattern->get_locus (),
+			   "cannot strip pattern in this position");
+	}
+
+      /* assuming that guard expression cannot be stripped as
+       * strictly speaking you would have to strip the whole guard to
+       * make syntactical sense, which you can't do. as such, only
+       * strip sub-expressions */
+      if (match_arm.has_match_arm_guard ())
+	{
+	  auto &guard_expr = match_arm.get_guard_expr ();
+	  guard_expr->accept_vis (*this);
+	  if (guard_expr->is_marked_for_strip ())
+	    rust_error_at (guard_expr->get_locus (),
+			   "cannot strip expression in this position - outer "
+			   "attributes not allowed");
+	}
+
+      // strip sub-expressions from match cases
+      auto &case_expr = match_case.get_expr ();
+      case_expr->accept_vis (*this);
+      if (case_expr->is_marked_for_strip ())
+	rust_error_at (case_expr->get_locus (),
+		       "cannot strip expression in this position - outer "
+		       "attributes not allowed");
+
+      // increment to next case if haven't continued
+      ++it;
+    }
+}
+void
+AttrVisitor::visit (AST::AwaitExpr &expr)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  /* can't strip awaited expr itself, but can strip sub-expressions
+   * - this is because you can't have no expr to await */
+  auto &awaited_expr = expr.get_awaited_expr ();
+  awaited_expr->accept_vis (*this);
+  if (awaited_expr->is_marked_for_strip ())
+    rust_error_at (awaited_expr->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::AsyncBlockExpr &expr)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (expr.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
+    {
+      expr.mark_for_strip ();
+      return;
+    }
+
+  // can't strip block itself, but can strip sub-expressions
+  auto &block_expr = expr.get_block_expr ();
+  block_expr->accept_vis (*this);
+  if (block_expr->is_marked_for_strip ())
+    rust_error_at (block_expr->get_locus (),
+		   "cannot strip block expression in this position - outer "
+		   "attributes not allowed");
+}
+
+void
+AttrVisitor::visit (AST::TypeParam &param)
+{
+  // outer attributes don't actually do anything, so ignore them
+
+  if (param.has_type_param_bounds ())
+    {
+      // don't strip directly, only components of bounds
+      for (auto &bound : param.get_type_param_bounds ())
+	bound->accept_vis (*this);
+    }
+
+  if (param.has_type ())
+    {
+      expander.push_context (MacroExpander::ContextType::TYPE);
+      auto &type = param.get_type ();
+      type->accept_vis (*this);
+
+      maybe_expand_type (type);
+
+      if (type->is_marked_for_strip ())
+	rust_error_at (type->get_locus (),
+		       "cannot strip type in this position");
+
+      expander.pop_context ();
+    }
+}
+void
+AttrVisitor::visit (AST::LifetimeWhereClauseItem &)
+{
+  // shouldn't require
+}
+void
+AttrVisitor::visit (AST::TypeBoundWhereClauseItem &item)
+{
+  // for lifetimes shouldn't require
+
+  expander.push_context (MacroExpander::ContextType::TYPE);
+
+  auto &type = item.get_type ();
+  type->accept_vis (*this);
+
+  maybe_expand_type (type);
+
+  if (type->is_marked_for_strip ())
+    rust_error_at (type->get_locus (), "cannot strip type in this position");
+
+  expander.pop_context ();
+
+  // don't strip directly, only components of bounds
+  for (auto &bound : item.get_type_param_bounds ())
+    bound->accept_vis (*this);
+}
+void
+AttrVisitor::visit (AST::Method &method)
+{
+  // initial test based on outer attrs
+  expander.expand_cfg_attrs (method.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (method.get_outer_attrs ()))
+    {
+      method.mark_for_strip ();
+      return;
+    }
+
+  // just expand sub-stuff - can't actually strip generic params themselves
+  for (auto &param : method.get_generic_params ())
+    param->accept_vis (*this);
+
+  /* assuming you can't strip self param - wouldn't be a method
+   * anymore. spec allows outer attrs on self param, but doesn't
+   * specify whether cfg is used. */
+  expand_self_param (method.get_self_param ());
+
+  /* strip method parameters if required - this is specifically
+   * allowed by spec */
+  expand_function_params (method.get_function_params ());
+
+  if (method.has_return_type ())
+    {
+      expander.push_context (MacroExpander::ContextType::TYPE);
+
+      auto &return_type = method.get_return_type ();
+      return_type->accept_vis (*this);
+
+      maybe_expand_type (return_type);
+
+      if (return_type->is_marked_for_strip ())
+	rust_error_at (return_type->get_locus (),
+		       "cannot strip type in this position");
+
+      expander.pop_context ();
+    }
+
+  if (method.has_where_clause ())
+    expand_where_clause (method.get_where_clause ());
+
+  /* body should always exist - if error state, should have returned
+   * before now */
+  // can't strip block itself, but can strip sub-expressions
+  auto &block_expr = method.get_definition ();
+  block_expr->accept_vis (*this);
+  if (block_expr->is_marked_for_strip ())
+    rust_error_at (block_expr->get_locus (),
+		   "cannot strip block expression in this position - outer "
+		   "attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::Module &module)
+{
+  // strip test based on outer attrs
+  expander.expand_cfg_attrs (module.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (module.get_outer_attrs ()))
+    {
+      module.mark_for_strip ();
+      return;
+    }
+
+  // A loaded module might have inner attributes
+  if (module.get_kind () == AST::Module::ModuleKind::LOADED)
+    {
+      // strip test based on inner attrs
+      expander.expand_cfg_attrs (module.get_inner_attrs ());
+      if (expander.fails_cfg_with_expand (module.get_inner_attrs ()))
+	{
+	  module.mark_for_strip ();
+	  return;
+	}
+    }
+
+  // Parse the module's items if they haven't been expanded and the file
+  // should be parsed (i.e isn't hidden behind an untrue or impossible cfg
+  // directive)
+  if (!module.is_marked_for_strip ()
+      && module.get_kind () == AST::Module::ModuleKind::UNLOADED)
+    {
+      module.load_items ();
+    }
+
+  // strip items if required
+  expand_pointer_allow_strip (module.get_items ());
+}
+void
+AttrVisitor::visit (AST::ExternCrate &extern_crate)
+{
+  // strip test based on outer attrs
+  expander.expand_cfg_attrs (extern_crate.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (extern_crate.get_outer_attrs ()))
+    {
+      extern_crate.mark_for_strip ();
+      return;
+    }
+
+  if (!extern_crate.references_self ())
+    {
+      Session &session = Session::get_instance ();
+      session.load_extern_crate (extern_crate.get_referenced_crate (),
+				 extern_crate.get_locus ());
+    }
+}
+void
+AttrVisitor::visit (AST::UseTreeGlob &)
+{
+  // shouldn't require?
+}
+void
+AttrVisitor::visit (AST::UseTreeList &)
+{
+  // shouldn't require?
+}
+void
+AttrVisitor::visit (AST::UseTreeRebind &)
+{
+  // shouldn't require?
+}
+void
+AttrVisitor::visit (AST::UseDeclaration &use_decl)
+{
+  // strip test based on outer attrs
+  expander.expand_cfg_attrs (use_decl.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (use_decl.get_outer_attrs ()))
+    {
+      use_decl.mark_for_strip ();
+      return;
+    }
+}
+void
+AttrVisitor::visit (AST::Function &function)
+{
+  // initial test based on outer attrs
+  expander.expand_cfg_attrs (function.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (function.get_outer_attrs ()))
+    {
+      function.mark_for_strip ();
+      return;
+    }
+
+  // just expand sub-stuff - can't actually strip generic params themselves
+  for (auto &param : function.get_generic_params ())
+    param->accept_vis (*this);
+
+  /* strip function parameters if required - this is specifically
+   * allowed by spec */
+  expand_function_params (function.get_function_params ());
+
+  if (function.has_return_type ())
+    {
+      expander.push_context (MacroExpander::ContextType::TYPE);
+
+      auto &return_type = function.get_return_type ();
+      return_type->accept_vis (*this);
+
+      maybe_expand_type (return_type);
+
+      if (return_type->is_marked_for_strip ())
+	rust_error_at (return_type->get_locus (),
+		       "cannot strip type in this position");
+
+      expander.pop_context ();
+    }
+
+  if (function.has_where_clause ())
+    expand_where_clause (function.get_where_clause ());
+
+  /* body should always exist - if error state, should have returned
+   * before now */
+  // can't strip block itself, but can strip sub-expressions
+  auto &block_expr = function.get_definition ();
+  block_expr->accept_vis (*this);
+  if (block_expr->is_marked_for_strip ())
+    rust_error_at (block_expr->get_locus (),
+		   "cannot strip block expression in this position - outer "
+		   "attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::TypeAlias &type_alias)
+{
+  // initial test based on outer attrs
+  expander.expand_cfg_attrs (type_alias.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (type_alias.get_outer_attrs ()))
+    {
+      type_alias.mark_for_strip ();
+      return;
+    }
+
+  // just expand sub-stuff - can't actually strip generic params themselves
+  for (auto &param : type_alias.get_generic_params ())
+    param->accept_vis (*this);
+
+  if (type_alias.has_where_clause ())
+    expand_where_clause (type_alias.get_where_clause ());
+
+  auto &type = type_alias.get_type_aliased ();
+  type->accept_vis (*this);
+  if (type->is_marked_for_strip ())
+    rust_error_at (type->get_locus (), "cannot strip type in this position");
+}
+void
+AttrVisitor::visit (AST::StructStruct &struct_item)
+{
+  // initial test based on outer attrs
+  expander.expand_cfg_attrs (struct_item.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (struct_item.get_outer_attrs ()))
+    {
+      struct_item.mark_for_strip ();
+      return;
+    }
+
+  // just expand sub-stuff - can't actually strip generic params themselves
+  for (auto &param : struct_item.get_generic_params ())
+    param->accept_vis (*this);
+
+  if (struct_item.has_where_clause ())
+    expand_where_clause (struct_item.get_where_clause ());
+
+  /* strip struct fields if required - this is presumably
+   * allowed by spec */
+  expand_struct_fields (struct_item.get_fields ());
+}
+void
+AttrVisitor::visit (AST::TupleStruct &tuple_struct)
+{
+  // initial test based on outer attrs
+  expander.expand_cfg_attrs (tuple_struct.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (tuple_struct.get_outer_attrs ()))
+    {
+      tuple_struct.mark_for_strip ();
+      return;
+    }
+
+  // just expand sub-stuff - can't actually strip generic params themselves
+  for (auto &param : tuple_struct.get_generic_params ())
+    param->accept_vis (*this);
+
+  /* strip struct fields if required - this is presumably
+   * allowed by spec */
+  expand_tuple_fields (tuple_struct.get_fields ());
+
+  if (tuple_struct.has_where_clause ())
+    expand_where_clause (tuple_struct.get_where_clause ());
+}
+void
+AttrVisitor::visit (AST::EnumItem &item)
+{
+  // initial test based on outer attrs
+  expander.expand_cfg_attrs (item.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (item.get_outer_attrs ()))
+    {
+      item.mark_for_strip ();
+      return;
+    }
+}
+void
+AttrVisitor::visit (AST::EnumItemTuple &item)
+{
+  // initial test based on outer attrs
+  expander.expand_cfg_attrs (item.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (item.get_outer_attrs ()))
+    {
+      item.mark_for_strip ();
+      return;
+    }
+
+  /* strip item fields if required - this is presumably
+   * allowed by spec */
+  expand_tuple_fields (item.get_tuple_fields ());
+}
+void
+AttrVisitor::visit (AST::EnumItemStruct &item)
+{
+  // initial test based on outer attrs
+  expander.expand_cfg_attrs (item.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (item.get_outer_attrs ()))
+    {
+      item.mark_for_strip ();
+      return;
+    }
+
+  /* strip item fields if required - this is presumably
+   * allowed by spec */
+  expand_struct_fields (item.get_struct_fields ());
+}
+void
+AttrVisitor::visit (AST::EnumItemDiscriminant &item)
+{
+  // initial test based on outer attrs
+  expander.expand_cfg_attrs (item.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (item.get_outer_attrs ()))
+    {
+      item.mark_for_strip ();
+      return;
+    }
+
+  /* strip any internal sub-expressions - expression itself isn't
+   * allowed to have external attributes in this position so can't be
+   * stripped. */
+  auto &expr = item.get_expr ();
+  expr->accept_vis (*this);
+  if (expr->is_marked_for_strip ())
+    rust_error_at (expr->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::Enum &enum_item)
+{
+  // initial test based on outer attrs
+  expander.expand_cfg_attrs (enum_item.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (enum_item.get_outer_attrs ()))
+    {
+      enum_item.mark_for_strip ();
+      return;
+    }
+
+  // just expand sub-stuff - can't actually strip generic params themselves
+  for (auto &param : enum_item.get_generic_params ())
+    param->accept_vis (*this);
+
+  if (enum_item.has_where_clause ())
+    expand_where_clause (enum_item.get_where_clause ());
+
+  /* strip enum fields if required - this is presumably
+   * allowed by spec */
+  expand_pointer_allow_strip (enum_item.get_variants ());
+}
+void
+AttrVisitor::visit (AST::Union &union_item)
+{
+  // initial test based on outer attrs
+  expander.expand_cfg_attrs (union_item.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (union_item.get_outer_attrs ()))
+    {
+      union_item.mark_for_strip ();
+      return;
+    }
+
+  // just expand sub-stuff - can't actually strip generic params themselves
+  for (auto &param : union_item.get_generic_params ())
+    param->accept_vis (*this);
+
+  if (union_item.has_where_clause ())
+    expand_where_clause (union_item.get_where_clause ());
+
+  /* strip union fields if required - this is presumably
+   * allowed by spec */
+  expand_struct_fields (union_item.get_variants ());
+}
+void
+AttrVisitor::visit (AST::ConstantItem &const_item)
+{
+  // initial test based on outer attrs
+  expander.expand_cfg_attrs (const_item.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (const_item.get_outer_attrs ()))
+    {
+      const_item.mark_for_strip ();
+      return;
+    }
+
+  expander.push_context (MacroExpander::ContextType::TYPE);
+
+  // strip any sub-types
+  auto &type = const_item.get_type ();
+  type->accept_vis (*this);
+
+  maybe_expand_type (type);
+
+  if (type->is_marked_for_strip ())
+    rust_error_at (type->get_locus (), "cannot strip type in this position");
+
+  expander.pop_context ();
+
+  /* strip any internal sub-expressions - expression itself isn't
+   * allowed to have external attributes in this position so can't be
+   * stripped. */
+  auto &expr = const_item.get_expr ();
+  expr->accept_vis (*this);
+  if (expr->is_marked_for_strip ())
+    rust_error_at (expr->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::StaticItem &static_item)
+{
+  // initial test based on outer attrs
+  expander.expand_cfg_attrs (static_item.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (static_item.get_outer_attrs ()))
+    {
+      static_item.mark_for_strip ();
+      return;
+    }
+
+  expander.push_context (MacroExpander::ContextType::TYPE);
+
+  // strip any sub-types
+  auto &type = static_item.get_type ();
+  type->accept_vis (*this);
+
+  maybe_expand_type (type);
+
+  if (type->is_marked_for_strip ())
+    rust_error_at (type->get_locus (), "cannot strip type in this position");
+
+  expander.pop_context ();
+
+  /* strip any internal sub-expressions - expression itself isn't
+   * allowed to have external attributes in this position so can't be
+   * stripped. */
+  auto &expr = static_item.get_expr ();
+  expr->accept_vis (*this);
+  if (expr->is_marked_for_strip ())
+    rust_error_at (expr->get_locus (),
+		   "cannot strip expression in this position - outer "
+		   "attributes not allowed");
+}
+void
+AttrVisitor::visit (AST::TraitItemFunc &item)
+{
+  // initial test based on outer attrs
+  expander.expand_cfg_attrs (item.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (item.get_outer_attrs ()))
+    {
+      item.mark_for_strip ();
+      return;
+    }
+
+  expand_trait_function_decl (item.get_trait_function_decl ());
+
+  if (item.has_definition ())
+    {
+      /* strip any internal sub-expressions - expression itself isn't
+       * allowed to have external attributes in this position so can't be
+       * stripped. */
+      auto &block = item.get_definition ();
+      block->accept_vis (*this);
+      if (block->is_marked_for_strip ())
+	rust_error_at (block->get_locus (),
+		       "cannot strip block expression in this "
+		       "position - outer attributes not allowed");
+    }
+}
+void
+AttrVisitor::visit (AST::TraitItemMethod &item)
+{
+  // initial test based on outer attrs
+  expander.expand_cfg_attrs (item.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (item.get_outer_attrs ()))
+    {
+      item.mark_for_strip ();
+      return;
+    }
+
+  expand_trait_method_decl (item.get_trait_method_decl ());
+
+  if (item.has_definition ())
+    {
+      /* strip any internal sub-expressions - expression itself isn't
+       * allowed to have external attributes in this position so can't be
+       * stripped. */
+      auto &block = item.get_definition ();
+      block->accept_vis (*this);
+      if (block->is_marked_for_strip ())
+	rust_error_at (block->get_locus (),
+		       "cannot strip block expression in this "
+		       "position - outer attributes not allowed");
+    }
+}
+void
+AttrVisitor::visit (AST::TraitItemConst &item)
+{
+  // initial test based on outer attrs
+  expander.expand_cfg_attrs (item.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (item.get_outer_attrs ()))
+    {
+      item.mark_for_strip ();
+      return;
+    }
+
+  expander.push_context (MacroExpander::ContextType::TYPE);
+
+  // strip any sub-types
+  auto &type = item.get_type ();
+  type->accept_vis (*this);
+
+  maybe_expand_type (type);
+
+  if (type->is_marked_for_strip ())
+    rust_error_at (type->get_locus (), "cannot strip type in this position");
+
+  expander.pop_context ();
+
+  /* strip any internal sub-expressions - expression itself isn't
+   * allowed to have external attributes in this position so can't be
+   * stripped */
+  if (item.has_expression ())
+    {
+      auto &expr = item.get_expr ();
+      expr->accept_vis (*this);
+      if (expr->is_marked_for_strip ())
+	rust_error_at (expr->get_locus (),
+		       "cannot strip expression in this position - outer "
+		       "attributes not allowed");
+    }
+}
+void
+AttrVisitor::visit (AST::TraitItemType &item)
+{
+  // initial test based on outer attrs
+  expander.expand_cfg_attrs (item.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (item.get_outer_attrs ()))
+    {
+      item.mark_for_strip ();
+      return;
+    }
+
+  if (item.has_type_param_bounds ())
+    {
+      // don't strip directly, only components of bounds
+      for (auto &bound : item.get_type_param_bounds ())
+	bound->accept_vis (*this);
+    }
+}
+void
+AttrVisitor::visit (AST::Trait &trait)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (trait.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (trait.get_outer_attrs ()))
+    {
+      trait.mark_for_strip ();
+      return;
+    }
+
+  // strip test based on inner attrs
+  expander.expand_cfg_attrs (trait.get_inner_attrs ());
+  if (expander.fails_cfg_with_expand (trait.get_inner_attrs ()))
+    {
+      trait.mark_for_strip ();
+      return;
+    }
+
+  // just expand sub-stuff - can't actually strip generic params themselves
+  for (auto &param : trait.get_generic_params ())
+    param->accept_vis (*this);
+
+  if (trait.has_type_param_bounds ())
+    {
+      // don't strip directly, only components of bounds
+      for (auto &bound : trait.get_type_param_bounds ())
+	bound->accept_vis (*this);
+    }
+
+  if (trait.has_where_clause ())
+    expand_where_clause (trait.get_where_clause ());
+
+  std::function<std::unique_ptr<AST::TraitItem> (AST::SingleASTNode)> extractor
+    = [] (AST::SingleASTNode node) { return node.take_trait_item (); };
+
+  expand_macro_children (MacroExpander::TRAIT, trait.get_trait_items (),
+			 extractor);
+}
+void
+AttrVisitor::visit (AST::InherentImpl &impl)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (impl.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (impl.get_outer_attrs ()))
+    {
+      impl.mark_for_strip ();
+      return;
+    }
+
+  // strip test based on inner attrs
+  expander.expand_cfg_attrs (impl.get_inner_attrs ());
+  if (expander.fails_cfg_with_expand (impl.get_inner_attrs ()))
+    {
+      impl.mark_for_strip ();
+      return;
+    }
+
+  // just expand sub-stuff - can't actually strip generic params themselves
+  for (auto &param : impl.get_generic_params ())
+    param->accept_vis (*this);
+
+  expander.push_context (MacroExpander::ContextType::TYPE);
+
+  auto &type = impl.get_type ();
+  type->accept_vis (*this);
+
+  maybe_expand_type (type);
+
+  if (type->is_marked_for_strip ())
+    rust_error_at (type->get_locus (), "cannot strip type in this position");
+
+  expander.pop_context ();
+
+  if (impl.has_where_clause ())
+    expand_where_clause (impl.get_where_clause ());
+
+  std::function<std::unique_ptr<AST::InherentImplItem> (AST::SingleASTNode)>
+    extractor = [] (AST::SingleASTNode node) { return node.take_impl_item (); };
+
+  expand_macro_children (MacroExpander::IMPL, impl.get_impl_items (),
+			 extractor);
+}
+void
+AttrVisitor::visit (AST::TraitImpl &impl)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (impl.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (impl.get_outer_attrs ()))
+    {
+      impl.mark_for_strip ();
+      return;
+    }
+
+  // strip test based on inner attrs
+  expander.expand_cfg_attrs (impl.get_inner_attrs ());
+  if (expander.fails_cfg_with_expand (impl.get_inner_attrs ()))
+    {
+      impl.mark_for_strip ();
+      return;
+    }
+
+  // just expand sub-stuff - can't actually strip generic params themselves
+  for (auto &param : impl.get_generic_params ())
+    param->accept_vis (*this);
+
+  expander.push_context (MacroExpander::ContextType::TYPE);
+
+  auto &type = impl.get_type ();
+  type->accept_vis (*this);
+
+  maybe_expand_type (type);
+
+  if (type->is_marked_for_strip ())
+    rust_error_at (type->get_locus (), "cannot strip type in this position");
+
+  expander.pop_context ();
+
+  auto &trait_path = impl.get_trait_path ();
+  visit (trait_path);
+  if (trait_path.is_marked_for_strip ())
+    rust_error_at (trait_path.get_locus (),
+		   "cannot strip typepath in this position");
+
+  if (impl.has_where_clause ())
+    expand_where_clause (impl.get_where_clause ());
+
+  std::function<std::unique_ptr<AST::TraitImplItem> (AST::SingleASTNode)>
+    extractor
+    = [] (AST::SingleASTNode node) { return node.take_trait_impl_item (); };
+
+  expand_macro_children (MacroExpander::TRAIT_IMPL, impl.get_impl_items (),
+			 extractor);
+}
+void
+AttrVisitor::visit (AST::ExternalStaticItem &item)
+{
+  // strip test based on outer attrs
+  expander.expand_cfg_attrs (item.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (item.get_outer_attrs ()))
+    {
+      item.mark_for_strip ();
+      return;
+    }
+
+  expander.push_context (MacroExpander::ContextType::TYPE);
+
+  auto &type = item.get_type ();
+  type->accept_vis (*this);
+
+  maybe_expand_type (type);
+
+  if (type->is_marked_for_strip ())
+    rust_error_at (type->get_locus (), "cannot strip type in this position");
+
+  expander.pop_context ();
+}
+void
+AttrVisitor::visit (AST::ExternalFunctionItem &item)
+{
+  // strip test based on outer attrs
+  expander.expand_cfg_attrs (item.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (item.get_outer_attrs ()))
+    {
+      item.mark_for_strip ();
+      return;
+    }
+
+  // just expand sub-stuff - can't actually strip generic params themselves
+  for (auto &param : item.get_generic_params ())
+    param->accept_vis (*this);
+
+  /* strip function parameters if required - this is specifically
+   * allowed by spec */
+  auto &params = item.get_function_params ();
+  for (auto it = params.begin (); it != params.end ();)
+    {
+      auto &param = *it;
+
+      auto &param_attrs = param.get_outer_attrs ();
+      expander.expand_cfg_attrs (param_attrs);
+      if (expander.fails_cfg_with_expand (param_attrs))
+	{
+	  it = params.erase (it);
+	  continue;
+	}
+
+      expander.push_context (MacroExpander::ContextType::TYPE);
+
+      auto &type = param.get_type ();
+      type->accept_vis (*this);
+
+      maybe_expand_type (type);
+
+      if (type->is_marked_for_strip ())
+	rust_error_at (type->get_locus (),
+		       "cannot strip type in this position");
+
+      expander.pop_context ();
+
+      // increment if nothing else happens
+      ++it;
+    }
+  /* NOTE: these are extern function params, which may have different
+   * rules and restrictions to "normal" function params. So expansion
+   * handled separately. */
+
+  /* TODO: assuming that variadic nature cannot be stripped. If this
+   * is not true, then have code here to do so. */
+
+  if (item.has_return_type ())
+    {
+      expander.push_context (MacroExpander::ContextType::TYPE);
+
+      auto &return_type = item.get_return_type ();
+      return_type->accept_vis (*this);
+
+      maybe_expand_type (return_type);
+
+      if (return_type->is_marked_for_strip ())
+	rust_error_at (return_type->get_locus (),
+		       "cannot strip type in this position");
+
+      expander.pop_context ();
+    }
+
+  if (item.has_where_clause ())
+    expand_where_clause (item.get_where_clause ());
+}
+
+void
+AttrVisitor::visit (AST::ExternBlock &block)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (block.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (block.get_outer_attrs ()))
+    {
+      block.mark_for_strip ();
+      return;
+    }
+
+  // strip test based on inner attrs
+  expander.expand_cfg_attrs (block.get_inner_attrs ());
+  if (expander.fails_cfg_with_expand (block.get_inner_attrs ()))
+    {
+      block.mark_for_strip ();
+      return;
+    }
+
+  std::function<std::unique_ptr<AST::ExternalItem> (AST::SingleASTNode)>
+    extractor
+    = [] (AST::SingleASTNode node) { return node.take_external_item (); };
+
+  expand_macro_children (MacroExpander::EXTERN, block.get_extern_items (),
+			 extractor);
+}
+
+// I don't think it would be possible to strip macros without expansion
+void
+AttrVisitor::visit (AST::MacroMatchFragment &)
+{}
+void
+AttrVisitor::visit (AST::MacroMatchRepetition &)
+{}
+void
+AttrVisitor::visit (AST::MacroMatcher &)
+{}
+void
+AttrVisitor::visit (AST::MacroRulesDefinition &rules_def)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (rules_def.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (rules_def.get_outer_attrs ()))
+    {
+      rules_def.mark_for_strip ();
+      return;
+    }
+
+  // I don't think any macro rules can be stripped in any way
+
+  auto path = Resolver::CanonicalPath::new_seg (rules_def.get_node_id (),
+						rules_def.get_rule_name ());
+  expander.resolver->get_macro_scope ().insert (path, rules_def.get_node_id (),
+						rules_def.get_locus ());
+  expander.mappings->insert_macro_def (&rules_def);
+  rust_debug_loc (rules_def.get_locus (), "inserting macro def: [%s]",
+		  path.get ().c_str ());
+}
+
+void
+AttrVisitor::visit (AST::MetaItemPath &)
+{}
+void
+AttrVisitor::visit (AST::MetaItemSeq &)
+{}
+void
+AttrVisitor::visit (AST::MetaWord &)
+{}
+void
+AttrVisitor::visit (AST::MetaNameValueStr &)
+{}
+void
+AttrVisitor::visit (AST::MetaListPaths &)
+{}
+void
+AttrVisitor::visit (AST::MetaListNameValueStr &)
+{}
+
+void
+AttrVisitor::visit (AST::LiteralPattern &)
+{
+  // not possible
+}
+void
+AttrVisitor::visit (AST::IdentifierPattern &pattern)
+{
+  // can only strip sub-patterns of the inner pattern to bind
+  if (!pattern.has_pattern_to_bind ())
+    return;
+
+  auto &sub_pattern = pattern.get_pattern_to_bind ();
+  sub_pattern->accept_vis (*this);
+  if (sub_pattern->is_marked_for_strip ())
+    rust_error_at (sub_pattern->get_locus (),
+		   "cannot strip pattern in this position");
+}
+void
+AttrVisitor::visit (AST::WildcardPattern &)
+{
+  // not possible
+}
+void
+AttrVisitor::visit (AST::RangePatternBoundLiteral &)
+{
+  // not possible
+}
+void
+AttrVisitor::visit (AST::RangePatternBoundPath &bound)
+{
+  // can expand path, but not strip it directly
+  auto &path = bound.get_path ();
+  visit (path);
+  if (path.is_marked_for_strip ())
+    rust_error_at (path.get_locus (), "cannot strip path in this position");
+}
+void
+AttrVisitor::visit (AST::RangePatternBoundQualPath &bound)
+{
+  // can expand path, but not strip it directly
+  auto &path = bound.get_qualified_path ();
+  visit (path);
+  if (path.is_marked_for_strip ())
+    rust_error_at (path.get_locus (), "cannot strip path in this position");
+}
+void
+AttrVisitor::visit (AST::RangePattern &pattern)
+{
+  // should have no capability to strip lower or upper bounds, only expand
+  pattern.get_lower_bound ()->accept_vis (*this);
+  pattern.get_upper_bound ()->accept_vis (*this);
+}
+void
+AttrVisitor::visit (AST::ReferencePattern &pattern)
+{
+  auto &sub_pattern = pattern.get_referenced_pattern ();
+  sub_pattern->accept_vis (*this);
+  if (sub_pattern->is_marked_for_strip ())
+    rust_error_at (sub_pattern->get_locus (),
+		   "cannot strip pattern in this position");
+}
+void
+AttrVisitor::visit (AST::StructPatternFieldTuplePat &field)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (field.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (field.get_outer_attrs ()))
+    {
+      field.mark_for_strip ();
+      return;
+    }
+
+  // strip sub-patterns (can't strip top-level pattern)
+  auto &sub_pattern = field.get_index_pattern ();
+  sub_pattern->accept_vis (*this);
+  if (sub_pattern->is_marked_for_strip ())
+    rust_error_at (sub_pattern->get_locus (),
+		   "cannot strip pattern in this position");
+}
+void
+AttrVisitor::visit (AST::StructPatternFieldIdentPat &field)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (field.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (field.get_outer_attrs ()))
+    {
+      field.mark_for_strip ();
+      return;
+    }
+
+  // strip sub-patterns (can't strip top-level pattern)
+  auto &sub_pattern = field.get_ident_pattern ();
+  sub_pattern->accept_vis (*this);
+  if (sub_pattern->is_marked_for_strip ())
+    rust_error_at (sub_pattern->get_locus (),
+		   "cannot strip pattern in this position");
+}
+void
+AttrVisitor::visit (AST::StructPatternFieldIdent &field)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (field.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (field.get_outer_attrs ()))
+    {
+      field.mark_for_strip ();
+      return;
+    }
+}
+void
+AttrVisitor::visit (AST::StructPattern &pattern)
+{
+  // expand (but don't strip) path
+  auto &path = pattern.get_path ();
+  visit (path);
+  if (path.is_marked_for_strip ())
+    rust_error_at (path.get_locus (), "cannot strip path in this position");
+
+  /* TODO: apparently struct pattern fields can have outer attrs. so can they
+   * be stripped? */
+  if (!pattern.has_struct_pattern_elems ())
+    return;
+
+  auto &elems = pattern.get_struct_pattern_elems ();
+
+  // assuming you can strip struct pattern fields
+  expand_pointer_allow_strip (elems.get_struct_pattern_fields ());
+
+  // assuming you can strip the ".." part
+  if (elems.has_etc ())
+    {
+      expander.expand_cfg_attrs (elems.get_etc_outer_attrs ());
+      if (expander.fails_cfg_with_expand (elems.get_etc_outer_attrs ()))
+	elems.strip_etc ();
+    }
+}
+void
+AttrVisitor::visit (AST::TupleStructItemsNoRange &tuple_items)
+{
+  // can't strip individual patterns, only sub-patterns
+  for (auto &pattern : tuple_items.get_patterns ())
+    {
+      pattern->accept_vis (*this);
+
+      if (pattern->is_marked_for_strip ())
+	rust_error_at (pattern->get_locus (),
+		       "cannot strip pattern in this position");
+      // TODO: quit stripping now? or keep going?
+    }
+}
+void
+AttrVisitor::visit (AST::TupleStructItemsRange &tuple_items)
+{
+  // can't strip individual patterns, only sub-patterns
+  for (auto &lower_pattern : tuple_items.get_lower_patterns ())
+    {
+      lower_pattern->accept_vis (*this);
+
+      if (lower_pattern->is_marked_for_strip ())
+	rust_error_at (lower_pattern->get_locus (),
+		       "cannot strip pattern in this position");
+      // TODO: quit stripping now? or keep going?
+    }
+  for (auto &upper_pattern : tuple_items.get_upper_patterns ())
+    {
+      upper_pattern->accept_vis (*this);
+
+      if (upper_pattern->is_marked_for_strip ())
+	rust_error_at (upper_pattern->get_locus (),
+		       "cannot strip pattern in this position");
+      // TODO: quit stripping now? or keep going?
+    }
+}
+void
+AttrVisitor::visit (AST::TupleStructPattern &pattern)
+{
+  // expand (but don't strip) path
+  auto &path = pattern.get_path ();
+  visit (path);
+  if (path.is_marked_for_strip ())
+    rust_error_at (path.get_locus (), "cannot strip path in this position");
+
+  if (pattern.has_items ())
+    pattern.get_items ()->accept_vis (*this);
+}
+void
+AttrVisitor::visit (AST::TuplePatternItemsMultiple &tuple_items)
+{
+  // can't strip individual patterns, only sub-patterns
+  for (auto &pattern : tuple_items.get_patterns ())
+    {
+      pattern->accept_vis (*this);
+
+      if (pattern->is_marked_for_strip ())
+	rust_error_at (pattern->get_locus (),
+		       "cannot strip pattern in this position");
+      // TODO: quit stripping now? or keep going?
+    }
+}
+void
+AttrVisitor::visit (AST::TuplePatternItemsRanged &tuple_items)
+{
+  // can't strip individual patterns, only sub-patterns
+  for (auto &lower_pattern : tuple_items.get_lower_patterns ())
+    {
+      lower_pattern->accept_vis (*this);
+
+      if (lower_pattern->is_marked_for_strip ())
+	rust_error_at (lower_pattern->get_locus (),
+		       "cannot strip pattern in this position");
+      // TODO: quit stripping now? or keep going?
+    }
+  for (auto &upper_pattern : tuple_items.get_upper_patterns ())
+    {
+      upper_pattern->accept_vis (*this);
+
+      if (upper_pattern->is_marked_for_strip ())
+	rust_error_at (upper_pattern->get_locus (),
+		       "cannot strip pattern in this position");
+      // TODO: quit stripping now? or keep going?
+    }
+}
+void
+AttrVisitor::visit (AST::TuplePattern &pattern)
+{
+  if (pattern.has_tuple_pattern_items ())
+    pattern.get_items ()->accept_vis (*this);
+}
+void
+AttrVisitor::visit (AST::GroupedPattern &pattern)
+{
+  // can't strip inner pattern, only sub-patterns
+  auto &pattern_in_parens = pattern.get_pattern_in_parens ();
+
+  pattern_in_parens->accept_vis (*this);
+
+  if (pattern_in_parens->is_marked_for_strip ())
+    rust_error_at (pattern_in_parens->get_locus (),
+		   "cannot strip pattern in this position");
+}
+void
+AttrVisitor::visit (AST::SlicePattern &pattern)
+{
+  // can't strip individual patterns, only sub-patterns
+  for (auto &item : pattern.get_items ())
+    {
+      item->accept_vis (*this);
+
+      if (item->is_marked_for_strip ())
+	rust_error_at (item->get_locus (),
+		       "cannot strip pattern in this position");
+      // TODO: quit stripping now? or keep going?
+    }
+}
+
+void
+AttrVisitor::visit (AST::EmptyStmt &)
+{
+  // assuming no outer attributes, so nothing can happen
+}
+void
+AttrVisitor::visit (AST::LetStmt &stmt)
+{
+  // initial strip test based on outer attrs
+  expander.expand_cfg_attrs (stmt.get_outer_attrs ());
+  if (expander.fails_cfg_with_expand (stmt.get_outer_attrs ()))
+    {
+      stmt.mark_for_strip ();
+      return;
+    }
+
+  // can't strip pattern, but call for sub-patterns
+  auto &pattern = stmt.get_pattern ();
+  pattern->accept_vis (*this);
+  if (pattern->is_marked_for_strip ())
+    rust_error_at (pattern->get_locus (),
+		   "cannot strip pattern in this position");
+
+  // similar for type
+  if (stmt.has_type ())
+    {
+      expander.push_context (MacroExpander::ContextType::TYPE);
+
+      auto &type = stmt.get_type ();
+      type->accept_vis (*this);
+
+      maybe_expand_type (type);
+
+      if (type->is_marked_for_strip ())
+	rust_error_at (type->get_locus (),
+		       "cannot strip type in this position");
+
+      expander.pop_context ();
+    }
+
+  /* strip any internal sub-expressions - expression itself isn't
+   * allowed to have external attributes in this position so can't be
+   * stripped */
+  if (stmt.has_init_expr ())
+    {
+      auto &init_expr = stmt.get_init_expr ();
+      init_expr->accept_vis (*this);
+
+      if (init_expr->is_marked_for_strip ())
+	rust_error_at (init_expr->get_locus (),
+		       "cannot strip expression in this position - outer "
+		       "attributes not allowed");
+
+      maybe_expand_expr (init_expr);
+    }
+}
+void
+AttrVisitor::visit (AST::ExprStmtWithoutBlock &stmt)
+{
+  // outer attributes associated with expr, so rely on expr
+
+  // guard - should prevent null pointer expr
+  if (stmt.is_marked_for_strip ())
+    return;
+
+  // strip if expr is to be stripped
+  auto &expr = stmt.get_expr ();
+  expr->accept_vis (*this);
+  if (expr->is_marked_for_strip ())
+    {
+      stmt.mark_for_strip ();
+      return;
+    }
+}
+void
+AttrVisitor::visit (AST::ExprStmtWithBlock &stmt)
+{
+  // outer attributes associated with expr, so rely on expr
+
+  // guard - should prevent null pointer expr
+  if (stmt.is_marked_for_strip ())
+    return;
+
+  // strip if expr is to be stripped
+  auto &expr = stmt.get_expr ();
+  expr->accept_vis (*this);
+  if (expr->is_marked_for_strip ())
+    {
+      stmt.mark_for_strip ();
+      return;
+    }
+}
+
+void
+AttrVisitor::visit (AST::TraitBound &bound)
+{
+  // nothing in for lifetimes to strip
+
+  // expand but don't strip type path
+  auto &path = bound.get_type_path ();
+  visit (path);
+  if (path.is_marked_for_strip ())
+    rust_error_at (path.get_locus (),
+		   "cannot strip type path in this position");
+}
+void
+AttrVisitor::visit (AST::ImplTraitType &type)
+{
+  // don't strip directly, only components of bounds
+  for (auto &bound : type.get_type_param_bounds ())
+    bound->accept_vis (*this);
+}
+void
+AttrVisitor::visit (AST::TraitObjectType &type)
+{
+  // don't strip directly, only components of bounds
+  for (auto &bound : type.get_type_param_bounds ())
+    bound->accept_vis (*this);
+}
+void
+AttrVisitor::visit (AST::ParenthesisedType &type)
+{
+  // expand but don't strip inner type
+  auto &inner_type = type.get_type_in_parens ();
+  inner_type->accept_vis (*this);
+  if (inner_type->is_marked_for_strip ())
+    rust_error_at (inner_type->get_locus (),
+		   "cannot strip type in this position");
+}
+void
+AttrVisitor::visit (AST::ImplTraitTypeOneBound &type)
+{
+  // no stripping possible
+  visit (type.get_trait_bound ());
+}
+void
+AttrVisitor::visit (AST::TraitObjectTypeOneBound &type)
+{
+  // no stripping possible
+  visit (type.get_trait_bound ());
+}
+void
+AttrVisitor::visit (AST::TupleType &type)
+{
+  // TODO: assuming that types can't be stripped as types don't have outer
+  // attributes
+  for (auto &elem_type : type.get_elems ())
+    {
+      elem_type->accept_vis (*this);
+      if (elem_type->is_marked_for_strip ())
+	rust_error_at (elem_type->get_locus (),
+		       "cannot strip type in this position");
+    }
+}
+void
+AttrVisitor::visit (AST::NeverType &)
+{
+  // no stripping possible
+}
+void
+AttrVisitor::visit (AST::RawPointerType &type)
+{
+  // expand but don't strip type pointed to
+  auto &pointed_type = type.get_type_pointed_to ();
+  pointed_type->accept_vis (*this);
+  if (pointed_type->is_marked_for_strip ())
+    rust_error_at (pointed_type->get_locus (),
+		   "cannot strip type in this position");
+}
+void
+AttrVisitor::visit (AST::ReferenceType &type)
+{
+  // expand but don't strip type referenced
+  auto &referenced_type = type.get_type_referenced ();
+  referenced_type->accept_vis (*this);
+  if (referenced_type->is_marked_for_strip ())
+    rust_error_at (referenced_type->get_locus (),
+		   "cannot strip type in this position");
+}
+void
+AttrVisitor::visit (AST::ArrayType &type)
+{
+  // expand but don't strip type referenced
+  auto &base_type = type.get_elem_type ();
+  base_type->accept_vis (*this);
+  if (base_type->is_marked_for_strip ())
+    rust_error_at (base_type->get_locus (),
+		   "cannot strip type in this position");
+
+  // same for expression
+  auto &size_expr = type.get_size_expr ();
+  size_expr->accept_vis (*this);
+  if (size_expr->is_marked_for_strip ())
+    rust_error_at (size_expr->get_locus (),
+		   "cannot strip expression in this position");
+}
+void
+AttrVisitor::visit (AST::SliceType &type)
+{
+  // expand but don't strip elem type
+  auto &elem_type = type.get_elem_type ();
+  elem_type->accept_vis (*this);
+  if (elem_type->is_marked_for_strip ())
+    rust_error_at (elem_type->get_locus (),
+		   "cannot strip type in this position");
+}
+void
+AttrVisitor::visit (AST::InferredType &)
+{
+  // none possible
+}
+void
+AttrVisitor::visit (AST::BareFunctionType &type)
+{
+  // seem to be no generics
+
+  // presumably function params can be stripped
+  auto &params = type.get_function_params ();
+  for (auto it = params.begin (); it != params.end ();)
+    {
+      auto &param = *it;
+
+      auto &param_attrs = param.get_outer_attrs ();
+      expander.expand_cfg_attrs (param_attrs);
+      if (expander.fails_cfg_with_expand (param_attrs))
+	{
+	  it = params.erase (it);
+	  continue;
+	}
+
+      expander.push_context (MacroExpander::ContextType::TYPE);
+
+      auto &type = param.get_type ();
+      type->accept_vis (*this);
+
+      maybe_expand_type (type);
+
+      if (type->is_marked_for_strip ())
+	rust_error_at (type->get_locus (),
+		       "cannot strip type in this position");
+
+      expander.pop_context ();
+
+      // increment if nothing else happens
+      ++it;
+    }
+
+  /* TODO: assuming that variadic nature cannot be stripped. If this
+   * is not true, then have code here to do so. */
+
+  if (type.has_return_type ())
+    {
+      // FIXME: Can we have type expansion in this position?
+      // In that case, we need to handle AST::TypeNoBounds on top of just
+      // AST::Types
+      auto &return_type = type.get_return_type ();
+      return_type->accept_vis (*this);
+      if (return_type->is_marked_for_strip ())
+	rust_error_at (return_type->get_locus (),
+		       "cannot strip type in this position");
+    }
+
+  // no where clause, apparently
+}
+void
+AttrVisitor::maybe_expand_expr (std::unique_ptr<AST::Expr> &expr)
+{
+  auto final_fragment = expand_macro_fragment_recursive ();
+  if (final_fragment.should_expand ())
+    expr = final_fragment.take_expression_fragment ();
+}
+
+void
+AttrVisitor::maybe_expand_type (std::unique_ptr<AST::Type> &type)
+{
+  auto final_fragment = expand_macro_fragment_recursive ();
+  if (final_fragment.should_expand ())
+    type = final_fragment.take_type_fragment ();
+}
+} // namespace Rust
diff --git a/gcc/rust/expand/rust-attribute-visitor.h b/gcc/rust/expand/rust-attribute-visitor.h
new file mode 100644
index 00000000000..0f9d1065334
--- /dev/null
+++ b/gcc/rust/expand/rust-attribute-visitor.h
@@ -0,0 +1,316 @@
+// 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/>.
+
+#include "rust-ast-visitor.h"
+#include "rust-ast.h"
+#include "rust-macro-expand.h"
+
+namespace Rust {
+// Visitor used to expand attributes.
+class AttrVisitor : public AST::ASTVisitor
+{
+private:
+  MacroExpander &expander;
+  void maybe_expand_expr (std::unique_ptr<AST::Expr> &expr);
+  void maybe_expand_type (std::unique_ptr<AST::Type> &expr);
+
+public:
+  AttrVisitor (MacroExpander &expander) : expander (expander) {}
+
+  void expand_struct_fields (std::vector<AST::StructField> &fields);
+  void expand_tuple_fields (std::vector<AST::TupleField> &fields);
+  void expand_function_params (std::vector<AST::FunctionParam> &params);
+  void expand_generic_args (AST::GenericArgs &args);
+  void expand_qualified_path_type (AST::QualifiedPathType &path_type);
+  void expand_closure_params (std::vector<AST::ClosureParam> &params);
+  void expand_self_param (AST::SelfParam &self_param);
+  void expand_where_clause (AST::WhereClause &where_clause);
+  void expand_trait_function_decl (AST::TraitFunctionDecl &decl);
+  void expand_trait_method_decl (AST::TraitMethodDecl &decl);
+
+  /**
+   * Expand The current macro fragment recursively until it could not be
+   * expanded further.
+   *
+   * The return value checking works because correctly
+   * expanded fragment can never be an error (if the fragment can not be
+   * expanded, a stand-in error fragment will be returned; for fragments that
+   * could not be further expanded: the fragment prior to the expansion failure
+   * will be returned).
+   *
+   * @return Either the expanded fragment or an empty errored-out fragment
+   * indicating an expansion failure.
+   */
+  AST::ASTFragment expand_macro_fragment_recursive ()
+  {
+    auto fragment = expander.take_expanded_fragment (*this);
+    unsigned int original_depth = expander.expansion_depth;
+    auto final_fragment = AST::ASTFragment ({}, true);
+
+    while (fragment.should_expand ())
+      {
+	final_fragment = std::move (fragment);
+	expander.expansion_depth++;
+	// further expand the previously expanded macro fragment
+	auto new_fragment = expander.take_expanded_fragment (*this);
+	if (new_fragment.is_error ())
+	  break;
+	fragment = std::move (new_fragment);
+      }
+    expander.expansion_depth = original_depth;
+    return final_fragment;
+  }
+
+  /**
+   * Expand a set of values, erasing them if they are marked for strip, and
+   * replacing them with expanded macro nodes if necessary.
+   * This function is slightly different from `expand_pointer_allow_strip` as
+   * it can only be called in certain expansion contexts - where macro
+   * invocations are allowed.
+   *
+   * @param ctx Context to use for macro expansion
+   * @param values Iterable reference over values to replace or erase
+   * @param extractor Function to call when replacing values with the content
+   * 		of an expanded AST node
+   */
+  template <typename T, typename U>
+  void expand_macro_children (MacroExpander::ContextType ctx, T &values,
+			      std::function<U (AST::SingleASTNode)> extractor)
+  {
+    expander.push_context (ctx);
+
+    for (auto it = values.begin (); it != values.end ();)
+      {
+	auto &value = *it;
+
+	// mark for stripping if required
+	value->accept_vis (*this);
+
+	// recursively expand the children
+	auto final_fragment = expand_macro_fragment_recursive ();
+
+	if (final_fragment.should_expand ())
+	  {
+	    it = values.erase (it);
+	    for (auto &node : final_fragment.get_nodes ())
+	      {
+		auto new_node = extractor (node);
+		if (new_node != nullptr && !new_node->is_marked_for_strip ())
+		  {
+		    it = values.insert (it, std::move (new_node));
+		    it++;
+		  }
+	      }
+	  }
+	else if (value->is_marked_for_strip ())
+	  {
+	    it = values.erase (it);
+	  }
+	else
+	  {
+	    ++it;
+	  }
+      }
+
+    expander.pop_context ();
+  }
+
+  template <typename T> void expand_pointer_allow_strip (T &values)
+  {
+    for (auto it = values.begin (); it != values.end ();)
+      {
+	auto &value = *it;
+
+	// mark for stripping if required
+	value->accept_vis (*this);
+	if (value->is_marked_for_strip ())
+	  {
+	    it = values.erase (it);
+	  }
+	else
+	  {
+	    ++it;
+	  }
+      }
+  }
+
+  void visit (AST::Token &) override;
+  void visit (AST::DelimTokenTree &) override;
+  void visit (AST::AttrInputMetaItemContainer &) override;
+  void visit (AST::IdentifierExpr &ident_expr) override;
+  void visit (AST::Lifetime &) override;
+  void visit (AST::LifetimeParam &) override;
+  void visit (AST::ConstGenericParam &) override;
+
+  void visit (AST::MacroInvocation &macro_invoc) override;
+
+  void visit (AST::PathInExpression &path) override;
+  void visit (AST::TypePathSegment &) override;
+  void visit (AST::TypePathSegmentGeneric &segment) override;
+  void visit (AST::TypePathSegmentFunction &segment) override;
+  void visit (AST::TypePath &path) override;
+  void visit (AST::QualifiedPathInExpression &path) override;
+  void visit (AST::QualifiedPathInType &path) override;
+
+  void visit (AST::LiteralExpr &expr) override;
+  void visit (AST::AttrInputLiteral &) override;
+  void visit (AST::MetaItemLitExpr &) override;
+  void visit (AST::MetaItemPathLit &) override;
+  void visit (AST::BorrowExpr &expr) override;
+  void visit (AST::DereferenceExpr &expr) override;
+  void visit (AST::ErrorPropagationExpr &expr) override;
+  void visit (AST::NegationExpr &expr) override;
+  void visit (AST::ArithmeticOrLogicalExpr &expr) override;
+  void visit (AST::ComparisonExpr &expr) override;
+  void visit (AST::LazyBooleanExpr &expr) override;
+  void visit (AST::TypeCastExpr &expr) override;
+  void visit (AST::AssignmentExpr &expr) override;
+  void visit (AST::CompoundAssignmentExpr &expr) override;
+  void visit (AST::GroupedExpr &expr) override;
+  void visit (AST::ArrayElemsValues &elems) override;
+  void visit (AST::ArrayElemsCopied &elems) override;
+  void visit (AST::ArrayExpr &expr) override;
+  void visit (AST::ArrayIndexExpr &expr) override;
+  void visit (AST::TupleExpr &expr) override;
+  void visit (AST::TupleIndexExpr &expr) override;
+  void visit (AST::StructExprStruct &expr) override;
+  void visit (AST::StructExprFieldIdentifier &) override;
+  void visit (AST::StructExprFieldIdentifierValue &field) override;
+
+  void visit (AST::StructExprFieldIndexValue &field) override;
+  void visit (AST::StructExprStructFields &expr) override;
+  void visit (AST::StructExprStructBase &expr) override;
+  void visit (AST::CallExpr &expr) override;
+  void visit (AST::MethodCallExpr &expr) override;
+  void visit (AST::FieldAccessExpr &expr) override;
+  void visit (AST::ClosureExprInner &expr) override;
+
+  void visit (AST::BlockExpr &expr) override;
+
+  void visit (AST::ClosureExprInnerTyped &expr) override;
+  void visit (AST::ContinueExpr &expr) override;
+  void visit (AST::BreakExpr &expr) override;
+  void visit (AST::RangeFromToExpr &expr) override;
+  void visit (AST::RangeFromExpr &expr) override;
+  void visit (AST::RangeToExpr &expr) override;
+  void visit (AST::RangeFullExpr &) override;
+  void visit (AST::RangeFromToInclExpr &expr) override;
+  void visit (AST::RangeToInclExpr &expr) override;
+  void visit (AST::ReturnExpr &expr) override;
+  void visit (AST::UnsafeBlockExpr &expr) override;
+  void visit (AST::LoopExpr &expr) override;
+  void visit (AST::WhileLoopExpr &expr) override;
+  void visit (AST::WhileLetLoopExpr &expr) override;
+  void visit (AST::ForLoopExpr &expr) override;
+  void visit (AST::IfExpr &expr) override;
+  void visit (AST::IfExprConseqElse &expr) override;
+  void visit (AST::IfExprConseqIf &expr) override;
+  void visit (AST::IfExprConseqIfLet &expr) override;
+  void visit (AST::IfLetExpr &expr) override;
+  void visit (AST::IfLetExprConseqElse &expr) override;
+  void visit (AST::IfLetExprConseqIf &expr) override;
+  void visit (AST::IfLetExprConseqIfLet &expr) override;
+  void visit (AST::MatchExpr &expr) override;
+  void visit (AST::AwaitExpr &expr) override;
+  void visit (AST::AsyncBlockExpr &expr) override;
+  void visit (AST::TypeParam &param) override;
+  void visit (AST::LifetimeWhereClauseItem &) override;
+  void visit (AST::TypeBoundWhereClauseItem &item) override;
+  void visit (AST::Method &method) override;
+  void visit (AST::Module &module) override;
+  void visit (AST::ExternCrate &crate) override;
+  void visit (AST::UseTreeGlob &) override;
+  void visit (AST::UseTreeList &) override;
+  void visit (AST::UseTreeRebind &) override;
+  void visit (AST::UseDeclaration &use_decl) override;
+  void visit (AST::Function &function) override;
+  void visit (AST::TypeAlias &type_alias) override;
+  void visit (AST::StructStruct &struct_item) override;
+  void visit (AST::TupleStruct &tuple_struct) override;
+  void visit (AST::EnumItem &item) override;
+  void visit (AST::EnumItemTuple &item) override;
+  void visit (AST::EnumItemStruct &item) override;
+  void visit (AST::EnumItemDiscriminant &item) override;
+  void visit (AST::Enum &enum_item) override;
+  void visit (AST::Union &union_item) override;
+  void visit (AST::ConstantItem &const_item) override;
+  void visit (AST::StaticItem &static_item) override;
+  void visit (AST::TraitItemFunc &item) override;
+  void visit (AST::TraitItemMethod &item) override;
+  void visit (AST::TraitItemConst &item) override;
+  void visit (AST::TraitItemType &item) override;
+  void visit (AST::Trait &trait) override;
+  void visit (AST::InherentImpl &impl) override;
+  void visit (AST::TraitImpl &impl) override;
+  void visit (AST::ExternalStaticItem &item) override;
+  void visit (AST::ExternalFunctionItem &item) override;
+  void visit (AST::ExternBlock &block) override;
+
+  // I don't think it would be possible to strip macros without expansion
+  void visit (AST::MacroMatchFragment &) override;
+  void visit (AST::MacroMatchRepetition &) override;
+  void visit (AST::MacroMatcher &) override;
+  void visit (AST::MacroRulesDefinition &rules_def) override;
+  void visit (AST::MetaItemPath &) override;
+  void visit (AST::MetaItemSeq &) override;
+  void visit (AST::MetaWord &) override;
+  void visit (AST::MetaNameValueStr &) override;
+  void visit (AST::MetaListPaths &) override;
+  void visit (AST::MetaListNameValueStr &) override;
+  void visit (AST::LiteralPattern &) override;
+  void visit (AST::IdentifierPattern &pattern) override;
+  void visit (AST::WildcardPattern &) override;
+  void visit (AST::RangePatternBoundLiteral &) override;
+  void visit (AST::RangePatternBoundPath &bound) override;
+  void visit (AST::RangePatternBoundQualPath &bound) override;
+  void visit (AST::RangePattern &pattern) override;
+  void visit (AST::ReferencePattern &pattern) override;
+  void visit (AST::StructPatternFieldTuplePat &field) override;
+  void visit (AST::StructPatternFieldIdentPat &field) override;
+  void visit (AST::StructPatternFieldIdent &field) override;
+  void visit (AST::StructPattern &pattern) override;
+  void visit (AST::TupleStructItemsNoRange &tuple_items) override;
+  void visit (AST::TupleStructItemsRange &tuple_items) override;
+  void visit (AST::TupleStructPattern &pattern) override;
+  void visit (AST::TuplePatternItemsMultiple &tuple_items) override;
+  void visit (AST::TuplePatternItemsRanged &tuple_items) override;
+  void visit (AST::TuplePattern &pattern) override;
+  void visit (AST::GroupedPattern &pattern) override;
+  void visit (AST::SlicePattern &pattern) override;
+
+  void visit (AST::EmptyStmt &) override;
+  void visit (AST::LetStmt &stmt) override;
+  void visit (AST::ExprStmtWithoutBlock &stmt) override;
+  void visit (AST::ExprStmtWithBlock &stmt) override;
+
+  void visit (AST::TraitBound &bound) override;
+  void visit (AST::ImplTraitType &type) override;
+  void visit (AST::TraitObjectType &type) override;
+  void visit (AST::ParenthesisedType &type) override;
+  void visit (AST::ImplTraitTypeOneBound &type) override;
+  void visit (AST::TraitObjectTypeOneBound &type) override;
+  void visit (AST::TupleType &type) override;
+  void visit (AST::NeverType &) override;
+  void visit (AST::RawPointerType &type) override;
+  void visit (AST::ReferenceType &type) override;
+  void visit (AST::ArrayType &type) override;
+  void visit (AST::SliceType &type) override;
+  void visit (AST::InferredType &) override;
+  void visit (AST::BareFunctionType &type) override;
+};
+} // namespace Rust
diff --git a/gcc/rust/expand/rust-macro-builtins.cc b/gcc/rust/expand/rust-macro-builtins.cc
new file mode 100644
index 00000000000..5eace13d197
--- /dev/null
+++ b/gcc/rust/expand/rust-macro-builtins.cc
@@ -0,0 +1,484 @@
+// 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/>.
+
+#include "rust-macro-builtins.h"
+#include "rust-diagnostics.h"
+#include "rust-expr.h"
+#include "rust-session-manager.h"
+#include "rust-macro-invoc-lexer.h"
+#include "rust-lex.h"
+#include "rust-parse.h"
+
+namespace Rust {
+namespace {
+std::unique_ptr<AST::Expr>
+make_string (Location locus, std::string value)
+{
+  return std::unique_ptr<AST::Expr> (
+    new AST::LiteralExpr (value, AST::Literal::STRING,
+			  PrimitiveCoreType::CORETYPE_STR, {}, locus));
+}
+
+/* Match the end token of a macro given the start delimiter of the macro */
+
+static inline TokenId
+macro_end_token (AST::DelimTokenTree &invoc_token_tree,
+		 Parser<MacroInvocLexer> &parser)
+{
+  auto last_token_id = TokenId::RIGHT_CURLY;
+  switch (invoc_token_tree.get_delim_type ())
+    {
+    case AST::DelimType::PARENS:
+      last_token_id = TokenId::RIGHT_PAREN;
+      rust_assert (parser.skip_token (LEFT_PAREN));
+      break;
+
+    case AST::DelimType::CURLY:
+      rust_assert (parser.skip_token (LEFT_CURLY));
+      break;
+
+    case AST::DelimType::SQUARE:
+      last_token_id = TokenId::RIGHT_SQUARE;
+      rust_assert (parser.skip_token (LEFT_SQUARE));
+      break;
+    }
+
+  return last_token_id;
+}
+
+/* Parse a single string literal from the given delimited token tree,
+   and return the LiteralExpr for it. Allow for an optional trailing comma,
+   but otherwise enforce that these are the only tokens.  */
+
+std::unique_ptr<AST::LiteralExpr>
+parse_single_string_literal (AST::DelimTokenTree &invoc_token_tree,
+			     Location invoc_locus)
+{
+  MacroInvocLexer lex (invoc_token_tree.to_token_stream ());
+  Parser<MacroInvocLexer> parser (lex);
+
+  auto last_token_id = macro_end_token (invoc_token_tree, parser);
+
+  std::unique_ptr<AST::LiteralExpr> lit_expr = nullptr;
+
+  if (parser.peek_current_token ()->get_id () == STRING_LITERAL)
+    {
+      lit_expr = parser.parse_literal_expr ();
+      parser.maybe_skip_token (COMMA);
+      if (parser.peek_current_token ()->get_id () != last_token_id)
+	{
+	  lit_expr = nullptr;
+	  rust_error_at (invoc_locus, "macro takes 1 argument");
+	}
+    }
+  else if (parser.peek_current_token ()->get_id () == last_token_id)
+    rust_error_at (invoc_locus, "macro takes 1 argument");
+  else
+    rust_error_at (invoc_locus, "argument must be a string literal");
+
+  parser.skip_token (last_token_id);
+
+  return lit_expr;
+}
+
+/* Treat PATH as a path relative to the source file currently being
+   compiled, and return the absolute path for it.  */
+
+std::string
+source_relative_path (std::string path, Location locus)
+{
+  std::string compile_fname
+    = Session::get_instance ().linemap->location_file (locus);
+
+  auto dir_separator_pos = compile_fname.rfind (file_separator);
+
+  /* If there is no file_separator in the path, use current dir ('.').  */
+  std::string dirname;
+  if (dir_separator_pos == std::string::npos)
+    dirname = std::string (".") + file_separator;
+  else
+    dirname = compile_fname.substr (0, dir_separator_pos) + file_separator;
+
+  return dirname + path;
+}
+
+/* Read the full contents of the file FILENAME and return them in a vector.
+   FIXME: platform specific.  */
+
+std::vector<uint8_t>
+load_file_bytes (const char *filename)
+{
+  RAIIFile file_wrap (filename);
+  if (file_wrap.get_raw () == nullptr)
+    {
+      rust_error_at (Location (), "cannot open filename %s: %m", filename);
+      return std::vector<uint8_t> ();
+    }
+
+  FILE *f = file_wrap.get_raw ();
+  fseek (f, 0L, SEEK_END);
+  long fsize = ftell (f);
+  fseek (f, 0L, SEEK_SET);
+
+  std::vector<uint8_t> buf (fsize);
+
+  if (fread (&buf[0], fsize, 1, f) != 1)
+    {
+      rust_error_at (Location (), "error reading file %s: %m", filename);
+      return std::vector<uint8_t> ();
+    }
+
+  return buf;
+}
+} // namespace
+
+AST::ASTFragment
+MacroBuiltin::assert (Location invoc_locus, AST::MacroInvocData &invoc)
+{
+  rust_debug ("assert!() called");
+
+  return AST::ASTFragment::create_error ();
+}
+
+AST::ASTFragment
+MacroBuiltin::file (Location invoc_locus, AST::MacroInvocData &invoc)
+{
+  auto current_file
+    = Session::get_instance ().linemap->location_file (invoc_locus);
+  auto file_str = AST::SingleASTNode (make_string (invoc_locus, current_file));
+
+  return AST::ASTFragment ({file_str});
+}
+
+AST::ASTFragment
+MacroBuiltin::column (Location invoc_locus, AST::MacroInvocData &invoc)
+{
+  auto current_column
+    = Session::get_instance ().linemap->location_to_column (invoc_locus);
+
+  auto column_no = AST::SingleASTNode (std::unique_ptr<AST::Expr> (
+    new AST::LiteralExpr (std::to_string (current_column), AST::Literal::INT,
+			  PrimitiveCoreType::CORETYPE_U32, {}, invoc_locus)));
+
+  return AST::ASTFragment ({column_no});
+}
+
+/* Expand builtin macro include_bytes!("filename"), which includes the contents
+   of the given file as reference to a byte array. Yields an expression of type
+   &'static [u8; N].  */
+
+AST::ASTFragment
+MacroBuiltin::include_bytes (Location invoc_locus, AST::MacroInvocData &invoc)
+{
+  /* Get target filename from the macro invocation, which is treated as a path
+     relative to the include!-ing file (currently being compiled).  */
+  auto lit_expr
+    = parse_single_string_literal (invoc.get_delim_tok_tree (), invoc_locus);
+  if (lit_expr == nullptr)
+    return AST::ASTFragment::create_error ();
+
+  std::string target_filename
+    = source_relative_path (lit_expr->as_string (), invoc_locus);
+
+  std::vector<uint8_t> bytes = load_file_bytes (target_filename.c_str ());
+
+  /* Is there a more efficient way to do this?  */
+  std::vector<std::unique_ptr<AST::Expr>> elts;
+  for (uint8_t b : bytes)
+    {
+      elts.emplace_back (
+	new AST::LiteralExpr (std::string (1, (char) b), AST::Literal::BYTE,
+			      PrimitiveCoreType::CORETYPE_U8,
+			      {} /* outer_attrs */, invoc_locus));
+    }
+
+  auto elems = std::unique_ptr<AST::ArrayElems> (
+    new AST::ArrayElemsValues (std::move (elts), invoc_locus));
+
+  auto array = std::unique_ptr<AST::Expr> (
+    new AST::ArrayExpr (std::move (elems), {}, {}, invoc_locus));
+
+  auto borrow = std::unique_ptr<AST::Expr> (
+    new AST::BorrowExpr (std::move (array), false, false, {}, invoc_locus));
+
+  auto node = AST::SingleASTNode (std::move (borrow));
+  return AST::ASTFragment ({node});
+}
+
+/* Expand builtin macro include_str!("filename"), which includes the contents
+   of the given file as a string. The file must be UTF-8 encoded. Yields an
+   expression of type &'static str.  */
+
+AST::ASTFragment
+MacroBuiltin::include_str (Location invoc_locus, AST::MacroInvocData &invoc)
+{
+  /* Get target filename from the macro invocation, which is treated as a path
+     relative to the include!-ing file (currently being compiled).  */
+  auto lit_expr
+    = parse_single_string_literal (invoc.get_delim_tok_tree (), invoc_locus);
+  if (lit_expr == nullptr)
+    return AST::ASTFragment::create_error ();
+
+  std::string target_filename
+    = source_relative_path (lit_expr->as_string (), invoc_locus);
+
+  std::vector<uint8_t> bytes = load_file_bytes (target_filename.c_str ());
+
+  /* FIXME: Enforce that the file contents are valid UTF-8.  */
+  std::string str ((const char *) &bytes[0], bytes.size ());
+
+  auto node = AST::SingleASTNode (make_string (invoc_locus, str));
+  return AST::ASTFragment ({node});
+}
+
+/* Expand builtin macro compile_error!("error"), which forces a compile error
+   during the compile time. */
+AST::ASTFragment
+MacroBuiltin::compile_error (Location invoc_locus, AST::MacroInvocData &invoc)
+{
+  auto lit_expr
+    = parse_single_string_literal (invoc.get_delim_tok_tree (), invoc_locus);
+  if (lit_expr == nullptr)
+    return AST::ASTFragment::create_error ();
+
+  std::string error_string = lit_expr->as_string ();
+  rust_error_at (invoc_locus, "%s", error_string.c_str ());
+
+  return AST::ASTFragment::create_error ();
+}
+
+/* Expand builtin macro concat!(), which joins all the literal parameters
+   into a string with no delimiter. */
+
+AST::ASTFragment
+MacroBuiltin::concat (Location invoc_locus, AST::MacroInvocData &invoc)
+{
+  auto invoc_token_tree = invoc.get_delim_tok_tree ();
+  MacroInvocLexer lex (invoc_token_tree.to_token_stream ());
+  Parser<MacroInvocLexer> parser (lex);
+
+  auto str = std::string ();
+  bool has_error = false;
+
+  auto last_token_id = macro_end_token (invoc_token_tree, parser);
+
+  /* NOTE: concat! could accept no argument, so we don't have any checks here */
+  while (parser.peek_current_token ()->get_id () != last_token_id)
+    {
+      auto lit_expr = parser.parse_literal_expr ();
+      if (lit_expr)
+	{
+	  str += lit_expr->as_string ();
+	}
+      else
+	{
+	  auto current_token = parser.peek_current_token ();
+	  rust_error_at (current_token->get_locus (),
+			 "argument must be a constant literal");
+	  has_error = true;
+	  // Just crash if the current token can't be skipped
+	  rust_assert (parser.skip_token (current_token->get_id ()));
+	}
+      parser.maybe_skip_token (COMMA);
+    }
+
+  parser.skip_token (last_token_id);
+
+  if (has_error)
+    return AST::ASTFragment::create_error ();
+
+  auto node = AST::SingleASTNode (make_string (invoc_locus, str));
+  return AST::ASTFragment ({node});
+}
+
+/* Expand builtin macro env!(), which inspects an environment variable at
+   compile time. */
+
+AST::ASTFragment
+MacroBuiltin::env (Location invoc_locus, AST::MacroInvocData &invoc)
+{
+  auto invoc_token_tree = invoc.get_delim_tok_tree ();
+  MacroInvocLexer lex (invoc_token_tree.to_token_stream ());
+  Parser<MacroInvocLexer> parser (lex);
+
+  auto last_token_id = macro_end_token (invoc_token_tree, parser);
+
+  if (parser.peek_current_token ()->get_id () != STRING_LITERAL)
+    {
+      if (parser.peek_current_token ()->get_id () == last_token_id)
+	rust_error_at (invoc_locus, "env! takes 1 or 2 arguments");
+      else
+	rust_error_at (parser.peek_current_token ()->get_locus (),
+		       "argument must be a string literal");
+      return AST::ASTFragment::create_error ();
+    }
+
+  auto lit_expr = parser.parse_literal_expr ();
+  auto comma_skipped = parser.maybe_skip_token (COMMA);
+
+  std::unique_ptr<AST::LiteralExpr> error_expr = nullptr;
+
+  if (parser.peek_current_token ()->get_id () != last_token_id)
+    {
+      if (!comma_skipped)
+	{
+	  rust_error_at (parser.peek_current_token ()->get_locus (),
+			 "expected token: %<,%>");
+	  return AST::ASTFragment::create_error ();
+	}
+      if (parser.peek_current_token ()->get_id () != STRING_LITERAL)
+	{
+	  rust_error_at (parser.peek_current_token ()->get_locus (),
+			 "argument must be a string literal");
+	  return AST::ASTFragment::create_error ();
+	}
+
+      error_expr = parser.parse_literal_expr ();
+      parser.maybe_skip_token (COMMA);
+    }
+
+  if (parser.peek_current_token ()->get_id () != last_token_id)
+    {
+      rust_error_at (invoc_locus, "env! takes 1 or 2 arguments");
+      return AST::ASTFragment::create_error ();
+    }
+
+  parser.skip_token (last_token_id);
+
+  auto env_value = getenv (lit_expr->as_string ().c_str ());
+
+  if (env_value == nullptr)
+    {
+      if (error_expr == nullptr)
+	rust_error_at (invoc_locus, "environment variable %qs not defined",
+		       lit_expr->as_string ().c_str ());
+      else
+	rust_error_at (invoc_locus, "%s", error_expr->as_string ().c_str ());
+      return AST::ASTFragment::create_error ();
+    }
+
+  auto node = AST::SingleASTNode (make_string (invoc_locus, env_value));
+  return AST::ASTFragment ({node});
+}
+
+AST::ASTFragment
+MacroBuiltin::cfg (Location invoc_locus, AST::MacroInvocData &invoc)
+{
+  // only parse if not already parsed
+  if (!invoc.is_parsed ())
+    {
+      std::unique_ptr<AST::AttrInputMetaItemContainer> converted_input (
+	invoc.get_delim_tok_tree ().parse_to_meta_item ());
+
+      if (converted_input == nullptr)
+	{
+	  rust_debug ("DEBUG: failed to parse macro to meta item");
+	  // TODO: do something now? is this an actual error?
+	}
+      else
+	{
+	  std::vector<std::unique_ptr<AST::MetaItemInner>> meta_items (
+	    std::move (converted_input->get_items ()));
+	  invoc.set_meta_item_output (std::move (meta_items));
+	}
+    }
+
+  /* TODO: assuming that cfg! macros can only have one meta item inner, like cfg
+   * attributes */
+  if (invoc.get_meta_items ().size () != 1)
+    return AST::ASTFragment::create_error ();
+
+  bool result = invoc.get_meta_items ()[0]->check_cfg_predicate (
+    Session::get_instance ());
+  auto literal_exp = AST::SingleASTNode (std::unique_ptr<AST::Expr> (
+    new AST::LiteralExpr (result ? "true" : "false", AST::Literal::BOOL,
+			  PrimitiveCoreType::CORETYPE_BOOL, {}, invoc_locus)));
+
+  return AST::ASTFragment ({literal_exp});
+}
+
+/* Expand builtin macro include!(), which includes a source file at the current
+ scope compile time. */
+
+AST::ASTFragment
+MacroBuiltin::include (Location invoc_locus, AST::MacroInvocData &invoc)
+{
+  /* Get target filename from the macro invocation, which is treated as a path
+     relative to the include!-ing file (currently being compiled).  */
+  auto lit_expr
+    = parse_single_string_literal (invoc.get_delim_tok_tree (), invoc_locus);
+  if (lit_expr == nullptr)
+    return AST::ASTFragment::create_error ();
+
+  std::string filename
+    = source_relative_path (lit_expr->as_string (), invoc_locus);
+  auto target_filename
+    = Rust::Session::get_instance ().include_extra_file (std::move (filename));
+
+  RAIIFile target_file (target_filename);
+  Linemap *linemap = Session::get_instance ().linemap;
+
+  if (!target_file.ok ())
+    {
+      rust_error_at (lit_expr->get_locus (),
+		     "cannot open included file %qs: %m", target_filename);
+      return AST::ASTFragment::create_error ();
+    }
+
+  rust_debug ("Attempting to parse included file %s", target_filename);
+
+  Lexer lex (target_filename, std::move (target_file), linemap);
+  Parser<Lexer> parser (lex);
+
+  auto parsed_items = parser.parse_items ();
+  bool has_error = !parser.get_errors ().empty ();
+
+  for (const auto &error : parser.get_errors ())
+    error.emit_error ();
+
+  if (has_error)
+    {
+      // inform the user that the errors above are from a included file
+      rust_inform (invoc_locus, "included from here");
+      return AST::ASTFragment::create_error ();
+    }
+
+  std::vector<AST::SingleASTNode> nodes{};
+  for (auto &item : parsed_items)
+    {
+      AST::SingleASTNode node (std::move (item));
+      nodes.push_back (node);
+    }
+
+  return AST::ASTFragment (nodes);
+}
+
+AST::ASTFragment
+MacroBuiltin::line (Location invoc_locus, AST::MacroInvocData &invoc)
+{
+  auto current_line
+    = Session::get_instance ().linemap->location_to_line (invoc_locus);
+
+  auto line_no = AST::SingleASTNode (std::unique_ptr<AST::Expr> (
+    new AST::LiteralExpr (std::to_string (current_line), AST::Literal::INT,
+			  PrimitiveCoreType::CORETYPE_U32, {}, invoc_locus)));
+
+  return AST::ASTFragment ({line_no});
+}
+
+} // namespace Rust
diff --git a/gcc/rust/expand/rust-macro-builtins.h b/gcc/rust/expand/rust-macro-builtins.h
new file mode 100644
index 00000000000..91f3727d450
--- /dev/null
+++ b/gcc/rust/expand/rust-macro-builtins.h
@@ -0,0 +1,107 @@
+// 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_MACRO_BUILTINS_H
+#define RUST_MACRO_BUILTINS_H
+
+#include "rust-ast.h"
+#include "rust-location.h"
+
+/**
+ * This class provides a list of builtin macros implemented by the compiler.
+ * The functions defined are called "builtin transcribers" in that they replace
+ * the transcribing part of a macro definition.
+ *
+ * Like regular macro transcribers, they are responsible for building and
+ * returning an AST fragment: basically a vector of AST nodes put together.
+ *
+ * Unlike regular declarative macros where each match arm has its own associated
+ * transcriber, builtin transcribers are responsible for handling all match arms
+ * of the macro. This means that you should take extra care when implementing a
+ * builtin containing multiple match arms: You will probably need to do some
+ * lookahead in order to determine which match arm the user intended to use.
+ *
+ * An example of this is the `assert!()` macro:
+ *
+ * ```
+ *  macro_rules! assert {
+ *	($cond:expr $(,)?) => {{ ... }};
+ *	($cond : expr, $ ($arg : tt) +) = > {{ ... }};
+ * }
+ * ```
+ *
+ * If more tokens exist beyond the optional comma, they need to be handled as
+ * a token-tree for a custom panic message.
+ *
+ * These builtin macros with empty transcribers are defined in the standard
+ * library. They are marked with a special attribute, `#[rustc_builtin_macro]`.
+ * When this attribute is present on a macro definition, the compiler should
+ * look for an associated transcriber in the mappings. Meaning that you must
+ * remember to insert your transcriber in the `builtin_macros` map of the
+ *`Mappings`.
+ *
+ * This map is built as a static variable in the `insert_macro_def()` method
+ * of the `Mappings` class.
+ */
+
+/* If assert is defined as a macro this file will not parse, so undefine this
+   before continuing.  */
+#ifdef assert
+#undef assert
+#endif
+
+namespace Rust {
+class MacroBuiltin
+{
+public:
+  static AST::ASTFragment assert (Location invoc_locus,
+				  AST::MacroInvocData &invoc);
+
+  static AST::ASTFragment file (Location invoc_locus,
+				AST::MacroInvocData &invoc);
+
+  static AST::ASTFragment column (Location invoc_locus,
+				  AST::MacroInvocData &invoc);
+
+  static AST::ASTFragment include_bytes (Location invoc_locus,
+					 AST::MacroInvocData &invoc);
+
+  static AST::ASTFragment include_str (Location invoc_locus,
+				       AST::MacroInvocData &invoc);
+
+  static AST::ASTFragment compile_error (Location invoc_locus,
+					 AST::MacroInvocData &invoc);
+
+  static AST::ASTFragment concat (Location invoc_locus,
+				  AST::MacroInvocData &invoc);
+
+  static AST::ASTFragment env (Location invoc_locus,
+			       AST::MacroInvocData &invoc);
+
+  static AST::ASTFragment cfg (Location invoc_locus,
+			       AST::MacroInvocData &invoc);
+
+  static AST::ASTFragment include (Location invoc_locus,
+				   AST::MacroInvocData &invoc);
+
+  static AST::ASTFragment line (Location invoc_locus,
+				AST::MacroInvocData &invoc);
+};
+} // namespace Rust
+
+#endif // RUST_MACRO_BUILTINS_H
diff --git a/gcc/rust/expand/rust-macro-expand.cc b/gcc/rust/expand/rust-macro-expand.cc
new file mode 100644
index 00000000000..1d57e394220
--- /dev/null
+++ b/gcc/rust/expand/rust-macro-expand.cc
@@ -0,0 +1,1012 @@
+// 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/>.
+
+#include "rust-macro-expand.h"
+#include "rust-macro-substitute-ctx.h"
+#include "rust-ast-full.h"
+#include "rust-ast-visitor.h"
+#include "rust-diagnostics.h"
+#include "rust-parse.h"
+#include "rust-attribute-visitor.h"
+
+namespace Rust {
+AST::ASTFragment
+MacroExpander::expand_decl_macro (Location invoc_locus,
+				  AST::MacroInvocData &invoc,
+				  AST::MacroRulesDefinition &rules_def,
+				  bool semicolon)
+{
+  // ensure that both invocation and rules are in a valid state
+  rust_assert (!invoc.is_marked_for_strip ());
+  rust_assert (!rules_def.is_marked_for_strip ());
+  rust_assert (rules_def.get_macro_rules ().size () > 0);
+
+  /* probably something here about parsing invoc and rules def token trees to
+   * token stream. if not, how would parser handle the captures of exprs and
+   * stuff? on the other hand, token trees may be kind of useful in rules def as
+   * creating a point where recursion can occur (like having
+   * "compare_macro_match" and then it calling itself when it finds delimiters)
+   */
+
+  /* find matching rule to invoc token tree, based on macro rule's matcher. if
+   * none exist, error.
+   * - specifically, check each matcher in order. if one fails to match, move
+   * onto next. */
+  /* TODO: does doing this require parsing expressions and whatever in the
+   * invoc? if so, might as well save the results if referenced using $ or
+   * whatever. If not, do another pass saving them. Except this is probably
+   * useless as different rules could have different starting points for exprs
+   * or whatever. Decision trees could avoid this, but they have their own
+   * issues. */
+  /* TODO: will need to modify the parser so that it can essentially "catch"
+   * errors - maybe "try_parse_expr" or whatever methods. */
+  // this technically creates a back-tracking parser - this will be the
+  // implementation style
+
+  /* then, after results are saved, generate the macro output from the
+   * transcriber token tree. if i understand this correctly, the macro
+   * invocation gets replaced by the transcriber tokens, except with
+   * substitutions made (e.g. for $i variables) */
+
+  /* TODO: it is probably better to modify AST::Token to store a pointer to a
+   * Lexer::Token (rather than being converted) - i.e. not so much have
+   * AST::Token as a Token but rather a TokenContainer (as it is another type of
+   * TokenTree). This will prevent re-conversion of Tokens between each type
+   * all the time, while still allowing the heterogenous storage of token trees.
+   */
+
+  AST::DelimTokenTree &invoc_token_tree = invoc.get_delim_tok_tree ();
+
+  // find matching arm
+  AST::MacroRule *matched_rule = nullptr;
+  std::map<std::string, MatchedFragmentContainer> matched_fragments;
+  for (auto &rule : rules_def.get_rules ())
+    {
+      sub_stack.push ();
+      bool did_match_rule = try_match_rule (rule, invoc_token_tree);
+      matched_fragments = sub_stack.pop ();
+
+      if (did_match_rule)
+	{
+	  //  // Debugging
+	  //  for (auto &kv : matched_fragments)
+	  //    rust_debug ("[fragment]: %s (%ld - %s)", kv.first.c_str (),
+	  //		kv.second.get_fragments ().size (),
+	  //		kv.second.get_kind ()
+	  //		    == MatchedFragmentContainer::Kind::Repetition
+	  //		  ? "repetition"
+	  //		  : "metavar");
+
+	  matched_rule = &rule;
+	  break;
+	}
+    }
+
+  if (matched_rule == nullptr)
+    {
+      RichLocation r (invoc_locus);
+      r.add_range (rules_def.get_locus ());
+      rust_error_at (r, "Failed to match any rule within macro");
+      return AST::ASTFragment::create_error ();
+    }
+
+  return transcribe_rule (*matched_rule, invoc_token_tree, matched_fragments,
+			  semicolon, peek_context ());
+}
+
+void
+MacroExpander::expand_invoc (AST::MacroInvocation &invoc, bool has_semicolon)
+{
+  if (depth_exceeds_recursion_limit ())
+    {
+      rust_error_at (invoc.get_locus (), "reached recursion limit");
+      return;
+    }
+
+  AST::MacroInvocData &invoc_data = invoc.get_invoc_data ();
+
+  // ??
+  // switch on type of macro:
+  //  - '!' syntax macro (inner switch)
+  //      - procedural macro - "A token-based function-like macro"
+  //      - 'macro_rules' (by example/pattern-match) macro? or not? "an
+  // AST-based function-like macro"
+  //      - else is unreachable
+  //  - attribute syntax macro (inner switch)
+  //  - procedural macro attribute syntax - "A token-based attribute
+  // macro"
+  //      - legacy macro attribute syntax? - "an AST-based attribute macro"
+  //      - non-macro attribute: mark known
+  //      - else is unreachable
+  //  - derive macro (inner switch)
+  //      - derive or legacy derive - "token-based" vs "AST-based"
+  //      - else is unreachable
+  //  - derive container macro - unreachable
+
+  // lookup the rules for this macro
+  NodeId resolved_node = UNKNOWN_NODEID;
+  NodeId source_node = UNKNOWN_NODEID;
+  if (has_semicolon)
+    source_node = invoc.get_macro_node_id ();
+  else
+    source_node = invoc.get_pattern_node_id ();
+  auto seg
+    = Resolver::CanonicalPath::new_seg (source_node,
+					invoc_data.get_path ().as_string ());
+
+  bool found = resolver->get_macro_scope ().lookup (seg, &resolved_node);
+  if (!found)
+    {
+      rust_error_at (invoc.get_locus (), "unknown macro: [%s]",
+		     seg.get ().c_str ());
+      return;
+    }
+
+  // lookup the rules
+  AST::MacroRulesDefinition *rules_def = nullptr;
+  bool ok = mappings->lookup_macro_def (resolved_node, &rules_def);
+  rust_assert (ok);
+
+  auto fragment = AST::ASTFragment::create_error ();
+
+  if (rules_def->is_builtin ())
+    fragment
+      = rules_def->get_builtin_transcriber () (invoc.get_locus (), invoc_data);
+  else
+    fragment = expand_decl_macro (invoc.get_locus (), invoc_data, *rules_def,
+				  has_semicolon);
+
+  set_expanded_fragment (std::move (fragment));
+}
+
+/* Determines whether any cfg predicate is false and hence item with attributes
+ * should be stripped. Note that attributes must be expanded before calling. */
+bool
+MacroExpander::fails_cfg (const AST::AttrVec &attrs) const
+{
+  for (const auto &attr : attrs)
+    {
+      if (attr.get_path () == "cfg" && !attr.check_cfg_predicate (session))
+	return true;
+    }
+  return false;
+}
+
+/* Determines whether any cfg predicate is false and hence item with attributes
+ * should be stripped. Will expand attributes as well. */
+bool
+MacroExpander::fails_cfg_with_expand (AST::AttrVec &attrs) const
+{
+  // TODO: maybe have something that strips cfg attributes that evaluate true?
+  for (auto &attr : attrs)
+    {
+      if (attr.get_path () == "cfg")
+	{
+	  if (!attr.is_parsed_to_meta_item ())
+	    attr.parse_attr_to_meta_item ();
+
+	  // DEBUG
+	  if (!attr.is_parsed_to_meta_item ())
+	    rust_debug ("failed to parse attr to meta item, right before "
+			"cfg predicate check");
+	  else
+	    rust_debug ("attr has been successfully parsed to meta item, "
+			"right before cfg predicate check");
+
+	  if (!attr.check_cfg_predicate (session))
+	    {
+	      // DEBUG
+	      rust_debug (
+		"cfg predicate failed for attribute: \033[0;31m'%s'\033[0m",
+		attr.as_string ().c_str ());
+
+	      return true;
+	    }
+	  else
+	    {
+	      // DEBUG
+	      rust_debug ("cfg predicate succeeded for attribute: "
+			  "\033[0;31m'%s'\033[0m",
+			  attr.as_string ().c_str ());
+	    }
+	}
+    }
+  return false;
+}
+
+// Expands cfg_attr attributes.
+void
+MacroExpander::expand_cfg_attrs (AST::AttrVec &attrs)
+{
+  for (std::size_t i = 0; i < attrs.size (); i++)
+    {
+      auto &attr = attrs[i];
+      if (attr.get_path () == "cfg_attr")
+	{
+	  if (!attr.is_parsed_to_meta_item ())
+	    attr.parse_attr_to_meta_item ();
+
+	  if (attr.check_cfg_predicate (session))
+	    {
+	      // split off cfg_attr
+	      AST::AttrVec new_attrs = attr.separate_cfg_attrs ();
+
+	      // remove attr from vector
+	      attrs.erase (attrs.begin () + i);
+
+	      // add new attrs to vector
+	      attrs.insert (attrs.begin () + i,
+			    std::make_move_iterator (new_attrs.begin ()),
+			    std::make_move_iterator (new_attrs.end ()));
+	    }
+
+	  /* do something - if feature (first token in tree) is in fact enabled,
+	   * make tokens listed afterwards into attributes. i.e.: for
+	   * [cfg_attr(feature = "wow", wow1, wow2)], if "wow" is true, then add
+	   * attributes [wow1] and [wow2] to attribute list. This can also be
+	   * recursive, so check for expanded attributes being recursive and
+	   * possibly recursively call the expand_attrs? */
+	}
+      else
+	{
+	  i++;
+	}
+    }
+  attrs.shrink_to_fit ();
+}
+
+void
+MacroExpander::expand_crate ()
+{
+  NodeId scope_node_id = crate.get_node_id ();
+  resolver->get_macro_scope ().push (scope_node_id);
+
+  /* fill macro/decorator map from init list? not sure where init list comes
+   * from? */
+
+  // TODO: does cfg apply for inner attributes? research.
+  // the apparent answer (from playground test) is yes
+
+  // expand crate cfg_attr attributes
+  expand_cfg_attrs (crate.inner_attrs);
+
+  if (fails_cfg_with_expand (crate.inner_attrs))
+    {
+      // basically, delete whole crate
+      crate.strip_crate ();
+      // TODO: maybe create warning here? probably not desired behaviour
+    }
+  // expand module attributes?
+
+  push_context (ITEM);
+
+  // expand attributes recursively and strip items if required
+  AttrVisitor attr_visitor (*this);
+  auto &items = crate.items;
+  for (auto it = items.begin (); it != items.end ();)
+    {
+      auto &item = *it;
+
+      // mark for stripping if required
+      item->accept_vis (attr_visitor);
+
+      auto fragment = take_expanded_fragment (attr_visitor);
+      if (fragment.should_expand ())
+	{
+	  // Remove the current expanded invocation
+	  it = items.erase (it);
+	  for (auto &node : fragment.get_nodes ())
+	    {
+	      it = items.insert (it, node.take_item ());
+	      it++;
+	    }
+	}
+      else if (item->is_marked_for_strip ())
+	it = items.erase (it);
+      else
+	it++;
+    }
+
+  pop_context ();
+
+  // TODO: should recursive attribute and macro expansion be done in the same
+  // transversal? Or in separate ones like currently?
+
+  // expand module tree recursively
+
+  // post-process
+
+  // extract exported macros?
+}
+
+bool
+MacroExpander::depth_exceeds_recursion_limit () const
+{
+  return expansion_depth >= cfg.recursion_limit;
+}
+
+bool
+MacroExpander::try_match_rule (AST::MacroRule &match_rule,
+			       AST::DelimTokenTree &invoc_token_tree)
+{
+  MacroInvocLexer lex (invoc_token_tree.to_token_stream ());
+  Parser<MacroInvocLexer> parser (lex);
+
+  AST::MacroMatcher &matcher = match_rule.get_matcher ();
+
+  expansion_depth++;
+  if (!match_matcher (parser, matcher))
+    {
+      expansion_depth--;
+      return false;
+    }
+  expansion_depth--;
+
+  bool used_all_input_tokens = parser.skip_token (END_OF_FILE);
+  return used_all_input_tokens;
+}
+
+bool
+MacroExpander::match_fragment (Parser<MacroInvocLexer> &parser,
+			       AST::MacroMatchFragment &fragment)
+{
+  switch (fragment.get_frag_spec ().get_kind ())
+    {
+    case AST::MacroFragSpec::EXPR:
+      parser.parse_expr ();
+      break;
+
+    case AST::MacroFragSpec::BLOCK:
+      parser.parse_block_expr ();
+      break;
+
+    case AST::MacroFragSpec::IDENT:
+      parser.parse_identifier_pattern ();
+      break;
+
+    case AST::MacroFragSpec::LITERAL:
+      parser.parse_literal_expr ();
+      break;
+
+    case AST::MacroFragSpec::ITEM:
+      parser.parse_item (false);
+      break;
+
+    case AST::MacroFragSpec::TY:
+      parser.parse_type ();
+      break;
+
+    case AST::MacroFragSpec::PAT:
+      parser.parse_pattern ();
+      break;
+
+    case AST::MacroFragSpec::PATH:
+      parser.parse_path_in_expression ();
+      break;
+
+    case AST::MacroFragSpec::VIS:
+      parser.parse_visibility ();
+      break;
+
+      case AST::MacroFragSpec::STMT: {
+	auto restrictions = ParseRestrictions ();
+	restrictions.consume_semi = false;
+	parser.parse_stmt (restrictions);
+	break;
+      }
+
+    case AST::MacroFragSpec::LIFETIME:
+      parser.parse_lifetime_params ();
+      break;
+
+      // is meta attributes?
+    case AST::MacroFragSpec::META:
+      parser.parse_attribute_body ();
+      break;
+
+    case AST::MacroFragSpec::TT:
+      parser.parse_token_tree ();
+      break;
+
+      // i guess we just ignore invalid and just error out
+    case AST::MacroFragSpec::INVALID:
+      return false;
+    }
+
+  // it matches if the parser did not produce errors trying to parse that type
+  // of item
+  return !parser.has_errors ();
+}
+
+bool
+MacroExpander::match_matcher (Parser<MacroInvocLexer> &parser,
+			      AST::MacroMatcher &matcher)
+{
+  if (depth_exceeds_recursion_limit ())
+    {
+      rust_error_at (matcher.get_match_locus (), "reached recursion limit");
+      return false;
+    }
+
+  auto delimiter = parser.peek_current_token ();
+
+  // this is used so we can check that we delimit the stream correctly.
+  switch (delimiter->get_id ())
+    {
+      case LEFT_PAREN: {
+	if (!parser.skip_token (LEFT_PAREN))
+	  return false;
+      }
+      break;
+
+      case LEFT_SQUARE: {
+	if (!parser.skip_token (LEFT_SQUARE))
+	  return false;
+      }
+      break;
+
+      case LEFT_CURLY: {
+	if (!parser.skip_token (LEFT_CURLY))
+	  return false;
+      }
+      break;
+    default:
+      gcc_unreachable ();
+    }
+
+  const MacroInvocLexer &source = parser.get_token_source ();
+
+  for (auto &match : matcher.get_matches ())
+    {
+      size_t offs_begin = source.get_offs ();
+
+      switch (match->get_macro_match_type ())
+	{
+	  case AST::MacroMatch::MacroMatchType::Fragment: {
+	    AST::MacroMatchFragment *fragment
+	      = static_cast<AST::MacroMatchFragment *> (match.get ());
+	    if (!match_fragment (parser, *fragment))
+	      return false;
+
+	    // matched fragment get the offset in the token stream
+	    size_t offs_end = source.get_offs ();
+	    sub_stack.insert_metavar (
+	      MatchedFragment (fragment->get_ident (), offs_begin, offs_end));
+	  }
+	  break;
+
+	  case AST::MacroMatch::MacroMatchType::Tok: {
+	    AST::Token *tok = static_cast<AST::Token *> (match.get ());
+	    if (!match_token (parser, *tok))
+	      return false;
+	  }
+	  break;
+
+	  case AST::MacroMatch::MacroMatchType::Repetition: {
+	    AST::MacroMatchRepetition *rep
+	      = static_cast<AST::MacroMatchRepetition *> (match.get ());
+	    if (!match_repetition (parser, *rep))
+	      return false;
+	  }
+	  break;
+
+	  case AST::MacroMatch::MacroMatchType::Matcher: {
+	    AST::MacroMatcher *m
+	      = static_cast<AST::MacroMatcher *> (match.get ());
+	    expansion_depth++;
+	    if (!match_matcher (parser, *m))
+	      {
+		expansion_depth--;
+		return false;
+	      }
+	    expansion_depth--;
+	  }
+	  break;
+	}
+    }
+
+  switch (delimiter->get_id ())
+    {
+      case LEFT_PAREN: {
+	if (!parser.skip_token (RIGHT_PAREN))
+	  return false;
+      }
+      break;
+
+      case LEFT_SQUARE: {
+	if (!parser.skip_token (RIGHT_SQUARE))
+	  return false;
+      }
+      break;
+
+      case LEFT_CURLY: {
+	if (!parser.skip_token (RIGHT_CURLY))
+	  return false;
+      }
+      break;
+    default:
+      gcc_unreachable ();
+    }
+
+  return true;
+}
+
+bool
+MacroExpander::match_token (Parser<MacroInvocLexer> &parser, AST::Token &token)
+{
+  // FIXME this needs to actually match the content and the type
+  return parser.skip_token (token.get_id ());
+}
+
+bool
+MacroExpander::match_n_matches (Parser<MacroInvocLexer> &parser,
+				AST::MacroMatchRepetition &rep,
+				size_t &match_amount, size_t lo_bound,
+				size_t hi_bound)
+{
+  match_amount = 0;
+  auto &matches = rep.get_matches ();
+
+  const MacroInvocLexer &source = parser.get_token_source ();
+  while (true)
+    {
+      // If the current token is a closing macro delimiter, break away.
+      // TODO: Is this correct?
+      auto t_id = parser.peek_current_token ()->get_id ();
+      if (t_id == RIGHT_PAREN || t_id == RIGHT_SQUARE || t_id == RIGHT_CURLY)
+	break;
+
+      // Skip parsing a separator on the first match, otherwise consume it.
+      // If it isn't present, this is an error
+      if (rep.has_sep () && match_amount > 0)
+	if (!match_token (parser, *rep.get_sep ()))
+	  break;
+
+      bool valid_current_match = false;
+      for (auto &match : matches)
+	{
+	  size_t offs_begin = source.get_offs ();
+	  switch (match->get_macro_match_type ())
+	    {
+	      case AST::MacroMatch::MacroMatchType::Fragment: {
+		AST::MacroMatchFragment *fragment
+		  = static_cast<AST::MacroMatchFragment *> (match.get ());
+		valid_current_match = match_fragment (parser, *fragment);
+
+		// matched fragment get the offset in the token stream
+		size_t offs_end = source.get_offs ();
+
+		// The main difference with match_matcher happens here: Instead
+		// of inserting a new fragment, we append to one. If that
+		// fragment does not exist, then the operation is similar to
+		// `insert_fragment` with the difference that we are not
+		// creating a metavariable, but a repetition of one, which is
+		// really different.
+		sub_stack.append_fragment (
+		  MatchedFragment (fragment->get_ident (), offs_begin,
+				   offs_end));
+	      }
+	      break;
+
+	      case AST::MacroMatch::MacroMatchType::Tok: {
+		AST::Token *tok = static_cast<AST::Token *> (match.get ());
+		valid_current_match = match_token (parser, *tok);
+	      }
+	      break;
+
+	      case AST::MacroMatch::MacroMatchType::Repetition: {
+		AST::MacroMatchRepetition *rep
+		  = static_cast<AST::MacroMatchRepetition *> (match.get ());
+		valid_current_match = match_repetition (parser, *rep);
+	      }
+	      break;
+
+	      case AST::MacroMatch::MacroMatchType::Matcher: {
+		AST::MacroMatcher *m
+		  = static_cast<AST::MacroMatcher *> (match.get ());
+		valid_current_match = match_matcher (parser, *m);
+	      }
+	      break;
+	    }
+	}
+      // If we've encountered an error once, stop trying to match more
+      // repetitions
+      if (!valid_current_match)
+	break;
+
+      match_amount++;
+
+      // Break early if we notice there's too many expressions already
+      if (hi_bound && match_amount > hi_bound)
+	break;
+    }
+
+  // Check if the amount of matches we got is valid: Is it more than the lower
+  // bound and less than the higher bound?
+  bool did_meet_lo_bound = match_amount >= lo_bound;
+  bool did_meet_hi_bound = hi_bound ? match_amount <= hi_bound : true;
+
+  // If the end-result is valid, then we can clear the parse errors: Since
+  // repetitions are parsed eagerly, it is okay to fail in some cases
+  auto res = did_meet_lo_bound && did_meet_hi_bound;
+  if (res)
+    parser.clear_errors ();
+
+  return res;
+}
+
+bool
+MacroExpander::match_repetition (Parser<MacroInvocLexer> &parser,
+				 AST::MacroMatchRepetition &rep)
+{
+  size_t match_amount = 0;
+  bool res = false;
+
+  std::string lo_str;
+  std::string hi_str;
+  switch (rep.get_op ())
+    {
+    case AST::MacroMatchRepetition::MacroRepOp::ANY:
+      lo_str = "0";
+      hi_str = "+inf";
+      res = match_n_matches (parser, rep, match_amount);
+      break;
+    case AST::MacroMatchRepetition::MacroRepOp::ONE_OR_MORE:
+      lo_str = "1";
+      hi_str = "+inf";
+      res = match_n_matches (parser, rep, match_amount, 1);
+      break;
+    case AST::MacroMatchRepetition::MacroRepOp::ZERO_OR_ONE:
+      lo_str = "0";
+      hi_str = "1";
+      res = match_n_matches (parser, rep, match_amount, 0, 1);
+      break;
+    default:
+      gcc_unreachable ();
+    }
+
+  if (!res)
+    rust_error_at (rep.get_match_locus (),
+		   "invalid amount of matches for macro invocation. Expected "
+		   "between %s and %s, got %lu",
+		   lo_str.c_str (), hi_str.c_str (),
+		   (unsigned long) match_amount);
+
+  rust_debug_loc (rep.get_match_locus (), "%s matched %lu times",
+		  res ? "successfully" : "unsuccessfully",
+		  (unsigned long) match_amount);
+
+  // We have to handle zero fragments differently: They will not have been
+  // "matched" but they are still valid and should be inserted as a special
+  // case. So we go through the stack map, and for every fragment which doesn't
+  // exist, insert a zero-matched fragment.
+  auto &stack_map = sub_stack.peek ();
+  for (auto &match : rep.get_matches ())
+    {
+      if (match->get_macro_match_type ()
+	  == AST::MacroMatch::MacroMatchType::Fragment)
+	{
+	  auto fragment = static_cast<AST::MacroMatchFragment *> (match.get ());
+	  auto it = stack_map.find (fragment->get_ident ());
+
+	  if (it == stack_map.end ())
+	    sub_stack.insert_matches (fragment->get_ident (),
+				      MatchedFragmentContainer::zero ());
+	}
+    }
+
+  return res;
+}
+
+/**
+ * Helper function to refactor calling a parsing function 0 or more times
+ */
+static AST::ASTFragment
+parse_many (Parser<MacroInvocLexer> &parser, TokenId &delimiter,
+	    std::function<AST::SingleASTNode ()> parse_fn)
+{
+  std::vector<AST::SingleASTNode> nodes;
+  while (true)
+    {
+      if (parser.peek_current_token ()->get_id () == delimiter)
+	break;
+
+      auto node = parse_fn ();
+      nodes.emplace_back (std::move (node));
+    }
+
+  return AST::ASTFragment (std::move (nodes));
+}
+
+/**
+ * Transcribe 0 or more items from a macro invocation
+ *
+ * @param parser Parser to extract items from
+ * @param delimiter Id of the token on which parsing should stop
+ */
+static AST::ASTFragment
+transcribe_many_items (Parser<MacroInvocLexer> &parser, TokenId &delimiter)
+{
+  return parse_many (parser, delimiter, [&parser] () {
+    auto item = parser.parse_item (true);
+    return AST::SingleASTNode (std::move (item));
+  });
+}
+
+/**
+ * Transcribe 0 or more external items from a macro invocation
+ *
+ * @param parser Parser to extract items from
+ * @param delimiter Id of the token on which parsing should stop
+ */
+static AST::ASTFragment
+transcribe_many_ext (Parser<MacroInvocLexer> &parser, TokenId &delimiter)
+{
+  return parse_many (parser, delimiter, [&parser] () {
+    auto item = parser.parse_external_item ();
+    return AST::SingleASTNode (std::move (item));
+  });
+}
+
+/**
+ * Transcribe 0 or more trait items from a macro invocation
+ *
+ * @param parser Parser to extract items from
+ * @param delimiter Id of the token on which parsing should stop
+ */
+static AST::ASTFragment
+transcribe_many_trait_items (Parser<MacroInvocLexer> &parser,
+			     TokenId &delimiter)
+{
+  return parse_many (parser, delimiter, [&parser] () {
+    auto item = parser.parse_trait_item ();
+    return AST::SingleASTNode (std::move (item));
+  });
+}
+
+/**
+ * Transcribe 0 or more impl items from a macro invocation
+ *
+ * @param parser Parser to extract items from
+ * @param delimiter Id of the token on which parsing should stop
+ */
+static AST::ASTFragment
+transcribe_many_impl_items (Parser<MacroInvocLexer> &parser, TokenId &delimiter)
+{
+  return parse_many (parser, delimiter, [&parser] () {
+    auto item = parser.parse_inherent_impl_item ();
+    return AST::SingleASTNode (std::move (item));
+  });
+}
+
+/**
+ * Transcribe 0 or more trait impl items from a macro invocation
+ *
+ * @param parser Parser to extract items from
+ * @param delimiter Id of the token on which parsing should stop
+ */
+static AST::ASTFragment
+transcribe_many_trait_impl_items (Parser<MacroInvocLexer> &parser,
+				  TokenId &delimiter)
+{
+  return parse_many (parser, delimiter, [&parser] () {
+    auto item = parser.parse_trait_impl_item ();
+    return AST::SingleASTNode (std::move (item));
+  });
+}
+
+/**
+ * Transcribe 0 or more statements from a macro invocation
+ *
+ * @param parser Parser to extract statements from
+ * @param delimiter Id of the token on which parsing should stop
+ */
+static AST::ASTFragment
+transcribe_many_stmts (Parser<MacroInvocLexer> &parser, TokenId &delimiter)
+{
+  auto restrictions = ParseRestrictions ();
+  restrictions.consume_semi = false;
+
+  // FIXME: This is invalid! It needs to also handle cases where the macro
+  // transcriber is an expression, but since the macro call is followed by
+  // a semicolon, it's a valid ExprStmt
+  return parse_many (parser, delimiter, [&parser, restrictions] () {
+    auto stmt = parser.parse_stmt (restrictions);
+    return AST::SingleASTNode (std::move (stmt));
+  });
+}
+
+/**
+ * Transcribe one expression from a macro invocation
+ *
+ * @param parser Parser to extract statements from
+ */
+static AST::ASTFragment
+transcribe_expression (Parser<MacroInvocLexer> &parser)
+{
+  auto expr = parser.parse_expr ();
+
+  return AST::ASTFragment ({std::move (expr)});
+}
+
+/**
+ * Transcribe one type from a macro invocation
+ *
+ * @param parser Parser to extract statements from
+ */
+static AST::ASTFragment
+transcribe_type (Parser<MacroInvocLexer> &parser)
+{
+  auto type = parser.parse_type ();
+
+  return AST::ASTFragment ({std::move (type)});
+}
+
+static AST::ASTFragment
+transcribe_on_delimiter (Parser<MacroInvocLexer> &parser, bool semicolon,
+			 AST::DelimType delimiter, TokenId last_token_id)
+{
+  if (semicolon || delimiter == AST::DelimType::CURLY)
+    return transcribe_many_stmts (parser, last_token_id);
+  else
+    return transcribe_expression (parser);
+} // namespace Rust
+
+static AST::ASTFragment
+transcribe_context (MacroExpander::ContextType ctx,
+		    Parser<MacroInvocLexer> &parser, bool semicolon,
+		    AST::DelimType delimiter, TokenId last_token_id)
+{
+  // The flow-chart in order to choose a parsing function is as follows:
+  //
+  // [switch special context]
+  //     -- Item --> parser.parse_item();
+  //     -- Trait --> parser.parse_trait_item();
+  //     -- Impl --> parser.parse_impl_item();
+  //     -- Extern --> parser.parse_extern_item();
+  //     -- None --> [has semicolon?]
+  //                 -- Yes --> parser.parse_stmt();
+  //                 -- No --> [switch invocation.delimiter()]
+  //                             -- { } --> parser.parse_stmt();
+  //                             -- _ --> parser.parse_expr(); // once!
+
+  // If there is a semicolon OR we are expanding a MacroInvocationSemi, then
+  // we can parse multiple items. Otherwise, parse *one* expression
+
+  switch (ctx)
+    {
+    case MacroExpander::ContextType::ITEM:
+      return transcribe_many_items (parser, last_token_id);
+      break;
+    case MacroExpander::ContextType::TRAIT:
+      return transcribe_many_trait_items (parser, last_token_id);
+      break;
+    case MacroExpander::ContextType::IMPL:
+      return transcribe_many_impl_items (parser, last_token_id);
+      break;
+    case MacroExpander::ContextType::TRAIT_IMPL:
+      return transcribe_many_trait_impl_items (parser, last_token_id);
+      break;
+    case MacroExpander::ContextType::EXTERN:
+      return transcribe_many_ext (parser, last_token_id);
+      break;
+    case MacroExpander::ContextType::TYPE:
+      return transcribe_type (parser);
+      break;
+    default:
+      return transcribe_on_delimiter (parser, semicolon, delimiter,
+				      last_token_id);
+    }
+}
+
+static std::string
+tokens_to_str (std::vector<std::unique_ptr<AST::Token>> &tokens)
+{
+  std::string str;
+  if (!tokens.empty ())
+    {
+      str += tokens[0]->as_string ();
+      for (size_t i = 1; i < tokens.size (); i++)
+	str += " " + tokens[i]->as_string ();
+    }
+
+  return str;
+}
+
+AST::ASTFragment
+MacroExpander::transcribe_rule (
+  AST::MacroRule &match_rule, AST::DelimTokenTree &invoc_token_tree,
+  std::map<std::string, MatchedFragmentContainer> &matched_fragments,
+  bool semicolon, ContextType ctx)
+{
+  // we can manipulate the token tree to substitute the dollar identifiers so
+  // that when we call parse its already substituted for us
+  AST::MacroTranscriber &transcriber = match_rule.get_transcriber ();
+  AST::DelimTokenTree &transcribe_tree = transcriber.get_token_tree ();
+
+  auto invoc_stream = invoc_token_tree.to_token_stream ();
+  auto macro_rule_tokens = transcribe_tree.to_token_stream ();
+
+  auto substitute_context
+    = SubstituteCtx (invoc_stream, macro_rule_tokens, matched_fragments);
+  std::vector<std::unique_ptr<AST::Token>> substituted_tokens
+    = substitute_context.substitute_tokens ();
+
+  rust_debug ("substituted tokens: %s",
+	      tokens_to_str (substituted_tokens).c_str ());
+
+  // parse it to an ASTFragment
+  MacroInvocLexer lex (std::move (substituted_tokens));
+  Parser<MacroInvocLexer> parser (lex);
+
+  auto last_token_id = TokenId::RIGHT_CURLY;
+
+  // this is used so we can check that we delimit the stream correctly.
+  switch (transcribe_tree.get_delim_type ())
+    {
+    case AST::DelimType::PARENS:
+      last_token_id = TokenId::RIGHT_PAREN;
+      rust_assert (parser.skip_token (LEFT_PAREN));
+      break;
+
+    case AST::DelimType::CURLY:
+      rust_assert (parser.skip_token (LEFT_CURLY));
+      break;
+
+    case AST::DelimType::SQUARE:
+      last_token_id = TokenId::RIGHT_SQUARE;
+      rust_assert (parser.skip_token (LEFT_SQUARE));
+      break;
+    }
+
+  // see https://github.com/Rust-GCC/gccrs/issues/22
+  // TL;DR:
+  //   - Treat all macro invocations with parentheses, (), or square brackets,
+  //   [], as expressions.
+  //   - If the macro invocation has curly brackets, {}, it may be parsed as a
+  //   statement depending on the context.
+  //   - If the macro invocation has a semicolon at the end, it must be parsed
+  //   as a statement (either via ExpressionStatement or
+  //   MacroInvocationWithSemi)
+
+  auto fragment
+    = transcribe_context (ctx, parser, semicolon,
+			  invoc_token_tree.get_delim_type (), last_token_id);
+
+  // emit any errors
+  if (parser.has_errors ())
+    {
+      for (auto &err : parser.get_errors ())
+	rust_error_at (err.locus, "%s", err.message.c_str ());
+      return AST::ASTFragment::create_error ();
+    }
+
+  // are all the tokens used?
+  bool did_delimit = parser.skip_token (last_token_id);
+
+  bool reached_end_of_stream = did_delimit && parser.skip_token (END_OF_FILE);
+  if (!reached_end_of_stream)
+    {
+      const_TokenPtr current_token = parser.peek_current_token ();
+      rust_error_at (current_token->get_locus (),
+		     "tokens here and after are unparsed");
+    }
+
+  return fragment;
+}
+} // namespace Rust
diff --git a/gcc/rust/expand/rust-macro-expand.h b/gcc/rust/expand/rust-macro-expand.h
new file mode 100644
index 00000000000..94d6702ecb8
--- /dev/null
+++ b/gcc/rust/expand/rust-macro-expand.h
@@ -0,0 +1,366 @@
+// 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_MACRO_EXPAND_H
+#define RUST_MACRO_EXPAND_H
+
+#include "rust-buffered-queue.h"
+#include "rust-parse.h"
+#include "rust-token.h"
+#include "rust-ast.h"
+#include "rust-macro.h"
+#include "rust-hir-map.h"
+#include "rust-name-resolver.h"
+#include "rust-macro-invoc-lexer.h"
+
+// Provides objects and method prototypes for macro expansion
+
+namespace Rust {
+// forward decls for AST
+namespace AST {
+class MacroInvocation;
+}
+
+// Object used to store configuration data for macro expansion.
+// NOTE: Keep all these items complying with the latest rustc.
+struct ExpansionCfg
+{
+  // features?
+  // TODO: Add `features' when we have it.
+  unsigned int recursion_limit = 1024;
+  bool trace_mac = false;   // trace macro
+  bool should_test = false; // strip #[test] nodes if false
+  bool keep_macs = false;   // keep macro definitions
+  std::string crate_name = "";
+};
+
+struct MatchedFragment
+{
+  std::string fragment_ident;
+  size_t token_offset_begin;
+  size_t token_offset_end;
+
+  MatchedFragment (std::string identifier, size_t token_offset_begin,
+		   size_t token_offset_end)
+    : fragment_ident (identifier), token_offset_begin (token_offset_begin),
+      token_offset_end (token_offset_end)
+  {}
+
+  /**
+   * Empty constructor for uninitialized fragments
+   */
+  MatchedFragment () : MatchedFragment ("", 0, 0) {}
+
+  std::string as_string () const
+  {
+    return fragment_ident + "=" + std::to_string (token_offset_begin) + ":"
+	   + std::to_string (token_offset_end);
+  }
+};
+
+class MatchedFragmentContainer
+{
+public:
+  // Does the container refer to a simple metavariable, different from a
+  // repetition repeated once
+  enum class Kind
+  {
+    MetaVar,
+    Repetition,
+  };
+
+  MatchedFragmentContainer (std::vector<MatchedFragment> fragments,
+			    Kind kind = Kind::Repetition)
+    : fragments (fragments), kind (kind)
+  {}
+
+  /**
+   * Create a valid fragment matched zero times. This is useful for repetitions
+   * which allow the absence of a fragment, such as * and ?
+   */
+  static MatchedFragmentContainer zero ()
+  {
+    return MatchedFragmentContainer ({});
+  }
+
+  /**
+   * Create a valid fragment matched one time
+   */
+  static MatchedFragmentContainer metavar (MatchedFragment fragment)
+  {
+    return MatchedFragmentContainer ({fragment}, Kind::MetaVar);
+  }
+
+  /**
+   * Add a matched fragment to the container
+   */
+  void add_fragment (MatchedFragment fragment)
+  {
+    rust_assert (!is_single_fragment ());
+
+    fragments.emplace_back (fragment);
+  }
+
+  size_t get_match_amount () const { return fragments.size (); }
+  const std::vector<MatchedFragment> &get_fragments () const
+  {
+    return fragments;
+  }
+  // const std::string &get_fragment_name () const { return fragment_name; }
+
+  bool is_single_fragment () const
+  {
+    return get_match_amount () == 1 && kind == Kind::MetaVar;
+  }
+
+  const MatchedFragment get_single_fragment () const
+  {
+    rust_assert (is_single_fragment ());
+
+    return fragments[0];
+  }
+
+  const Kind &get_kind () const { return kind; }
+
+private:
+  /**
+   * Fragments matched `match_amount` times. This can be an empty vector
+   * in case having zero matches is allowed (i.e ? or * operators)
+   */
+  std::vector<MatchedFragment> fragments;
+  Kind kind;
+};
+
+class SubstitutionScope
+{
+public:
+  SubstitutionScope () : stack () {}
+
+  void push () { stack.push_back ({}); }
+
+  std::map<std::string, MatchedFragmentContainer> pop ()
+  {
+    auto top = stack.back ();
+    stack.pop_back ();
+    return top;
+  }
+
+  std::map<std::string, MatchedFragmentContainer> &peek ()
+  {
+    return stack.back ();
+  }
+
+  /**
+   * Insert a new matched metavar into the current substitution map
+   */
+  void insert_metavar (MatchedFragment fragment)
+  {
+    auto &current_map = stack.back ();
+    auto it = current_map.find (fragment.fragment_ident);
+
+    if (it == current_map.end ())
+      current_map.insert ({fragment.fragment_ident,
+			   MatchedFragmentContainer::metavar (fragment)});
+    else
+      gcc_unreachable ();
+  }
+
+  /**
+   * Append a new matched fragment to a repetition into the current substitution
+   * map
+   */
+  void append_fragment (MatchedFragment fragment)
+  {
+    auto &current_map = stack.back ();
+    auto it = current_map.find (fragment.fragment_ident);
+
+    if (it == current_map.end ())
+      current_map.insert (
+	{fragment.fragment_ident, MatchedFragmentContainer ({fragment})});
+    else
+      it->second.add_fragment (fragment);
+  }
+
+  void insert_matches (std::string key, MatchedFragmentContainer matches)
+  {
+    auto &current_map = stack.back ();
+    auto it = current_map.find (key);
+    rust_assert (it == current_map.end ());
+
+    current_map.insert ({key, matches});
+  }
+
+private:
+  std::vector<std::map<std::string, MatchedFragmentContainer>> stack;
+};
+
+// Object used to store shared data (between functions) for macro expansion.
+struct MacroExpander
+{
+  enum ContextType
+  {
+    ITEM,
+    BLOCK,
+    EXTERN,
+    TYPE,
+    TRAIT,
+    IMPL,
+    TRAIT_IMPL,
+  };
+
+  ExpansionCfg cfg;
+  unsigned int expansion_depth = 0;
+
+  MacroExpander (AST::Crate &crate, ExpansionCfg cfg, Session &session)
+    : cfg (cfg), crate (crate), session (session),
+      sub_stack (SubstitutionScope ()),
+      expanded_fragment (AST::ASTFragment::create_error ()),
+      resolver (Resolver::Resolver::get ()),
+      mappings (Analysis::Mappings::get ())
+  {}
+
+  ~MacroExpander () = default;
+
+  // Expands all macros in the crate passed in.
+  void expand_crate ();
+
+  /* Expands a macro invocation - possibly make both
+   * have similar duck-typed interface and use templates?*/
+  // should this be public or private?
+  void expand_invoc (AST::MacroInvocation &invoc, bool has_semicolon);
+
+  // Expands a single declarative macro.
+  AST::ASTFragment expand_decl_macro (Location locus,
+				      AST::MacroInvocData &invoc,
+				      AST::MacroRulesDefinition &rules_def,
+				      bool semicolon);
+
+  void expand_cfg_attrs (AST::AttrVec &attrs);
+  bool fails_cfg (const AST::AttrVec &attr) const;
+  bool fails_cfg_with_expand (AST::AttrVec &attrs) const;
+
+  bool depth_exceeds_recursion_limit () const;
+
+  bool try_match_rule (AST::MacroRule &match_rule,
+		       AST::DelimTokenTree &invoc_token_tree);
+
+  AST::ASTFragment transcribe_rule (
+    AST::MacroRule &match_rule, AST::DelimTokenTree &invoc_token_tree,
+    std::map<std::string, MatchedFragmentContainer> &matched_fragments,
+    bool semicolon, ContextType ctx);
+
+  bool match_fragment (Parser<MacroInvocLexer> &parser,
+		       AST::MacroMatchFragment &fragment);
+
+  bool match_token (Parser<MacroInvocLexer> &parser, AST::Token &token);
+
+  bool match_repetition (Parser<MacroInvocLexer> &parser,
+			 AST::MacroMatchRepetition &rep);
+
+  bool match_matcher (Parser<MacroInvocLexer> &parser,
+		      AST::MacroMatcher &matcher);
+
+  /**
+   * Match any amount of matches
+   *
+   * @param parser Parser to use for matching
+   * @param rep Repetition to try and match
+   * @param match_amount Reference in which to store the ammount of succesful
+   * and valid matches
+   *
+   * @param lo_bound Lower bound of the matcher. When specified, the matcher
+   * will only succeed if it parses at *least* `lo_bound` fragments. If
+   * unspecified, the matcher could succeed when parsing 0 fragments.
+   *
+   * @param hi_bound Higher bound of the matcher. When specified, the matcher
+   * will only succeed if it parses *less than* `hi_bound` fragments. If
+   * unspecified, the matcher could succeed when parsing an infinity of
+   * fragments.
+   *
+   * @return true if matching was successful and within the given limits, false
+   * otherwise
+   */
+  bool match_n_matches (Parser<MacroInvocLexer> &parser,
+			AST::MacroMatchRepetition &rep, size_t &match_amount,
+			size_t lo_bound = 0, size_t hi_bound = 0);
+
+  void push_context (ContextType t) { context.push_back (t); }
+
+  ContextType pop_context ()
+  {
+    rust_assert (!context.empty ());
+
+    ContextType t = context.back ();
+    context.pop_back ();
+
+    return t;
+  }
+
+  ContextType peek_context () { return context.back (); }
+
+  void set_expanded_fragment (AST::ASTFragment &&fragment)
+  {
+    expanded_fragment = std::move (fragment);
+  }
+
+  AST::ASTFragment take_expanded_fragment (AST::ASTVisitor &vis)
+  {
+    AST::ASTFragment old_fragment = std::move (expanded_fragment);
+    auto accumulator = std::vector<AST::SingleASTNode> ();
+    expanded_fragment = AST::ASTFragment::create_error ();
+
+    for (auto &node : old_fragment.get_nodes ())
+      {
+	expansion_depth++;
+	node.accept_vis (vis);
+	// we'll decide the next move according to the outcome of the macro
+	// expansion
+	if (expanded_fragment.is_error ())
+	  accumulator.push_back (node); // if expansion fails, there might be a
+					// non-macro expression we need to keep
+	else
+	  {
+	    // if expansion succeeded, then we need to merge the fragment with
+	    // the contents in the accumulator, so that our final expansion
+	    // result will contain non-macro nodes as it should
+	    auto new_nodes = expanded_fragment.get_nodes ();
+	    std::move (new_nodes.begin (), new_nodes.end (),
+		       std::back_inserter (accumulator));
+	    expanded_fragment = AST::ASTFragment (accumulator);
+	  }
+	expansion_depth--;
+      }
+
+    return old_fragment;
+  }
+
+private:
+  AST::Crate &crate;
+  Session &session;
+  SubstitutionScope sub_stack;
+  std::vector<ContextType> context;
+  AST::ASTFragment expanded_fragment;
+
+public:
+  Resolver::Resolver *resolver;
+  Analysis::Mappings *mappings;
+};
+
+} // namespace Rust
+
+#endif
diff --git a/gcc/rust/expand/rust-macro-invoc-lexer.cc b/gcc/rust/expand/rust-macro-invoc-lexer.cc
new file mode 100644
index 00000000000..8a43d29e0d1
--- /dev/null
+++ b/gcc/rust/expand/rust-macro-invoc-lexer.cc
@@ -0,0 +1,29 @@
+#include "rust-macro-invoc-lexer.h"
+
+namespace Rust {
+
+const_TokenPtr
+MacroInvocLexer::peek_token (int n)
+{
+  if ((offs + n) >= token_stream.size ())
+    return Token::make (END_OF_FILE, Location ());
+
+  return token_stream.at (offs + n)->get_tok_ptr ();
+}
+
+// Advances current token to n + 1 tokens ahead of current position.
+void
+MacroInvocLexer::skip_token (int n)
+{
+  offs += (n + 1);
+}
+
+void
+MacroInvocLexer::split_current_token (TokenId new_left __attribute__ ((unused)),
+				      TokenId new_right
+				      __attribute__ ((unused)))
+{
+  // FIXME
+  gcc_unreachable ();
+}
+} // namespace Rust
diff --git a/gcc/rust/expand/rust-macro-invoc-lexer.h b/gcc/rust/expand/rust-macro-invoc-lexer.h
new file mode 100644
index 00000000000..0fd4554d02f
--- /dev/null
+++ b/gcc/rust/expand/rust-macro-invoc-lexer.h
@@ -0,0 +1,64 @@
+// 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_MACRO_INVOC_LEXER_H
+#define RUST_MACRO_INVOC_LEXER_H
+
+#include "rust-ast.h"
+
+namespace Rust {
+class MacroInvocLexer
+{
+public:
+  MacroInvocLexer (std::vector<std::unique_ptr<AST::Token>> stream)
+    : offs (0), token_stream (std::move (stream))
+  {}
+
+  // Returns token n tokens ahead of current position.
+  const_TokenPtr peek_token (int n);
+
+  // Peeks the current token.
+  const_TokenPtr peek_token () { return peek_token (0); }
+
+  // Advances current token to n + 1 tokens ahead of current position.
+  void skip_token (int n);
+
+  // Skips the current token.
+  void skip_token () { skip_token (0); }
+
+  // Splits the current token into two. Intended for use with nested generics
+  // closes (i.e. T<U<X>> where >> is wrongly lexed as one token). Note that
+  // this will only work with "simple" tokens like punctuation.
+  void split_current_token (TokenId new_left, TokenId new_right);
+
+  std::string get_filename () const
+  {
+    // FIXME
+    gcc_unreachable ();
+    return "FIXME";
+  }
+
+  size_t get_offs () const { return offs; }
+
+private:
+  size_t offs;
+  std::vector<std::unique_ptr<AST::Token>> token_stream;
+};
+} // namespace Rust
+
+#endif // RUST_MACRO_INVOC_LEXER_H
diff --git a/gcc/rust/expand/rust-macro-substitute-ctx.cc b/gcc/rust/expand/rust-macro-substitute-ctx.cc
new file mode 100644
index 00000000000..9592d2d2a9e
--- /dev/null
+++ b/gcc/rust/expand/rust-macro-substitute-ctx.cc
@@ -0,0 +1,312 @@
+#include "rust-macro-substitute-ctx.h"
+
+namespace Rust {
+
+std::vector<std::unique_ptr<AST::Token>>
+SubstituteCtx::substitute_metavar (std::unique_ptr<AST::Token> &metavar)
+{
+  auto metavar_name = metavar->get_str ();
+
+  std::vector<std::unique_ptr<AST::Token>> expanded;
+  auto it = fragments.find (metavar_name);
+  if (it == fragments.end ())
+    {
+      // Return a copy of the original token
+      expanded.push_back (metavar->clone_token ());
+    }
+  else
+    {
+      // If we are expanding a metavar which has a lof of matches, we are
+      // currently expanding a repetition metavar - not a simple metavar. We
+      // need to error out and inform the user.
+      // Associated test case for an example: compile/macro-issue1224.rs
+      if (it->second.get_match_amount () != 1)
+	{
+	  rust_error_at (metavar->get_locus (),
+			 "metavariable is still repeating at this depth");
+	  rust_inform (
+	    metavar->get_locus (),
+	    "you probably forgot the repetition operator: %<%s%s%s%>", "$(",
+	    metavar->as_string ().c_str (), ")*");
+	  return expanded;
+	}
+
+      // We only care about the vector when expanding repetitions.
+      // Just access the first element of the vector.
+      auto &frag = it->second.get_single_fragment ();
+      for (size_t offs = frag.token_offset_begin; offs < frag.token_offset_end;
+	   offs++)
+	{
+	  auto &tok = input.at (offs);
+	  expanded.push_back (tok->clone_token ());
+	}
+    }
+
+  return expanded;
+}
+
+bool
+SubstituteCtx::check_repetition_amount (size_t pattern_start,
+					size_t pattern_end,
+					size_t &expected_repetition_amount)
+{
+  bool first_fragment_found = false;
+  bool is_valid = true;
+
+  for (size_t i = pattern_start; i < pattern_end; i++)
+    {
+      if (macro.at (i)->get_id () == DOLLAR_SIGN)
+	{
+	  auto &frag_token = macro.at (i + 1);
+	  if (frag_token->get_id () == IDENTIFIER)
+	    {
+	      auto it = fragments.find (frag_token->get_str ());
+	      if (it == fragments.end ())
+		{
+		  // If the repetition is not anything we know (ie no declared
+		  // metavars, or metavars which aren't present in the
+		  // fragment), we can just error out. No need to paste the
+		  // tokens as if nothing had happened.
+		  rust_error_at (frag_token->get_locus (),
+				 "metavar %s used in repetition does not exist",
+				 frag_token->get_str ().c_str ());
+
+		  is_valid = false;
+		}
+
+	      auto &fragment = it->second;
+
+	      size_t repeat_amount = fragment.get_match_amount ();
+	      if (!first_fragment_found)
+		{
+		  first_fragment_found = true;
+		  expected_repetition_amount = repeat_amount;
+		}
+	      else
+		{
+		  if (repeat_amount != expected_repetition_amount
+		      && !fragment.is_single_fragment ())
+		    {
+		      rust_error_at (
+			frag_token->get_locus (),
+			"different amount of matches used in merged "
+			"repetitions: expected %lu, got %lu",
+			(unsigned long) expected_repetition_amount,
+			(unsigned long) repeat_amount);
+		      is_valid = false;
+		    }
+		}
+	    }
+	}
+    }
+
+  return is_valid;
+}
+
+std::vector<std::unique_ptr<AST::Token>>
+SubstituteCtx::substitute_repetition (
+  size_t pattern_start, size_t pattern_end,
+  std::unique_ptr<AST::Token> separator_token)
+{
+  rust_assert (pattern_end < macro.size ());
+
+  size_t repeat_amount = 0;
+  if (!check_repetition_amount (pattern_start, pattern_end, repeat_amount))
+    return {};
+
+  rust_debug ("repetition amount to use: %lu", (unsigned long) repeat_amount);
+  std::vector<std::unique_ptr<AST::Token>> expanded;
+  std::vector<std::unique_ptr<AST::Token>> new_macro;
+
+  // We want to generate a "new macro" to substitute with. This new macro
+  // should contain only the tokens inside the pattern
+  for (size_t tok_idx = pattern_start; tok_idx < pattern_end; tok_idx++)
+    new_macro.emplace_back (macro.at (tok_idx)->clone_token ());
+
+  // Then, we want to create a subset of the matches so that
+  // `substitute_tokens()` can only see one fragment per metavar. Let's say we
+  // have the following user input: (1 145 'h')
+  // on the following match arm: ($($lit:literal)*)
+  // which causes the following matches: { "lit": [1, 145, 'h'] }
+  //
+  // The pattern (new_macro) is `$lit:literal`
+  // The first time we expand it, we want $lit to have the following token: 1
+  // The second time, 145
+  // The third and final time, 'h'
+  //
+  // In order to do so we must create "sub maps", which only contain parts of
+  // the original matches
+  // sub-maps: [ { "lit": 1 }, { "lit": 145 }, { "lit": 'h' } ]
+  //
+  // and give them to `substitute_tokens` one by one.
+
+  for (size_t i = 0; i < repeat_amount; i++)
+    {
+      std::map<std::string, MatchedFragmentContainer> sub_map;
+      for (auto &kv_match : fragments)
+	{
+	  MatchedFragment sub_fragment;
+
+	  // FIXME: Hack: If a fragment is not repeated, how does it fit in the
+	  // submap? Do we really want to expand it? Is this normal behavior?
+	  if (kv_match.second.is_single_fragment ())
+	    sub_fragment = kv_match.second.get_single_fragment ();
+	  else
+	    sub_fragment = kv_match.second.get_fragments ()[i];
+
+	  sub_map.insert (
+	    {kv_match.first, MatchedFragmentContainer::metavar (sub_fragment)});
+	}
+
+      auto substitute_context = SubstituteCtx (input, new_macro, sub_map);
+      auto new_tokens = substitute_context.substitute_tokens ();
+
+      // Skip the first repetition, but add the separator to the expanded
+      // tokens if it is present
+      if (i != 0 && separator_token)
+	expanded.emplace_back (separator_token->clone_token ());
+
+      for (auto &new_token : new_tokens)
+	expanded.emplace_back (new_token->clone_token ());
+    }
+
+  // FIXME: We also need to make sure that all subsequent fragments
+  // contain the same amount of repetitions as the first one
+
+  return expanded;
+}
+
+static bool
+is_rep_op (std::unique_ptr<AST::Token> &tok)
+{
+  auto id = tok->get_id ();
+  return id == QUESTION_MARK || id == ASTERISK || id == PLUS;
+}
+
+std::pair<std::vector<std::unique_ptr<AST::Token>>, size_t>
+SubstituteCtx::substitute_token (size_t token_idx)
+{
+  auto &token = macro.at (token_idx);
+  switch (token->get_id ())
+    {
+    case IDENTIFIER:
+      rust_debug ("expanding metavar: %s", token->get_str ().c_str ());
+      return {substitute_metavar (token), 1};
+      case LEFT_PAREN: {
+	// We need to parse up until the closing delimiter and expand this
+	// fragment->n times.
+	rust_debug ("expanding repetition");
+
+	// We're in a context where macro repetitions have already been
+	// parsed and validated: This means that
+	// 1/ There will be no delimiters as that is an error
+	// 2/ There are no fragment specifiers anymore, which prevents us
+	// from reusing parser functions.
+	//
+	// Repetition patterns are also special in that they cannot contain
+	// "rogue" delimiters: For example, this is invalid, as they are
+	// parsed as MacroMatches and must contain a correct amount of
+	// delimiters.
+	// `$($e:expr ) )`
+	//            ^ rogue closing parenthesis
+	//
+	// With all of that in mind, we can simply skip ahead from one
+	// parenthesis to the other to find the pattern to expand. Of course,
+	// pairs of delimiters, including parentheses, are allowed.
+	// `$($e:expr ( ) )`
+	// Parentheses are the sole delimiter for which we need a special
+	// behavior since they delimit the repetition pattern
+
+	size_t pattern_start = token_idx + 1;
+	size_t pattern_end = pattern_start;
+	auto parentheses_stack = 0;
+	for (size_t idx = pattern_start; idx < macro.size (); idx++)
+	  {
+	    if (macro.at (idx)->get_id () == LEFT_PAREN)
+	      {
+		parentheses_stack++;
+	      }
+	    else if (macro.at (idx)->get_id () == RIGHT_PAREN)
+	      {
+		if (parentheses_stack == 0)
+		  {
+		    pattern_end = idx;
+		    break;
+		  }
+		parentheses_stack--;
+	      }
+	  }
+
+	// Unreachable case, but let's make sure we don't ever run into it
+	rust_assert (pattern_end != pattern_start);
+
+	std::unique_ptr<AST::Token> separator_token = nullptr;
+	if (pattern_end + 1 <= macro.size ())
+	  {
+	    auto &post_pattern_token = macro.at (pattern_end + 1);
+	    if (!is_rep_op (post_pattern_token))
+	      separator_token = post_pattern_token->clone_token ();
+	  }
+
+	// Amount of tokens to skip
+	auto to_skip = 0;
+	// Parentheses
+	to_skip += 2;
+	// Repetition operator
+	to_skip += 1;
+	// Separator
+	if (separator_token)
+	  to_skip += 1;
+
+	return {substitute_repetition (pattern_start, pattern_end,
+				       std::move (separator_token)),
+		pattern_end - pattern_start + to_skip};
+      }
+      // TODO: We need to check if the $ was alone. In that case, do
+      // not error out: Simply act as if there was an empty identifier
+      // with no associated fragment and paste the dollar sign in the
+      // transcription. Unsure how to do that since we always have at
+      // least the closing curly brace after an empty $...
+    default:
+      rust_error_at (token->get_locus (),
+		     "unexpected token in macro transcribe: expected "
+		     "%<(%> or identifier after %<$%>, got %<%s%>",
+		     get_token_description (token->get_id ()));
+    }
+
+  // FIXME: gcc_unreachable() error case?
+  return {std::vector<std::unique_ptr<AST::Token>> (), 0};
+}
+
+std::vector<std::unique_ptr<AST::Token>>
+SubstituteCtx::substitute_tokens ()
+{
+  std::vector<std::unique_ptr<AST::Token>> replaced_tokens;
+  rust_debug ("expanding tokens");
+
+  for (size_t i = 0; i < macro.size (); i++)
+    {
+      auto &tok = macro.at (i);
+      if (tok->get_id () == DOLLAR_SIGN)
+	{
+	  // Aaaaah, if only we had C++17 :)
+	  // auto [expanded, tok_to_skip] = ...
+	  auto p = substitute_token (i + 1);
+	  auto expanded = std::move (p.first);
+	  auto tok_to_skip = p.second;
+
+	  i += tok_to_skip;
+
+	  for (auto &token : expanded)
+	    replaced_tokens.emplace_back (token->clone_token ());
+	}
+      else
+	{
+	  replaced_tokens.emplace_back (tok->clone_token ());
+	}
+    }
+
+  return replaced_tokens;
+}
+
+} // namespace Rust
diff --git a/gcc/rust/expand/rust-macro-substitute-ctx.h b/gcc/rust/expand/rust-macro-substitute-ctx.h
new file mode 100644
index 00000000000..81dcab7643b
--- /dev/null
+++ b/gcc/rust/expand/rust-macro-substitute-ctx.h
@@ -0,0 +1,93 @@
+// 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/>.
+
+#include "rust-ast.h"
+#include "rust-macro-expand.h"
+
+namespace Rust {
+class SubstituteCtx
+{
+  std::vector<std::unique_ptr<AST::Token>> &input;
+  std::vector<std::unique_ptr<AST::Token>> &macro;
+  std::map<std::string, MatchedFragmentContainer> &fragments;
+
+  /**
+   * Find the repetition amount to use when expanding a repetition, and
+   * check that all fragments used respect that repetition amount
+   *
+   * @param pattern_start Start of the repetition pattern
+   * @param pattern_end End of the repetition pattern
+   * @param repeat_amount Reference to fill with the matched repetition amount
+   */
+  bool check_repetition_amount (size_t pattern_start, size_t pattern_end,
+				size_t &repeat_amount);
+
+public:
+  SubstituteCtx (std::vector<std::unique_ptr<AST::Token>> &input,
+		 std::vector<std::unique_ptr<AST::Token>> &macro,
+		 std::map<std::string, MatchedFragmentContainer> &fragments)
+    : input (input), macro (macro), fragments (fragments)
+  {}
+
+  /**
+   * Substitute a metavariable by its given fragment in a transcribing context,
+   * i.e. replacing $var with the associated fragment.
+   *
+   * @param metavar Metavariable to try and replace
+   *
+   * @return A token containing the associated fragment expanded into tokens if
+   * any, or the cloned token if no fragment was associated
+   */
+  std::vector<std::unique_ptr<AST::Token>>
+  substitute_metavar (std::unique_ptr<AST::Token> &metavar);
+
+  /**
+   * Substitute a macro repetition by its given fragments
+   *
+   * @param pattern_start Start index of the pattern tokens
+   * @param pattern_end End index of the patterns tokens
+   * @param separator Optional separator to include when expanding tokens
+   *
+   * @return A vector containing the repeated pattern
+   */
+  std::vector<std::unique_ptr<AST::Token>>
+  substitute_repetition (size_t pattern_start, size_t pattern_end,
+			 std::unique_ptr<AST::Token> separator);
+
+  /**
+   * Substitute a given token by its appropriate representation
+   *
+   * @param token_idx Current token to try and substitute
+   *
+   * @return A token containing the associated fragment expanded into tokens if
+   * any, or the cloned token if no fragment was associated, as well as the
+   * amount of tokens that should be skipped before the next invocation. Since
+   * this function may consume more than just one token, it is important to skip
+   * ahead of the input to avoid mis-substitutions
+   */
+  std::pair<std::vector<std::unique_ptr<AST::Token>>, size_t>
+  substitute_token (size_t token_idx);
+
+  /**
+   * Substitute all tokens by their appropriate representation
+   *
+   * @return A vector containing the substituted tokens
+   */
+  std::vector<std::unique_ptr<AST::Token>> substitute_tokens ();
+};
+} // namespace Rust
-- 
2.25.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [PATCH Rust front-end v2 12/37] gccrs: Add name resolution pass to the Rust front-end
  2022-08-24 11:59 Rust frontend patches v2 herron.philip
                   ` (10 preceding siblings ...)
  2022-08-24 11:59 ` [PATCH Rust front-end v2 11/37] gccrs: Add expansion pass for the " herron.philip
@ 2022-08-24 11:59 ` herron.philip
  2022-08-24 11:59 ` [PATCH Rust front-end v2 13/37] gccrs: Add second intermedite representation called HIR herron.philip
                   ` (25 subsequent siblings)
  37 siblings, 0 replies; 59+ messages in thread
From: herron.philip @ 2022-08-24 11:59 UTC (permalink / raw)
  To: gcc-patches; +Cc: gcc-rust, Philip Herron

From: Philip Herron <philip.herron@embecosm.com>

The name resolution is split into two phases, one toplevel pass which scans
the whole "Crate" which iterates all items and nested items in modules to
generate a context class full of CanonicalPath items. It also generates
a hierarchy of parent->child and child->parent relationships using the AST
NodeId for PathResolution in the second phase.

The second phase drills into each item like functions and creates a stack
of canonical paths for variables etc so that we can store information in
a side table of usage variable 'a' resolves to NodeId '123' which refers
to the NodeId of the "let a;" statement.
---
 gcc/rust/resolve/rust-ast-resolve-base.cc     |  658 +++++++++
 gcc/rust/resolve/rust-ast-resolve-base.h      |  221 +++
 gcc/rust/resolve/rust-ast-resolve-expr.cc     |  574 ++++++++
 gcc/rust/resolve/rust-ast-resolve-expr.h      |  133 ++
 gcc/rust/resolve/rust-ast-resolve-implitem.h  |  275 ++++
 gcc/rust/resolve/rust-ast-resolve-item.cc     | 1237 +++++++++++++++++
 gcc/rust/resolve/rust-ast-resolve-item.h      |  149 ++
 gcc/rust/resolve/rust-ast-resolve-path.cc     |  384 +++++
 gcc/rust/resolve/rust-ast-resolve-path.h      |   52 +
 gcc/rust/resolve/rust-ast-resolve-pattern.cc  |  163 +++
 gcc/rust/resolve/rust-ast-resolve-pattern.h   |   98 ++
 gcc/rust/resolve/rust-ast-resolve-stmt.cc     |   38 +
 gcc/rust/resolve/rust-ast-resolve-stmt.h      |  378 +++++
 .../rust-ast-resolve-struct-expr-field.cc     |   61 +
 .../rust-ast-resolve-struct-expr-field.h      |   55 +
 gcc/rust/resolve/rust-ast-resolve-toplevel.h  |  460 ++++++
 gcc/rust/resolve/rust-ast-resolve-type.cc     |  582 ++++++++
 gcc/rust/resolve/rust-ast-resolve-type.h      |  290 ++++
 gcc/rust/resolve/rust-ast-resolve.cc          |  115 ++
 gcc/rust/resolve/rust-ast-resolve.h           |   50 +
 gcc/rust/resolve/rust-ast-verify-assignee.h   |   84 ++
 gcc/rust/resolve/rust-name-resolver.cc        |  503 +++++++
 gcc/rust/resolve/rust-name-resolver.h         |  212 +++
 23 files changed, 6772 insertions(+)
 create mode 100644 gcc/rust/resolve/rust-ast-resolve-base.cc
 create mode 100644 gcc/rust/resolve/rust-ast-resolve-base.h
 create mode 100644 gcc/rust/resolve/rust-ast-resolve-expr.cc
 create mode 100644 gcc/rust/resolve/rust-ast-resolve-expr.h
 create mode 100644 gcc/rust/resolve/rust-ast-resolve-implitem.h
 create mode 100644 gcc/rust/resolve/rust-ast-resolve-item.cc
 create mode 100644 gcc/rust/resolve/rust-ast-resolve-item.h
 create mode 100644 gcc/rust/resolve/rust-ast-resolve-path.cc
 create mode 100644 gcc/rust/resolve/rust-ast-resolve-path.h
 create mode 100644 gcc/rust/resolve/rust-ast-resolve-pattern.cc
 create mode 100644 gcc/rust/resolve/rust-ast-resolve-pattern.h
 create mode 100644 gcc/rust/resolve/rust-ast-resolve-stmt.cc
 create mode 100644 gcc/rust/resolve/rust-ast-resolve-stmt.h
 create mode 100644 gcc/rust/resolve/rust-ast-resolve-struct-expr-field.cc
 create mode 100644 gcc/rust/resolve/rust-ast-resolve-struct-expr-field.h
 create mode 100644 gcc/rust/resolve/rust-ast-resolve-toplevel.h
 create mode 100644 gcc/rust/resolve/rust-ast-resolve-type.cc
 create mode 100644 gcc/rust/resolve/rust-ast-resolve-type.h
 create mode 100644 gcc/rust/resolve/rust-ast-resolve.cc
 create mode 100644 gcc/rust/resolve/rust-ast-resolve.h
 create mode 100644 gcc/rust/resolve/rust-ast-verify-assignee.h
 create mode 100644 gcc/rust/resolve/rust-name-resolver.cc
 create mode 100644 gcc/rust/resolve/rust-name-resolver.h

diff --git a/gcc/rust/resolve/rust-ast-resolve-base.cc b/gcc/rust/resolve/rust-ast-resolve-base.cc
new file mode 100644
index 00000000000..2a78918fbdb
--- /dev/null
+++ b/gcc/rust/resolve/rust-ast-resolve-base.cc
@@ -0,0 +1,658 @@
+// 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/>.
+
+#include "rust-ast-resolve-base.h"
+#include "rust-ast-resolve-expr.h"
+#include "rust-ast-resolve-path.h"
+#include "rust-item.h"
+
+namespace Rust {
+namespace Resolver {
+
+bool
+ResolverBase::resolve_visibility (const AST::Visibility &vis)
+{
+  if (vis.has_path ())
+    {
+      auto path = vis.get_path ();
+      ResolvePath::go (&path);
+
+      // Do we need to lookup something here?
+      // Is it just about resolving the names correctly so we can look them up
+      // later?
+    }
+
+  return true;
+}
+
+// Default visitors implementations
+
+void
+ResolverBase::visit (AST::Token &)
+{}
+
+void
+ResolverBase::visit (AST::DelimTokenTree &)
+{}
+
+void
+ResolverBase::visit (AST::AttrInputMetaItemContainer &)
+{}
+
+void
+ResolverBase::visit (AST::IdentifierExpr &)
+{}
+
+void
+ResolverBase::visit (AST::Lifetime &)
+{}
+
+void
+ResolverBase::visit (AST::LifetimeParam &)
+{}
+
+void
+ResolverBase::visit (AST::ConstGenericParam &)
+{}
+
+void
+ResolverBase::visit (AST::PathInExpression &)
+{}
+
+void
+ResolverBase::visit (AST::TypePathSegment &)
+{}
+
+void
+ResolverBase::visit (AST::TypePathSegmentGeneric &)
+{}
+
+void
+ResolverBase::visit (AST::TypePathSegmentFunction &)
+{}
+
+void
+ResolverBase::visit (AST::TypePath &)
+{}
+
+void
+ResolverBase::visit (AST::QualifiedPathInExpression &)
+{}
+
+void
+ResolverBase::visit (AST::QualifiedPathInType &)
+{}
+
+void
+ResolverBase::visit (AST::LiteralExpr &)
+{}
+
+void
+ResolverBase::visit (AST::AttrInputLiteral &)
+{}
+
+void
+ResolverBase::visit (AST::MetaItemLitExpr &)
+{}
+
+void
+ResolverBase::visit (AST::MetaItemPathLit &)
+{}
+
+void
+ResolverBase::visit (AST::BorrowExpr &)
+{}
+
+void
+ResolverBase::visit (AST::DereferenceExpr &)
+{}
+
+void
+ResolverBase::visit (AST::ErrorPropagationExpr &)
+{}
+
+void
+ResolverBase::visit (AST::NegationExpr &)
+{}
+
+void
+ResolverBase::visit (AST::ArithmeticOrLogicalExpr &)
+{}
+
+void
+ResolverBase::visit (AST::ComparisonExpr &)
+{}
+
+void
+ResolverBase::visit (AST::LazyBooleanExpr &)
+{}
+
+void
+ResolverBase::visit (AST::TypeCastExpr &)
+{}
+
+void
+ResolverBase::visit (AST::AssignmentExpr &)
+{}
+
+void
+ResolverBase::visit (AST::CompoundAssignmentExpr &)
+{}
+
+void
+ResolverBase::visit (AST::GroupedExpr &)
+{}
+
+void
+ResolverBase::visit (AST::ArrayElemsValues &)
+{}
+
+void
+ResolverBase::visit (AST::ArrayElemsCopied &)
+{}
+
+void
+ResolverBase::visit (AST::ArrayExpr &)
+{}
+
+void
+ResolverBase::visit (AST::ArrayIndexExpr &)
+{}
+
+void
+ResolverBase::visit (AST::TupleExpr &)
+{}
+
+void
+ResolverBase::visit (AST::TupleIndexExpr &)
+{}
+
+void
+ResolverBase::visit (AST::StructExprStruct &)
+{}
+
+void
+ResolverBase::visit (AST::StructExprFieldIdentifier &)
+{}
+
+void
+ResolverBase::visit (AST::StructExprFieldIdentifierValue &)
+{}
+
+void
+ResolverBase::visit (AST::StructExprFieldIndexValue &)
+{}
+
+void
+ResolverBase::visit (AST::StructExprStructFields &)
+{}
+
+void
+ResolverBase::visit (AST::StructExprStructBase &)
+{}
+
+void
+ResolverBase::visit (AST::CallExpr &)
+{}
+
+void
+ResolverBase::visit (AST::MethodCallExpr &)
+{}
+
+void
+ResolverBase::visit (AST::FieldAccessExpr &)
+{}
+
+void
+ResolverBase::visit (AST::ClosureExprInner &)
+{}
+
+void
+ResolverBase::visit (AST::BlockExpr &)
+{}
+
+void
+ResolverBase::visit (AST::ClosureExprInnerTyped &)
+{}
+
+void
+ResolverBase::visit (AST::ContinueExpr &)
+{}
+
+void
+ResolverBase::visit (AST::BreakExpr &)
+{}
+
+void
+ResolverBase::visit (AST::RangeFromToExpr &)
+{}
+
+void
+ResolverBase::visit (AST::RangeFromExpr &)
+{}
+
+void
+ResolverBase::visit (AST::RangeToExpr &)
+{}
+
+void
+ResolverBase::visit (AST::RangeFullExpr &)
+{}
+
+void
+ResolverBase::visit (AST::RangeFromToInclExpr &)
+{}
+
+void
+ResolverBase::visit (AST::RangeToInclExpr &)
+{}
+
+void
+ResolverBase::visit (AST::ReturnExpr &)
+{}
+
+void
+ResolverBase::visit (AST::UnsafeBlockExpr &)
+{}
+
+void
+ResolverBase::visit (AST::LoopExpr &)
+{}
+
+void
+ResolverBase::visit (AST::WhileLoopExpr &)
+{}
+
+void
+ResolverBase::visit (AST::WhileLetLoopExpr &)
+{}
+
+void
+ResolverBase::visit (AST::ForLoopExpr &)
+{}
+
+void
+ResolverBase::visit (AST::IfExpr &)
+{}
+
+void
+ResolverBase::visit (AST::IfExprConseqElse &)
+{}
+
+void
+ResolverBase::visit (AST::IfExprConseqIf &)
+{}
+
+void
+ResolverBase::visit (AST::IfExprConseqIfLet &)
+{}
+
+void
+ResolverBase::visit (AST::IfLetExpr &)
+{}
+
+void
+ResolverBase::visit (AST::IfLetExprConseqElse &)
+{}
+
+void
+ResolverBase::visit (AST::IfLetExprConseqIf &)
+{}
+
+void
+ResolverBase::visit (AST::IfLetExprConseqIfLet &)
+{}
+
+void
+ResolverBase::visit (AST::MatchExpr &)
+{}
+
+void
+ResolverBase::visit (AST::AwaitExpr &)
+{}
+
+void
+ResolverBase::visit (AST::AsyncBlockExpr &)
+{}
+
+void
+ResolverBase::visit (AST::TypeParam &)
+{}
+
+void
+ResolverBase::visit (AST::LifetimeWhereClauseItem &)
+{}
+
+void
+ResolverBase::visit (AST::TypeBoundWhereClauseItem &)
+{}
+
+void
+ResolverBase::visit (AST::Method &)
+{}
+
+void
+ResolverBase::visit (AST::Module &)
+{}
+
+void
+ResolverBase::visit (AST::ExternCrate &)
+{}
+
+void
+ResolverBase::visit (AST::UseTreeGlob &)
+{}
+
+void
+ResolverBase::visit (AST::UseTreeList &)
+{}
+
+void
+ResolverBase::visit (AST::UseTreeRebind &)
+{}
+
+void
+ResolverBase::visit (AST::UseDeclaration &)
+{}
+
+void
+ResolverBase::visit (AST::Function &)
+{}
+
+void
+ResolverBase::visit (AST::TypeAlias &)
+{}
+
+void
+ResolverBase::visit (AST::StructStruct &)
+{}
+
+void
+ResolverBase::visit (AST::TupleStruct &)
+{}
+
+void
+ResolverBase::visit (AST::EnumItem &)
+{}
+
+void
+ResolverBase::visit (AST::EnumItemTuple &)
+{}
+
+void
+ResolverBase::visit (AST::EnumItemStruct &)
+{}
+
+void
+ResolverBase::visit (AST::EnumItemDiscriminant &)
+{}
+
+void
+ResolverBase::visit (AST::Enum &)
+{}
+
+void
+ResolverBase::visit (AST::Union &)
+{}
+
+void
+ResolverBase::visit (AST::ConstantItem &)
+{}
+
+void
+ResolverBase::visit (AST::StaticItem &)
+{}
+
+void
+ResolverBase::visit (AST::TraitItemFunc &)
+{}
+
+void
+ResolverBase::visit (AST::TraitItemMethod &)
+{}
+
+void
+ResolverBase::visit (AST::TraitItemConst &)
+{}
+
+void
+ResolverBase::visit (AST::TraitItemType &)
+{}
+
+void
+ResolverBase::visit (AST::Trait &)
+{}
+
+void
+ResolverBase::visit (AST::InherentImpl &)
+{}
+
+void
+ResolverBase::visit (AST::TraitImpl &)
+{}
+
+void
+ResolverBase::visit (AST::ExternalStaticItem &)
+{}
+
+void
+ResolverBase::visit (AST::ExternalFunctionItem &)
+{}
+
+void
+ResolverBase::visit (AST::ExternBlock &)
+{}
+
+void
+ResolverBase::visit (AST::MacroMatchFragment &)
+{}
+
+void
+ResolverBase::visit (AST::MacroMatchRepetition &)
+{}
+
+void
+ResolverBase::visit (AST::MacroMatcher &)
+{}
+
+void
+ResolverBase::visit (AST::MacroRulesDefinition &)
+{}
+
+void
+ResolverBase::visit (AST::MacroInvocation &)
+{}
+
+void
+ResolverBase::visit (AST::MetaItemPath &)
+{}
+
+void
+ResolverBase::visit (AST::MetaItemSeq &)
+{}
+
+void
+ResolverBase::visit (AST::MetaWord &)
+{}
+
+void
+ResolverBase::visit (AST::MetaNameValueStr &)
+{}
+
+void
+ResolverBase::visit (AST::MetaListPaths &)
+{}
+
+void
+ResolverBase::visit (AST::MetaListNameValueStr &)
+{}
+
+void
+ResolverBase::visit (AST::LiteralPattern &)
+{}
+
+void
+ResolverBase::visit (AST::IdentifierPattern &)
+{}
+
+void
+ResolverBase::visit (AST::WildcardPattern &)
+{}
+
+void
+ResolverBase::visit (AST::RangePatternBoundLiteral &)
+{}
+
+void
+ResolverBase::visit (AST::RangePatternBoundPath &)
+{}
+
+void
+ResolverBase::visit (AST::RangePatternBoundQualPath &)
+{}
+
+void
+ResolverBase::visit (AST::RangePattern &)
+{}
+
+void
+ResolverBase::visit (AST::ReferencePattern &)
+{}
+
+void
+ResolverBase::visit (AST::StructPatternFieldTuplePat &)
+{}
+
+void
+ResolverBase::visit (AST::StructPatternFieldIdentPat &)
+{}
+
+void
+ResolverBase::visit (AST::StructPatternFieldIdent &)
+{}
+
+void
+ResolverBase::visit (AST::StructPattern &)
+{}
+
+void
+ResolverBase::visit (AST::TupleStructItemsNoRange &)
+{}
+
+void
+ResolverBase::visit (AST::TupleStructItemsRange &)
+{}
+
+void
+ResolverBase::visit (AST::TupleStructPattern &)
+{}
+
+void
+ResolverBase::visit (AST::TuplePatternItemsMultiple &)
+{}
+
+void
+ResolverBase::visit (AST::TuplePatternItemsRanged &)
+{}
+
+void
+ResolverBase::visit (AST::TuplePattern &)
+{}
+
+void
+ResolverBase::visit (AST::GroupedPattern &)
+{}
+
+void
+ResolverBase::visit (AST::SlicePattern &)
+{}
+
+void
+ResolverBase::visit (AST::EmptyStmt &)
+{}
+
+void
+ResolverBase::visit (AST::LetStmt &)
+{}
+
+void
+ResolverBase::visit (AST::ExprStmtWithoutBlock &)
+{}
+
+void
+ResolverBase::visit (AST::ExprStmtWithBlock &)
+{}
+
+void
+ResolverBase::visit (AST::TraitBound &)
+{}
+
+void
+ResolverBase::visit (AST::ImplTraitType &)
+{}
+
+void
+ResolverBase::visit (AST::TraitObjectType &)
+{}
+
+void
+ResolverBase::visit (AST::ParenthesisedType &)
+{}
+
+void
+ResolverBase::visit (AST::ImplTraitTypeOneBound &)
+{}
+
+void
+ResolverBase::visit (AST::TraitObjectTypeOneBound &)
+{}
+
+void
+ResolverBase::visit (AST::TupleType &)
+{}
+
+void
+ResolverBase::visit (AST::NeverType &)
+{}
+
+void
+ResolverBase::visit (AST::RawPointerType &)
+{}
+
+void
+ResolverBase::visit (AST::ReferenceType &)
+{}
+
+void
+ResolverBase::visit (AST::ArrayType &)
+{}
+
+void
+ResolverBase::visit (AST::SliceType &)
+{}
+
+void
+ResolverBase::visit (AST::InferredType &)
+{}
+
+void
+ResolverBase::visit (AST::BareFunctionType &)
+{}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/resolve/rust-ast-resolve-base.h b/gcc/rust/resolve/rust-ast-resolve-base.h
new file mode 100644
index 00000000000..32f30bcea62
--- /dev/null
+++ b/gcc/rust/resolve/rust-ast-resolve-base.h
@@ -0,0 +1,221 @@
+// 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_AST_RESOLVE_BASE_H
+#define RUST_AST_RESOLVE_BASE_H
+
+#include "rust-ast-visitor.h"
+#include "rust-name-resolver.h"
+#include "rust-diagnostics.h"
+#include "rust-location.h"
+
+namespace Rust {
+namespace Resolver {
+
+class ResolverBase : public AST::ASTVisitor
+{
+public:
+  virtual ~ResolverBase () {}
+
+  void visit (AST::Token &);
+  void visit (AST::DelimTokenTree &);
+  void visit (AST::AttrInputMetaItemContainer &);
+  void visit (AST::IdentifierExpr &);
+  void visit (AST::Lifetime &);
+  void visit (AST::LifetimeParam &);
+  void visit (AST::ConstGenericParam &);
+  void visit (AST::PathInExpression &);
+  void visit (AST::TypePathSegment &);
+  void visit (AST::TypePathSegmentGeneric &);
+  void visit (AST::TypePathSegmentFunction &);
+  void visit (AST::TypePath &);
+  void visit (AST::QualifiedPathInExpression &);
+  void visit (AST::QualifiedPathInType &);
+  void visit (AST::LiteralExpr &);
+  void visit (AST::AttrInputLiteral &);
+  void visit (AST::MetaItemLitExpr &);
+  void visit (AST::MetaItemPathLit &);
+  void visit (AST::BorrowExpr &);
+  void visit (AST::DereferenceExpr &);
+  void visit (AST::ErrorPropagationExpr &);
+  void visit (AST::NegationExpr &);
+  void visit (AST::ArithmeticOrLogicalExpr &);
+  void visit (AST::ComparisonExpr &);
+  void visit (AST::LazyBooleanExpr &);
+  void visit (AST::TypeCastExpr &);
+  void visit (AST::AssignmentExpr &);
+  void visit (AST::CompoundAssignmentExpr &);
+  void visit (AST::GroupedExpr &);
+  void visit (AST::ArrayElemsValues &);
+  void visit (AST::ArrayElemsCopied &);
+  void visit (AST::ArrayExpr &);
+  void visit (AST::ArrayIndexExpr &);
+  void visit (AST::TupleExpr &);
+  void visit (AST::TupleIndexExpr &);
+  void visit (AST::StructExprStruct &);
+  void visit (AST::StructExprFieldIdentifier &);
+  void visit (AST::StructExprFieldIdentifierValue &);
+  void visit (AST::StructExprFieldIndexValue &);
+  void visit (AST::StructExprStructFields &);
+  void visit (AST::StructExprStructBase &);
+  void visit (AST::CallExpr &);
+  void visit (AST::MethodCallExpr &);
+  void visit (AST::FieldAccessExpr &);
+  void visit (AST::ClosureExprInner &);
+  void visit (AST::BlockExpr &);
+  void visit (AST::ClosureExprInnerTyped &);
+  void visit (AST::ContinueExpr &);
+  void visit (AST::BreakExpr &);
+  void visit (AST::RangeFromToExpr &);
+  void visit (AST::RangeFromExpr &);
+  void visit (AST::RangeToExpr &);
+  void visit (AST::RangeFullExpr &);
+  void visit (AST::RangeFromToInclExpr &);
+  void visit (AST::RangeToInclExpr &);
+  void visit (AST::ReturnExpr &);
+  void visit (AST::UnsafeBlockExpr &);
+  void visit (AST::LoopExpr &);
+  void visit (AST::WhileLoopExpr &);
+  void visit (AST::WhileLetLoopExpr &);
+  void visit (AST::ForLoopExpr &);
+  void visit (AST::IfExpr &);
+  void visit (AST::IfExprConseqElse &);
+  void visit (AST::IfExprConseqIf &);
+  void visit (AST::IfExprConseqIfLet &);
+  void visit (AST::IfLetExpr &);
+  void visit (AST::IfLetExprConseqElse &);
+  void visit (AST::IfLetExprConseqIf &);
+  void visit (AST::IfLetExprConseqIfLet &);
+
+  void visit (AST::MatchExpr &);
+  void visit (AST::AwaitExpr &);
+  void visit (AST::AsyncBlockExpr &);
+
+  void visit (AST::TypeParam &);
+
+  void visit (AST::LifetimeWhereClauseItem &);
+  void visit (AST::TypeBoundWhereClauseItem &);
+  void visit (AST::Method &);
+  void visit (AST::Module &);
+  void visit (AST::ExternCrate &);
+
+  void visit (AST::UseTreeGlob &);
+  void visit (AST::UseTreeList &);
+  void visit (AST::UseTreeRebind &);
+  void visit (AST::UseDeclaration &);
+  void visit (AST::Function &);
+  void visit (AST::TypeAlias &);
+  void visit (AST::StructStruct &);
+  void visit (AST::TupleStruct &);
+  void visit (AST::EnumItem &);
+  void visit (AST::EnumItemTuple &);
+  void visit (AST::EnumItemStruct &);
+  void visit (AST::EnumItemDiscriminant &);
+  void visit (AST::Enum &);
+  void visit (AST::Union &);
+  void visit (AST::ConstantItem &);
+  void visit (AST::StaticItem &);
+  void visit (AST::TraitItemFunc &);
+  void visit (AST::TraitItemMethod &);
+  void visit (AST::TraitItemConst &);
+  void visit (AST::TraitItemType &);
+  void visit (AST::Trait &);
+  void visit (AST::InherentImpl &);
+  void visit (AST::TraitImpl &);
+
+  void visit (AST::ExternalStaticItem &);
+  void visit (AST::ExternalFunctionItem &);
+  void visit (AST::ExternBlock &);
+
+  void visit (AST::MacroMatchFragment &);
+  void visit (AST::MacroMatchRepetition &);
+  void visit (AST::MacroMatcher &);
+  void visit (AST::MacroRulesDefinition &);
+  void visit (AST::MacroInvocation &);
+  void visit (AST::MetaItemPath &);
+  void visit (AST::MetaItemSeq &);
+  void visit (AST::MetaWord &);
+  void visit (AST::MetaNameValueStr &);
+  void visit (AST::MetaListPaths &);
+  void visit (AST::MetaListNameValueStr &);
+
+  void visit (AST::LiteralPattern &);
+  void visit (AST::IdentifierPattern &);
+  void visit (AST::WildcardPattern &);
+
+  void visit (AST::RangePatternBoundLiteral &);
+  void visit (AST::RangePatternBoundPath &);
+  void visit (AST::RangePatternBoundQualPath &);
+  void visit (AST::RangePattern &);
+  void visit (AST::ReferencePattern &);
+
+  void visit (AST::StructPatternFieldTuplePat &);
+  void visit (AST::StructPatternFieldIdentPat &);
+  void visit (AST::StructPatternFieldIdent &);
+  void visit (AST::StructPattern &);
+
+  void visit (AST::TupleStructItemsNoRange &);
+  void visit (AST::TupleStructItemsRange &);
+  void visit (AST::TupleStructPattern &);
+
+  void visit (AST::TuplePatternItemsMultiple &);
+  void visit (AST::TuplePatternItemsRanged &);
+  void visit (AST::TuplePattern &);
+  void visit (AST::GroupedPattern &);
+  void visit (AST::SlicePattern &);
+
+  void visit (AST::EmptyStmt &);
+  void visit (AST::LetStmt &);
+  void visit (AST::ExprStmtWithoutBlock &);
+  void visit (AST::ExprStmtWithBlock &);
+
+  void visit (AST::TraitBound &);
+  void visit (AST::ImplTraitType &);
+  void visit (AST::TraitObjectType &);
+  void visit (AST::ParenthesisedType &);
+  void visit (AST::ImplTraitTypeOneBound &);
+  void visit (AST::TraitObjectTypeOneBound &);
+  void visit (AST::TupleType &);
+  void visit (AST::NeverType &);
+  void visit (AST::RawPointerType &);
+  void visit (AST::ReferenceType &);
+  void visit (AST::ArrayType &);
+  void visit (AST::SliceType &);
+  void visit (AST::InferredType &);
+  void visit (AST::BareFunctionType &);
+
+protected:
+  ResolverBase ()
+    : resolver (Resolver::get ()), mappings (Analysis::Mappings::get ()),
+      resolved_node (UNKNOWN_NODEID)
+  {}
+
+  /**
+   * Resolve a visibility's path through the name resolver
+   */
+  bool resolve_visibility (const AST::Visibility &vis);
+
+  Resolver *resolver;
+  Analysis::Mappings *mappings;
+  NodeId resolved_node;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_AST_RESOLVE_BASE_H
diff --git a/gcc/rust/resolve/rust-ast-resolve-expr.cc b/gcc/rust/resolve/rust-ast-resolve-expr.cc
new file mode 100644
index 00000000000..4cc4e26e3e9
--- /dev/null
+++ b/gcc/rust/resolve/rust-ast-resolve-expr.cc
@@ -0,0 +1,574 @@
+// 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/>.
+
+#include "rust-ast-resolve-expr.h"
+#include "rust-ast-resolve-stmt.h"
+#include "rust-ast-resolve-struct-expr-field.h"
+#include "rust-ast-verify-assignee.h"
+#include "rust-ast-resolve-type.h"
+#include "rust-ast-resolve-pattern.h"
+#include "rust-ast-resolve-path.h"
+
+namespace Rust {
+namespace Resolver {
+
+void
+ResolveExpr::go (AST::Expr *expr, const CanonicalPath &prefix,
+		 const CanonicalPath &canonical_prefix)
+{
+  ResolveExpr resolver (prefix, canonical_prefix);
+  expr->accept_vis (resolver);
+}
+
+void
+ResolveExpr::visit (AST::TupleIndexExpr &expr)
+{
+  ResolveExpr::go (expr.get_tuple_expr ().get (), prefix, canonical_prefix);
+}
+
+void
+ResolveExpr::visit (AST::TupleExpr &expr)
+{
+  if (expr.is_unit ())
+    return;
+
+  for (auto &elem : expr.get_tuple_elems ())
+    ResolveExpr::go (elem.get (), prefix, canonical_prefix);
+}
+
+void
+ResolveExpr::visit (AST::PathInExpression &expr)
+{
+  ResolvePath::go (&expr);
+}
+
+void
+ResolveExpr::visit (AST::QualifiedPathInExpression &expr)
+{
+  ResolvePath::go (&expr);
+}
+
+void
+ResolveExpr::visit (AST::ReturnExpr &expr)
+{
+  if (expr.has_returned_expr ())
+    ResolveExpr::go (expr.get_returned_expr ().get (), prefix,
+		     canonical_prefix);
+}
+
+void
+ResolveExpr::visit (AST::CallExpr &expr)
+{
+  ResolveExpr::go (expr.get_function_expr ().get (), prefix, canonical_prefix);
+  for (auto &param : expr.get_params ())
+    ResolveExpr::go (param.get (), prefix, canonical_prefix);
+}
+
+void
+ResolveExpr::visit (AST::MethodCallExpr &expr)
+{
+  ResolveExpr::go (expr.get_receiver_expr ().get (), prefix, canonical_prefix);
+
+  if (expr.get_method_name ().has_generic_args ())
+    {
+      AST::GenericArgs &args = expr.get_method_name ().get_generic_args ();
+      ResolveGenericArgs::go (args, prefix, canonical_prefix);
+    }
+
+  auto const &in_params = expr.get_params ();
+  for (auto &param : in_params)
+    ResolveExpr::go (param.get (), prefix, canonical_prefix);
+}
+
+void
+ResolveExpr::visit (AST::AssignmentExpr &expr)
+{
+  ResolveExpr::go (expr.get_left_expr ().get (), prefix, canonical_prefix);
+  ResolveExpr::go (expr.get_right_expr ().get (), prefix, canonical_prefix);
+
+  // need to verify the assignee
+  VerifyAsignee::go (expr.get_left_expr ().get (), expr.get_node_id ());
+}
+
+void
+ResolveExpr::visit (AST::IdentifierExpr &expr)
+{
+  if (resolver->get_name_scope ().lookup (
+	CanonicalPath::new_seg (expr.get_node_id (), expr.as_string ()),
+	&resolved_node))
+    {
+      resolver->insert_resolved_name (expr.get_node_id (), resolved_node);
+    }
+  else if (resolver->get_type_scope ().lookup (
+	     CanonicalPath::new_seg (expr.get_node_id (), expr.as_string ()),
+	     &resolved_node))
+    {
+      resolver->insert_resolved_type (expr.get_node_id (), resolved_node);
+    }
+  else
+    {
+      rust_error_at (expr.get_locus (), "failed to find name: %s",
+		     expr.as_string ().c_str ());
+    }
+}
+
+void
+ResolveExpr::visit (AST::ArithmeticOrLogicalExpr &expr)
+{
+  ResolveExpr::go (expr.get_left_expr ().get (), prefix, canonical_prefix);
+  ResolveExpr::go (expr.get_right_expr ().get (), prefix, canonical_prefix);
+}
+
+void
+ResolveExpr::visit (AST::CompoundAssignmentExpr &expr)
+{
+  ResolveExpr::go (expr.get_left_expr ().get (), prefix, canonical_prefix);
+  ResolveExpr::go (expr.get_right_expr ().get (), prefix, canonical_prefix);
+
+  // need to verify the assignee
+  VerifyAsignee::go (expr.get_left_expr ().get (), expr.get_node_id ());
+}
+
+void
+ResolveExpr::visit (AST::ComparisonExpr &expr)
+{
+  ResolveExpr::go (expr.get_left_expr ().get (), prefix, canonical_prefix);
+  ResolveExpr::go (expr.get_right_expr ().get (), prefix, canonical_prefix);
+}
+
+void
+ResolveExpr::visit (AST::LazyBooleanExpr &expr)
+{
+  ResolveExpr::go (expr.get_left_expr ().get (), prefix, canonical_prefix);
+  ResolveExpr::go (expr.get_right_expr ().get (), prefix, canonical_prefix);
+}
+
+void
+ResolveExpr::visit (AST::NegationExpr &expr)
+{
+  ResolveExpr::go (expr.get_negated_expr ().get (), prefix, canonical_prefix);
+}
+
+void
+ResolveExpr::visit (AST::TypeCastExpr &expr)
+{
+  ResolveType::go (expr.get_type_to_cast_to ().get ());
+  ResolveExpr::go (expr.get_casted_expr ().get (), prefix, canonical_prefix);
+}
+
+void
+ResolveExpr::visit (AST::IfExpr &expr)
+{
+  ResolveExpr::go (expr.get_condition_expr ().get (), prefix, canonical_prefix);
+  ResolveExpr::go (expr.get_if_block ().get (), prefix, canonical_prefix);
+}
+
+void
+ResolveExpr::visit (AST::IfExprConseqElse &expr)
+{
+  ResolveExpr::go (expr.get_condition_expr ().get (), prefix, canonical_prefix);
+  ResolveExpr::go (expr.get_if_block ().get (), prefix, canonical_prefix);
+  ResolveExpr::go (expr.get_else_block ().get (), prefix, canonical_prefix);
+}
+
+void
+ResolveExpr::visit (AST::IfExprConseqIf &expr)
+{
+  ResolveExpr::go (expr.get_condition_expr ().get (), prefix, canonical_prefix);
+  ResolveExpr::go (expr.get_if_block ().get (), prefix, canonical_prefix);
+  ResolveExpr::go (expr.get_conseq_if_expr ().get (), prefix, canonical_prefix);
+}
+
+void
+ResolveExpr::visit (AST::IfLetExpr &expr)
+{
+  ResolveExpr::go (expr.get_value_expr ().get (), prefix, canonical_prefix);
+
+  NodeId scope_node_id = expr.get_node_id ();
+  resolver->get_name_scope ().push (scope_node_id);
+  resolver->get_type_scope ().push (scope_node_id);
+  resolver->get_label_scope ().push (scope_node_id);
+  resolver->push_new_name_rib (resolver->get_name_scope ().peek ());
+  resolver->push_new_type_rib (resolver->get_type_scope ().peek ());
+  resolver->push_new_label_rib (resolver->get_type_scope ().peek ());
+
+  for (auto &pattern : expr.get_patterns ())
+    {
+      PatternDeclaration::go (pattern.get ());
+    }
+
+  ResolveExpr::go (expr.get_if_block ().get (), prefix, canonical_prefix);
+
+  resolver->get_name_scope ().pop ();
+  resolver->get_type_scope ().pop ();
+  resolver->get_label_scope ().pop ();
+}
+
+void
+ResolveExpr::visit (AST::BlockExpr &expr)
+{
+  NodeId scope_node_id = expr.get_node_id ();
+  resolver->get_name_scope ().push (scope_node_id);
+  resolver->get_type_scope ().push (scope_node_id);
+  resolver->get_label_scope ().push (scope_node_id);
+  resolver->push_new_name_rib (resolver->get_name_scope ().peek ());
+  resolver->push_new_type_rib (resolver->get_type_scope ().peek ());
+  resolver->push_new_label_rib (resolver->get_type_scope ().peek ());
+
+  for (auto &s : expr.get_statements ())
+    {
+      if (s->is_item ())
+	ResolveStmt::go (s.get (), prefix, canonical_prefix,
+			 CanonicalPath::create_empty ());
+    }
+
+  for (auto &s : expr.get_statements ())
+    {
+      if (!s->is_item ())
+	ResolveStmt::go (s.get (), prefix, canonical_prefix,
+			 CanonicalPath::create_empty ());
+    }
+
+  if (expr.has_tail_expr ())
+    ResolveExpr::go (expr.get_tail_expr ().get (), prefix, canonical_prefix);
+
+  resolver->get_name_scope ().pop ();
+  resolver->get_type_scope ().pop ();
+  resolver->get_label_scope ().pop ();
+}
+
+void
+ResolveExpr::visit (AST::UnsafeBlockExpr &expr)
+{
+  expr.get_block_expr ()->accept_vis (*this);
+}
+
+void
+ResolveExpr::visit (AST::ArrayElemsValues &elems)
+{
+  for (auto &elem : elems.get_values ())
+    ResolveExpr::go (elem.get (), prefix, canonical_prefix);
+}
+
+void
+ResolveExpr::visit (AST::ArrayExpr &expr)
+{
+  expr.get_array_elems ()->accept_vis (*this);
+}
+
+void
+ResolveExpr::visit (AST::ArrayIndexExpr &expr)
+{
+  ResolveExpr::go (expr.get_array_expr ().get (), prefix, canonical_prefix);
+  ResolveExpr::go (expr.get_index_expr ().get (), prefix, canonical_prefix);
+}
+
+void
+ResolveExpr::visit (AST::ArrayElemsCopied &expr)
+{
+  ResolveExpr::go (expr.get_num_copies ().get (), prefix, canonical_prefix);
+  ResolveExpr::go (expr.get_elem_to_copy ().get (), prefix, canonical_prefix);
+}
+
+// this this an empty struct constructor like 'S {}'
+void
+ResolveExpr::visit (AST::StructExprStruct &struct_expr)
+{
+  ResolveExpr::go (&struct_expr.get_struct_name (), prefix, canonical_prefix);
+}
+
+// this this a struct constructor with fields
+void
+ResolveExpr::visit (AST::StructExprStructFields &struct_expr)
+{
+  ResolveExpr::go (&struct_expr.get_struct_name (), prefix, canonical_prefix);
+
+  if (struct_expr.has_struct_base ())
+    {
+      AST::StructBase &base = struct_expr.get_struct_base ();
+      ResolveExpr::go (base.get_base_struct ().get (), prefix,
+		       canonical_prefix);
+    }
+
+  auto const &struct_fields = struct_expr.get_fields ();
+  for (auto &struct_field : struct_fields)
+    {
+      ResolveStructExprField::go (struct_field.get (), prefix,
+				  canonical_prefix);
+    }
+}
+
+void
+ResolveExpr::visit (AST::GroupedExpr &expr)
+{
+  ResolveExpr::go (expr.get_expr_in_parens ().get (), prefix, canonical_prefix);
+}
+
+void
+ResolveExpr::visit (AST::FieldAccessExpr &expr)
+{
+  ResolveExpr::go (expr.get_receiver_expr ().get (), prefix, canonical_prefix);
+}
+
+void
+ResolveExpr::visit (AST::LoopExpr &expr)
+{
+  if (expr.has_loop_label ())
+    {
+      auto label = expr.get_loop_label ();
+      if (label.get_lifetime ().get_lifetime_type ()
+	  != AST::Lifetime::LifetimeType::NAMED)
+	{
+	  rust_error_at (label.get_locus (),
+			 "Labels must be a named lifetime value");
+	  return;
+	}
+
+      auto label_name = label.get_lifetime ().get_lifetime_name ();
+      auto label_lifetime_node_id = label.get_lifetime ().get_node_id ();
+      resolver->get_label_scope ().insert (
+	CanonicalPath::new_seg (expr.get_node_id (), label_name),
+	label_lifetime_node_id, label.get_locus (), false,
+	[&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	  rust_error_at (label.get_locus (), "label redefined multiple times");
+	  rust_error_at (locus, "was defined here");
+	});
+    }
+  ResolveExpr::go (expr.get_loop_block ().get (), prefix, canonical_prefix);
+}
+
+void
+ResolveExpr::visit (AST::BreakExpr &expr)
+{
+  if (expr.has_label ())
+    {
+      auto label = expr.get_label ();
+      if (label.get_lifetime_type () != AST::Lifetime::LifetimeType::NAMED)
+	{
+	  rust_error_at (label.get_locus (),
+			 "Labels must be a named lifetime value");
+	  return;
+	}
+
+      NodeId resolved_node = UNKNOWN_NODEID;
+      if (!resolver->get_label_scope ().lookup (
+	    CanonicalPath::new_seg (label.get_node_id (),
+				    label.get_lifetime_name ()),
+	    &resolved_node))
+	{
+	  rust_error_at (expr.get_label ().get_locus (),
+			 "failed to resolve label");
+	  return;
+	}
+      resolver->insert_resolved_label (label.get_node_id (), resolved_node);
+    }
+
+  if (expr.has_break_expr ())
+    ResolveExpr::go (expr.get_break_expr ().get (), prefix, canonical_prefix);
+}
+
+void
+ResolveExpr::visit (AST::WhileLoopExpr &expr)
+{
+  if (expr.has_loop_label ())
+    {
+      auto label = expr.get_loop_label ();
+      if (label.get_lifetime ().get_lifetime_type ()
+	  != AST::Lifetime::LifetimeType::NAMED)
+	{
+	  rust_error_at (label.get_locus (),
+			 "Labels must be a named lifetime value");
+	  return;
+	}
+
+      auto label_name = label.get_lifetime ().get_lifetime_name ();
+      auto label_lifetime_node_id = label.get_lifetime ().get_node_id ();
+      resolver->get_label_scope ().insert (
+	CanonicalPath::new_seg (label.get_node_id (), label_name),
+	label_lifetime_node_id, label.get_locus (), false,
+	[&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	  rust_error_at (label.get_locus (), "label redefined multiple times");
+	  rust_error_at (locus, "was defined here");
+	});
+    }
+
+  ResolveExpr::go (expr.get_predicate_expr ().get (), prefix, canonical_prefix);
+  ResolveExpr::go (expr.get_loop_block ().get (), prefix, canonical_prefix);
+}
+
+void
+ResolveExpr::visit (AST::ForLoopExpr &expr)
+{
+  if (expr.has_loop_label ())
+    {
+      auto label = expr.get_loop_label ();
+      if (label.get_lifetime ().get_lifetime_type ()
+	  != AST::Lifetime::LifetimeType::NAMED)
+	{
+	  rust_error_at (label.get_locus (),
+			 "Labels must be a named lifetime value");
+	  return;
+	}
+
+      auto label_name = label.get_lifetime ().get_lifetime_name ();
+      auto label_lifetime_node_id = label.get_lifetime ().get_node_id ();
+      resolver->get_label_scope ().insert (
+	CanonicalPath::new_seg (label.get_node_id (), label_name),
+	label_lifetime_node_id, label.get_locus (), false,
+	[&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	  rust_error_at (label.get_locus (), "label redefined multiple times");
+	  rust_error_at (locus, "was defined here");
+	});
+    }
+
+  // this needs a new rib to contain the pattern
+  NodeId scope_node_id = expr.get_node_id ();
+  resolver->get_name_scope ().push (scope_node_id);
+  resolver->get_type_scope ().push (scope_node_id);
+  resolver->get_label_scope ().push (scope_node_id);
+  resolver->push_new_name_rib (resolver->get_name_scope ().peek ());
+  resolver->push_new_type_rib (resolver->get_type_scope ().peek ());
+  resolver->push_new_label_rib (resolver->get_type_scope ().peek ());
+
+  // resolve the expression
+  PatternDeclaration::go (expr.get_pattern ().get ());
+  ResolveExpr::go (expr.get_iterator_expr ().get (), prefix, canonical_prefix);
+  ResolveExpr::go (expr.get_loop_block ().get (), prefix, canonical_prefix);
+
+  // done
+  resolver->get_name_scope ().pop ();
+  resolver->get_type_scope ().pop ();
+  resolver->get_label_scope ().pop ();
+}
+
+void
+ResolveExpr::visit (AST::ContinueExpr &expr)
+{
+  if (expr.has_label ())
+    {
+      auto label = expr.get_label ();
+      if (label.get_lifetime_type () != AST::Lifetime::LifetimeType::NAMED)
+	{
+	  rust_error_at (label.get_locus (),
+			 "Labels must be a named lifetime value");
+	  return;
+	}
+
+      NodeId resolved_node = UNKNOWN_NODEID;
+      if (!resolver->get_label_scope ().lookup (
+	    CanonicalPath::new_seg (label.get_node_id (),
+				    label.get_lifetime_name ()),
+	    &resolved_node))
+	{
+	  rust_error_at (expr.get_label ().get_locus (),
+			 "failed to resolve label");
+	  return;
+	}
+      resolver->insert_resolved_label (label.get_node_id (), resolved_node);
+    }
+}
+
+void
+ResolveExpr::visit (AST::BorrowExpr &expr)
+{
+  ResolveExpr::go (expr.get_borrowed_expr ().get (), prefix, canonical_prefix);
+}
+
+void
+ResolveExpr::visit (AST::DereferenceExpr &expr)
+{
+  ResolveExpr::go (expr.get_dereferenced_expr ().get (), prefix,
+		   canonical_prefix);
+}
+
+void
+ResolveExpr::visit (AST::MatchExpr &expr)
+{
+  ResolveExpr::go (expr.get_scrutinee_expr ().get (), prefix, canonical_prefix);
+  for (auto &match_case : expr.get_match_cases ())
+    {
+      // each arm is in its own scope
+      NodeId scope_node_id = match_case.get_node_id ();
+      resolver->get_name_scope ().push (scope_node_id);
+      resolver->get_type_scope ().push (scope_node_id);
+      resolver->get_label_scope ().push (scope_node_id);
+      resolver->push_new_name_rib (resolver->get_name_scope ().peek ());
+      resolver->push_new_type_rib (resolver->get_type_scope ().peek ());
+      resolver->push_new_label_rib (resolver->get_type_scope ().peek ());
+
+      // resolve
+      AST::MatchArm &arm = match_case.get_arm ();
+      if (arm.has_match_arm_guard ())
+	ResolveExpr::go (arm.get_guard_expr ().get (), prefix,
+			 canonical_prefix);
+
+      // insert any possible new patterns
+      for (auto &pattern : arm.get_patterns ())
+	{
+	  PatternDeclaration::go (pattern.get ());
+	}
+
+      // resolve the body
+      ResolveExpr::go (match_case.get_expr ().get (), prefix, canonical_prefix);
+
+      // done
+      resolver->get_name_scope ().pop ();
+      resolver->get_type_scope ().pop ();
+      resolver->get_label_scope ().pop ();
+    }
+}
+
+void
+ResolveExpr::visit (AST::RangeFromToExpr &expr)
+{
+  ResolveExpr::go (expr.get_from_expr ().get (), prefix, canonical_prefix);
+  ResolveExpr::go (expr.get_to_expr ().get (), prefix, canonical_prefix);
+}
+
+void
+ResolveExpr::visit (AST::RangeFromExpr &expr)
+{
+  ResolveExpr::go (expr.get_from_expr ().get (), prefix, canonical_prefix);
+}
+
+void
+ResolveExpr::visit (AST::RangeToExpr &expr)
+{
+  ResolveExpr::go (expr.get_to_expr ().get (), prefix, canonical_prefix);
+}
+
+void
+ResolveExpr::visit (AST::RangeFullExpr &expr)
+{
+  // nothing to do
+}
+
+void
+ResolveExpr::visit (AST::RangeFromToInclExpr &expr)
+{
+  ResolveExpr::go (expr.get_from_expr ().get (), prefix, canonical_prefix);
+  ResolveExpr::go (expr.get_to_expr ().get (), prefix, canonical_prefix);
+}
+
+ResolveExpr::ResolveExpr (const CanonicalPath &prefix,
+			  const CanonicalPath &canonical_prefix)
+  : ResolverBase (), prefix (prefix), canonical_prefix (canonical_prefix)
+{}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/resolve/rust-ast-resolve-expr.h b/gcc/rust/resolve/rust-ast-resolve-expr.h
new file mode 100644
index 00000000000..11a846ac8cd
--- /dev/null
+++ b/gcc/rust/resolve/rust-ast-resolve-expr.h
@@ -0,0 +1,133 @@
+// 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_AST_RESOLVE_EXPR_H
+#define RUST_AST_RESOLVE_EXPR_H
+
+#include "rust-ast-resolve-base.h"
+#include "rust-ast-full.h"
+
+namespace Rust {
+namespace Resolver {
+
+class ResolveExpr : public ResolverBase
+{
+  using Rust::Resolver::ResolverBase::visit;
+
+public:
+  static void go (AST::Expr *expr, const CanonicalPath &prefix,
+		  const CanonicalPath &canonical_prefix);
+
+  void visit (AST::TupleIndexExpr &expr) override;
+
+  void visit (AST::TupleExpr &expr) override;
+
+  void visit (AST::PathInExpression &expr) override;
+
+  void visit (AST::QualifiedPathInExpression &expr) override;
+
+  void visit (AST::ReturnExpr &expr) override;
+
+  void visit (AST::CallExpr &expr) override;
+
+  void visit (AST::MethodCallExpr &expr) override;
+
+  void visit (AST::AssignmentExpr &expr) override;
+
+  void visit (AST::IdentifierExpr &expr) override;
+
+  void visit (AST::ArithmeticOrLogicalExpr &expr) override;
+
+  void visit (AST::CompoundAssignmentExpr &expr) override;
+
+  void visit (AST::ComparisonExpr &expr) override;
+
+  void visit (AST::LazyBooleanExpr &expr) override;
+
+  void visit (AST::NegationExpr &expr) override;
+
+  void visit (AST::TypeCastExpr &expr) override;
+
+  void visit (AST::IfExpr &expr) override;
+
+  void visit (AST::IfExprConseqElse &expr) override;
+
+  void visit (AST::IfExprConseqIf &expr) override;
+
+  void visit (AST::IfLetExpr &expr) override;
+
+  void visit (AST::BlockExpr &expr) override;
+
+  void visit (AST::UnsafeBlockExpr &expr) override;
+
+  void visit (AST::ArrayElemsValues &elems) override;
+
+  void visit (AST::ArrayExpr &expr) override;
+
+  void visit (AST::ArrayIndexExpr &expr) override;
+
+  void visit (AST::ArrayElemsCopied &elems) override;
+
+  // this this an empty struct constructor like 'S {}'
+  void visit (AST::StructExprStruct &struct_expr) override;
+
+  // this this a struct constructor with fields
+  void visit (AST::StructExprStructFields &struct_expr) override;
+
+  void visit (AST::GroupedExpr &expr) override;
+
+  void visit (AST::FieldAccessExpr &expr) override;
+
+  void visit (AST::LoopExpr &expr) override;
+
+  void visit (AST::BreakExpr &expr) override;
+
+  void visit (AST::WhileLoopExpr &expr) override;
+
+  void visit (AST::ForLoopExpr &expr) override;
+
+  void visit (AST::ContinueExpr &expr) override;
+
+  void visit (AST::BorrowExpr &expr) override;
+
+  void visit (AST::DereferenceExpr &expr) override;
+
+  void visit (AST::MatchExpr &expr) override;
+
+  void visit (AST::RangeFromToExpr &expr) override;
+
+  void visit (AST::RangeFromExpr &expr) override;
+
+  void visit (AST::RangeToExpr &expr) override;
+
+  void visit (AST::RangeFullExpr &expr) override;
+
+  void visit (AST::RangeFromToInclExpr &expr) override;
+
+private:
+  ResolveExpr (const CanonicalPath &prefix,
+	       const CanonicalPath &canonical_prefix);
+
+  const CanonicalPath &prefix;
+  const CanonicalPath &canonical_prefix;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_AST_RESOLVE_EXPR_H
diff --git a/gcc/rust/resolve/rust-ast-resolve-implitem.h b/gcc/rust/resolve/rust-ast-resolve-implitem.h
new file mode 100644
index 00000000000..29dbe3436f5
--- /dev/null
+++ b/gcc/rust/resolve/rust-ast-resolve-implitem.h
@@ -0,0 +1,275 @@
+// 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_AST_RESOLVE_IMPLITEM_H
+#define RUST_AST_RESOLVE_IMPLITEM_H
+
+#include "rust-ast-resolve-base.h"
+#include "rust-ast-resolve-type.h"
+#include "rust-ast-full.h"
+
+namespace Rust {
+namespace Resolver {
+
+class ResolveToplevelImplItem : public ResolverBase
+{
+  using Rust::Resolver::ResolverBase::visit;
+
+public:
+  static void go (AST::InherentImplItem *item, const CanonicalPath &prefix)
+  {
+    if (item->is_marked_for_strip ())
+      return;
+
+    ResolveToplevelImplItem resolver (prefix);
+    item->accept_vis (resolver);
+  }
+
+  static void go (AST::TraitImplItem *item, const CanonicalPath &prefix)
+  {
+    if (item->is_marked_for_strip ())
+      return;
+
+    ResolveToplevelImplItem resolver (prefix);
+    item->accept_vis (resolver);
+  }
+
+  void visit (AST::TypeAlias &type) override
+  {
+    auto decl
+      = CanonicalPath::new_seg (type.get_node_id (), type.get_new_type_name ());
+    auto path = prefix.append (decl);
+
+    resolver->get_type_scope ().insert (
+      path, type.get_node_id (), type.get_locus (), false,
+      [&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	RichLocation r (type.get_locus ());
+	r.add_range (locus);
+	rust_error_at (r, "redefined multiple times");
+      });
+  }
+
+  void visit (AST::ConstantItem &constant) override
+  {
+    auto decl = CanonicalPath::new_seg (constant.get_node_id (),
+					constant.get_identifier ());
+    auto path = prefix.append (decl);
+
+    resolver->get_name_scope ().insert (
+      path, constant.get_node_id (), constant.get_locus (), false,
+      [&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	RichLocation r (constant.get_locus ());
+	r.add_range (locus);
+	rust_error_at (r, "redefined multiple times");
+      });
+  }
+
+  void visit (AST::Function &function) override
+  {
+    auto decl = CanonicalPath::new_seg (function.get_node_id (),
+					function.get_function_name ());
+    auto path = prefix.append (decl);
+
+    resolver->get_name_scope ().insert (
+      path, function.get_node_id (), function.get_locus (), false,
+      [&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	RichLocation r (function.get_locus ());
+	r.add_range (locus);
+	rust_error_at (r, "redefined multiple times");
+      });
+  }
+
+  void visit (AST::Method &method) override
+  {
+    auto decl = CanonicalPath::new_seg (method.get_node_id (),
+					method.get_method_name ());
+    auto path = prefix.append (decl);
+
+    resolver->get_name_scope ().insert (
+      path, method.get_node_id (), method.get_locus (), false,
+      [&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	RichLocation r (method.get_locus ());
+	r.add_range (locus);
+	rust_error_at (r, "redefined multiple times");
+      });
+  }
+
+private:
+  ResolveToplevelImplItem (const CanonicalPath &prefix)
+    : ResolverBase (), prefix (prefix)
+  {
+    rust_assert (!prefix.is_empty ());
+  }
+
+  const CanonicalPath &prefix;
+};
+
+class ResolveTopLevelTraitItems : public ResolverBase
+{
+  using Rust::Resolver::ResolverBase::visit;
+
+public:
+  static void go (AST::TraitItem *item, const CanonicalPath &prefix,
+		  const CanonicalPath &canonical_prefix)
+  {
+    ResolveTopLevelTraitItems resolver (prefix, canonical_prefix);
+    item->accept_vis (resolver);
+  };
+
+  void visit (AST::TraitItemFunc &function) override
+  {
+    auto decl = CanonicalPath::new_seg (
+      function.get_node_id (),
+      function.get_trait_function_decl ().get_identifier ());
+    auto path = prefix.append (decl);
+    auto cpath = canonical_prefix.append (decl);
+
+    resolver->get_name_scope ().insert (
+      path, function.get_node_id (), function.get_locus (), false,
+      [&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	RichLocation r (function.get_locus ());
+	r.add_range (locus);
+	rust_error_at (r, "redefined multiple times");
+      });
+
+    mappings->insert_canonical_path (function.get_node_id (), cpath);
+  }
+
+  void visit (AST::TraitItemMethod &method) override
+  {
+    auto decl = CanonicalPath::new_seg (
+      method.get_node_id (), method.get_trait_method_decl ().get_identifier ());
+    auto path = prefix.append (decl);
+    auto cpath = canonical_prefix.append (decl);
+
+    resolver->get_name_scope ().insert (
+      path, method.get_node_id (), method.get_locus (), false,
+      [&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	RichLocation r (method.get_locus ());
+	r.add_range (locus);
+	rust_error_at (r, "redefined multiple times");
+      });
+
+    mappings->insert_canonical_path (method.get_node_id (), cpath);
+  }
+
+  void visit (AST::TraitItemConst &constant) override
+  {
+    auto decl = CanonicalPath::new_seg (constant.get_node_id (),
+					constant.get_identifier ());
+    auto path = prefix.append (decl);
+    auto cpath = canonical_prefix.append (decl);
+
+    resolver->get_name_scope ().insert (
+      path, constant.get_node_id (), constant.get_locus (), false,
+      [&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	RichLocation r (constant.get_locus ());
+	r.add_range (locus);
+	rust_error_at (r, "redefined multiple times");
+      });
+
+    mappings->insert_canonical_path (constant.get_node_id (), cpath);
+  }
+
+  void visit (AST::TraitItemType &type) override
+  {
+    auto decl
+      = CanonicalPath::new_seg (type.get_node_id (), type.get_identifier ());
+    auto path = prefix.append (decl);
+    auto cpath = canonical_prefix.append (decl);
+
+    resolver->get_type_scope ().insert (
+      path, type.get_node_id (), type.get_locus (), false,
+      [&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	RichLocation r (type.get_locus ());
+	r.add_range (locus);
+	rust_error_at (r, "redefined multiple times");
+      });
+
+    mappings->insert_canonical_path (type.get_node_id (), cpath);
+  }
+
+private:
+  ResolveTopLevelTraitItems (const CanonicalPath &prefix,
+			     const CanonicalPath &canonical_prefix)
+    : ResolverBase (), prefix (prefix), canonical_prefix (canonical_prefix)
+  {}
+
+  const CanonicalPath &prefix;
+  const CanonicalPath &canonical_prefix;
+};
+
+class ResolveToplevelExternItem : public ResolverBase
+{
+  using Rust::Resolver::ResolverBase::visit;
+
+public:
+  static void go (AST::ExternalItem *item, const CanonicalPath &prefix)
+  {
+    ResolveToplevelExternItem resolver (prefix);
+    item->accept_vis (resolver);
+  };
+
+  void visit (AST::ExternalFunctionItem &function) override
+  {
+    auto decl = CanonicalPath::new_seg (function.get_node_id (),
+					function.get_identifier ());
+    auto path = prefix.append (decl);
+
+    resolver->get_name_scope ().insert (
+      path, function.get_node_id (), function.get_locus (), false,
+      [&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	RichLocation r (function.get_locus ());
+	r.add_range (locus);
+	rust_error_at (r, "redefined multiple times");
+      });
+
+    NodeId current_module = resolver->peek_current_module_scope ();
+    mappings->insert_module_child_item (current_module, decl);
+  }
+
+  void visit (AST::ExternalStaticItem &item) override
+  {
+    auto decl
+      = CanonicalPath::new_seg (item.get_node_id (), item.get_identifier ());
+    auto path = prefix.append (decl);
+
+    resolver->get_name_scope ().insert (
+      path, item.get_node_id (), item.get_locus (), false,
+      [&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	RichLocation r (item.get_locus ());
+	r.add_range (locus);
+	rust_error_at (r, "redefined multiple times");
+      });
+
+    NodeId current_module = resolver->peek_current_module_scope ();
+    mappings->insert_module_child_item (current_module, decl);
+  }
+
+private:
+  ResolveToplevelExternItem (const CanonicalPath &prefix)
+    : ResolverBase (), prefix (prefix)
+  {}
+
+  const CanonicalPath &prefix;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_AST_RESOLVE_IMPLITEM_H
diff --git a/gcc/rust/resolve/rust-ast-resolve-item.cc b/gcc/rust/resolve/rust-ast-resolve-item.cc
new file mode 100644
index 00000000000..0c38f28d530
--- /dev/null
+++ b/gcc/rust/resolve/rust-ast-resolve-item.cc
@@ -0,0 +1,1237 @@
+// 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/>.
+
+#include "rust-ast-resolve-item.h"
+#include "rust-ast-resolve-path.h"
+#include "selftest.h"
+
+namespace Rust {
+namespace Resolver {
+
+ResolveTraitItems::ResolveTraitItems (const CanonicalPath &prefix,
+				      const CanonicalPath &canonical_prefix)
+  : ResolverBase (), prefix (prefix), canonical_prefix (canonical_prefix)
+{}
+
+void
+ResolveTraitItems::go (AST::TraitItem *item, const CanonicalPath &prefix,
+		       const CanonicalPath &canonical_prefix)
+{
+  if (item->is_marked_for_strip ())
+    return;
+
+  ResolveTraitItems resolver (prefix, canonical_prefix);
+  item->accept_vis (resolver);
+}
+
+void
+ResolveTraitItems::visit (AST::TraitItemType &type)
+{
+  auto decl
+    = CanonicalPath::new_seg (type.get_node_id (), type.get_identifier ());
+  auto path = prefix.append (decl);
+  auto cpath = canonical_prefix.append (decl);
+  mappings->insert_canonical_path (type.get_node_id (), cpath);
+
+  for (auto &bound : type.get_type_param_bounds ())
+    ResolveTypeBound::go (bound.get ());
+}
+
+void
+ResolveTraitItems::visit (AST::TraitItemFunc &func)
+{
+  auto decl = CanonicalPath::new_seg (
+    func.get_node_id (), func.get_trait_function_decl ().get_identifier ());
+  auto path = prefix.append (decl);
+  auto cpath = canonical_prefix.append (decl);
+  mappings->insert_canonical_path (func.get_node_id (), cpath);
+
+  NodeId scope_node_id = func.get_node_id ();
+  resolver->get_name_scope ().push (scope_node_id);
+  resolver->get_type_scope ().push (scope_node_id);
+  resolver->get_label_scope ().push (scope_node_id);
+  resolver->push_new_name_rib (resolver->get_name_scope ().peek ());
+  resolver->push_new_type_rib (resolver->get_type_scope ().peek ());
+  resolver->push_new_label_rib (resolver->get_type_scope ().peek ());
+
+  AST::TraitFunctionDecl &function = func.get_trait_function_decl ();
+  if (function.has_generics ())
+    for (auto &generic : function.get_generic_params ())
+      ResolveGenericParam::go (generic.get (), prefix, canonical_prefix);
+
+  if (function.has_return_type ())
+    ResolveType::go (function.get_return_type ().get ());
+
+  // we make a new scope so the names of parameters are resolved and shadowed
+  // correctly
+  for (auto &param : function.get_function_params ())
+    {
+      ResolveType::go (param.get_type ().get ());
+      PatternDeclaration::go (param.get_pattern ().get ());
+    }
+
+  if (function.has_where_clause ())
+    ResolveWhereClause::Resolve (function.get_where_clause ());
+
+  // trait items have an optional body
+  if (func.has_definition ())
+    ResolveExpr::go (func.get_definition ().get (), path, cpath);
+
+  resolver->get_name_scope ().pop ();
+  resolver->get_type_scope ().pop ();
+  resolver->get_label_scope ().pop ();
+}
+
+void
+ResolveTraitItems::visit (AST::TraitItemMethod &func)
+{
+  auto decl
+    = CanonicalPath::new_seg (func.get_node_id (),
+			      func.get_trait_method_decl ().get_identifier ());
+  auto path = prefix.append (decl);
+  auto cpath = canonical_prefix.append (decl);
+  mappings->insert_canonical_path (func.get_node_id (), cpath);
+
+  NodeId scope_node_id = func.get_node_id ();
+  resolver->get_name_scope ().push (scope_node_id);
+  resolver->get_type_scope ().push (scope_node_id);
+  resolver->get_label_scope ().push (scope_node_id);
+  resolver->push_new_name_rib (resolver->get_name_scope ().peek ());
+  resolver->push_new_type_rib (resolver->get_type_scope ().peek ());
+  resolver->push_new_label_rib (resolver->get_type_scope ().peek ());
+
+  AST::TraitMethodDecl &function = func.get_trait_method_decl ();
+  if (function.has_generics ())
+    for (auto &generic : function.get_generic_params ())
+      ResolveGenericParam::go (generic.get (), prefix, canonical_prefix);
+
+  if (function.has_return_type ())
+    ResolveType::go (function.get_return_type ().get ());
+
+  // self turns into (self: Self) as a function param
+  AST::SelfParam &self_param = function.get_self_param ();
+  AST::IdentifierPattern self_pattern (self_param.get_node_id (), "self",
+				       self_param.get_locus (),
+				       self_param.get_has_ref (),
+				       self_param.get_is_mut (),
+				       std::unique_ptr<AST::Pattern> (nullptr));
+
+  std::vector<std::unique_ptr<AST::TypePathSegment>> segments;
+  segments.push_back (std::unique_ptr<AST::TypePathSegment> (
+    new AST::TypePathSegment ("Self", false, self_param.get_locus ())));
+
+  AST::TypePath self_type_path (std::move (segments), self_param.get_locus ());
+
+  ResolveType::go (&self_type_path);
+  PatternDeclaration::go (&self_pattern);
+
+  // we make a new scope so the names of parameters are resolved and shadowed
+  // correctly
+  for (auto &param : function.get_function_params ())
+    {
+      ResolveType::go (param.get_type ().get ());
+      PatternDeclaration::go (param.get_pattern ().get ());
+    }
+
+  if (function.has_where_clause ())
+    ResolveWhereClause::Resolve (function.get_where_clause ());
+
+  // trait items have an optional body
+  if (func.has_definition ())
+    ResolveExpr::go (func.get_definition ().get (), path, cpath);
+
+  resolver->get_name_scope ().pop ();
+  resolver->get_type_scope ().pop ();
+  resolver->get_label_scope ().pop ();
+}
+
+void
+ResolveTraitItems::visit (AST::TraitItemConst &constant)
+{
+  auto decl = CanonicalPath::new_seg (constant.get_node_id (),
+				      constant.get_identifier ());
+  auto path = prefix.append (decl);
+  auto cpath = canonical_prefix.append (decl);
+  mappings->insert_canonical_path (constant.get_node_id (), cpath);
+
+  ResolveType::go (constant.get_type ().get ());
+
+  if (constant.has_expr ())
+    ResolveExpr::go (constant.get_expr ().get (), path, cpath);
+}
+
+ResolveItem::ResolveItem (const CanonicalPath &prefix,
+			  const CanonicalPath &canonical_prefix)
+  : ResolverBase (), prefix (prefix), canonical_prefix (canonical_prefix)
+{}
+
+void
+ResolveItem::go (AST::Item *item, const CanonicalPath &prefix,
+		 const CanonicalPath &canonical_prefix)
+{
+  ResolveItem resolver (prefix, canonical_prefix);
+  item->accept_vis (resolver);
+}
+
+void
+ResolveItem::visit (AST::TypeAlias &alias)
+{
+  auto talias
+    = CanonicalPath::new_seg (alias.get_node_id (), alias.get_new_type_name ());
+  auto path = prefix.append (talias);
+  auto cpath = canonical_prefix.append (talias);
+  mappings->insert_canonical_path (alias.get_node_id (), cpath);
+
+  NodeId scope_node_id = alias.get_node_id ();
+  resolver->get_type_scope ().push (scope_node_id);
+
+  if (alias.has_generics ())
+    for (auto &generic : alias.get_generic_params ())
+      ResolveGenericParam::go (generic.get (), prefix, canonical_prefix);
+
+  if (alias.has_where_clause ())
+    ResolveWhereClause::Resolve (alias.get_where_clause ());
+
+  ResolveType::go (alias.get_type_aliased ().get ());
+
+  resolver->get_type_scope ().pop ();
+}
+
+void
+ResolveItem::visit (AST::Module &module)
+{
+  auto mod = CanonicalPath::new_seg (module.get_node_id (), module.get_name ());
+  auto path = prefix.append (mod);
+  auto cpath = canonical_prefix.append (mod);
+  mappings->insert_canonical_path (module.get_node_id (), cpath);
+
+  resolve_visibility (module.get_visibility ());
+
+  NodeId scope_node_id = module.get_node_id ();
+  resolver->get_name_scope ().push (scope_node_id);
+  resolver->get_type_scope ().push (scope_node_id);
+  resolver->get_label_scope ().push (scope_node_id);
+  resolver->push_new_name_rib (resolver->get_name_scope ().peek ());
+  resolver->push_new_type_rib (resolver->get_type_scope ().peek ());
+  resolver->push_new_label_rib (resolver->get_type_scope ().peek ());
+
+  // FIXME: Should we reinsert a child here? Any reason we ResolveTopLevel::go
+  // in ResolveTopLevel::visit (AST::Module) as well as here?
+  for (auto &item : module.get_items ())
+    ResolveTopLevel::go (item.get (), CanonicalPath::create_empty (), cpath);
+
+  resolver->push_new_module_scope (module.get_node_id ());
+  for (auto &item : module.get_items ())
+    ResolveItem::go (item.get (), path, cpath);
+
+  resolver->pop_module_scope ();
+
+  resolver->get_name_scope ().pop ();
+  resolver->get_type_scope ().pop ();
+  resolver->get_label_scope ().pop ();
+}
+
+void
+ResolveItem::visit (AST::TupleStruct &struct_decl)
+{
+  auto decl = CanonicalPath::new_seg (struct_decl.get_node_id (),
+				      struct_decl.get_identifier ());
+  auto path = prefix.append (decl);
+  auto cpath = canonical_prefix.append (decl);
+  mappings->insert_canonical_path (struct_decl.get_node_id (), cpath);
+
+  resolve_visibility (struct_decl.get_visibility ());
+
+  NodeId scope_node_id = struct_decl.get_node_id ();
+  resolver->get_type_scope ().push (scope_node_id);
+
+  if (struct_decl.has_generics ())
+    for (auto &generic : struct_decl.get_generic_params ())
+      ResolveGenericParam::go (generic.get (), prefix, canonical_prefix);
+
+  if (struct_decl.has_where_clause ())
+    ResolveWhereClause::Resolve (struct_decl.get_where_clause ());
+
+  for (AST::TupleField &field : struct_decl.get_fields ())
+    {
+      if (field.get_field_type ()->is_marked_for_strip ())
+	continue;
+
+      resolve_visibility (field.get_visibility ());
+
+      ResolveType::go (field.get_field_type ().get ());
+    }
+
+  resolver->get_type_scope ().pop ();
+}
+
+void
+ResolveItem::visit (AST::Enum &enum_decl)
+{
+  auto decl = CanonicalPath::new_seg (enum_decl.get_node_id (),
+				      enum_decl.get_identifier ());
+  auto path = prefix.append (decl);
+  auto cpath = canonical_prefix.append (decl);
+  mappings->insert_canonical_path (enum_decl.get_node_id (), cpath);
+
+  resolve_visibility (enum_decl.get_visibility ());
+
+  NodeId scope_node_id = enum_decl.get_node_id ();
+  resolver->get_type_scope ().push (scope_node_id);
+
+  if (enum_decl.has_generics ())
+    for (auto &generic : enum_decl.get_generic_params ())
+      ResolveGenericParam::go (generic.get (), prefix, cpath);
+
+  if (enum_decl.has_where_clause ())
+    ResolveWhereClause::Resolve (enum_decl.get_where_clause ());
+
+  /* The actual fields are inside the variants.  */
+  for (auto &variant : enum_decl.get_variants ())
+    ResolveItem::go (variant.get (), path, cpath);
+
+  resolver->get_type_scope ().pop ();
+}
+
+/* EnumItem doesn't need to be handled, no fields.  */
+void
+ResolveItem::visit (AST::EnumItem &item)
+{
+  // Since at this point we cannot have visibilities on enum items anymore, we
+  // can skip handling them
+
+  auto decl
+    = CanonicalPath::new_seg (item.get_node_id (), item.get_identifier ());
+  auto path = prefix.append (decl);
+  auto cpath = canonical_prefix.append (decl);
+  mappings->insert_canonical_path (item.get_node_id (), cpath);
+}
+
+void
+ResolveItem::visit (AST::EnumItemTuple &item)
+{
+  auto decl
+    = CanonicalPath::new_seg (item.get_node_id (), item.get_identifier ());
+  auto path = prefix.append (decl);
+  auto cpath = canonical_prefix.append (decl);
+  mappings->insert_canonical_path (item.get_node_id (), cpath);
+
+  for (auto &field : item.get_tuple_fields ())
+    {
+      if (field.get_field_type ()->is_marked_for_strip ())
+	continue;
+
+      ResolveType::go (field.get_field_type ().get ());
+    }
+}
+
+void
+ResolveItem::visit (AST::EnumItemStruct &item)
+{
+  auto decl
+    = CanonicalPath::new_seg (item.get_node_id (), item.get_identifier ());
+  auto path = prefix.append (decl);
+  auto cpath = canonical_prefix.append (decl);
+  mappings->insert_canonical_path (item.get_node_id (), cpath);
+
+  for (auto &field : item.get_struct_fields ())
+    {
+      if (field.get_field_type ()->is_marked_for_strip ())
+	continue;
+
+      ResolveType::go (field.get_field_type ().get ());
+    }
+}
+
+void
+ResolveItem::visit (AST::EnumItemDiscriminant &item)
+{
+  auto decl
+    = CanonicalPath::new_seg (item.get_node_id (), item.get_identifier ());
+  auto path = prefix.append (decl);
+  auto cpath = canonical_prefix.append (decl);
+
+  mappings->insert_canonical_path (item.get_node_id (), cpath);
+}
+
+void
+ResolveItem::visit (AST::StructStruct &struct_decl)
+{
+  auto decl = CanonicalPath::new_seg (struct_decl.get_node_id (),
+				      struct_decl.get_identifier ());
+  auto path = prefix.append (decl);
+  auto cpath = canonical_prefix.append (decl);
+  mappings->insert_canonical_path (struct_decl.get_node_id (), cpath);
+
+  resolve_visibility (struct_decl.get_visibility ());
+
+  NodeId scope_node_id = struct_decl.get_node_id ();
+  resolver->get_type_scope ().push (scope_node_id);
+
+  if (struct_decl.has_generics ())
+    for (auto &generic : struct_decl.get_generic_params ())
+      ResolveGenericParam::go (generic.get (), prefix, canonical_prefix);
+
+  if (struct_decl.has_where_clause ())
+    ResolveWhereClause::Resolve (struct_decl.get_where_clause ());
+
+  for (AST::StructField &field : struct_decl.get_fields ())
+    {
+      if (field.get_field_type ()->is_marked_for_strip ())
+	continue;
+
+      resolve_visibility (field.get_visibility ());
+
+      ResolveType::go (field.get_field_type ().get ());
+    }
+
+  resolver->get_type_scope ().pop ();
+}
+
+void
+ResolveItem::visit (AST::Union &union_decl)
+{
+  auto decl = CanonicalPath::new_seg (union_decl.get_node_id (),
+				      union_decl.get_identifier ());
+  auto path = prefix.append (decl);
+  auto cpath = canonical_prefix.append (decl);
+  mappings->insert_canonical_path (union_decl.get_node_id (), cpath);
+
+  resolve_visibility (union_decl.get_visibility ());
+
+  NodeId scope_node_id = union_decl.get_node_id ();
+  resolver->get_type_scope ().push (scope_node_id);
+
+  if (union_decl.has_generics ())
+    for (auto &generic : union_decl.get_generic_params ())
+      ResolveGenericParam::go (generic.get (), prefix, canonical_prefix);
+
+  if (union_decl.has_where_clause ())
+    ResolveWhereClause::Resolve (union_decl.get_where_clause ());
+
+  for (AST::StructField &field : union_decl.get_variants ())
+    {
+      if (field.get_field_type ()->is_marked_for_strip ())
+	continue;
+
+      ResolveType::go (field.get_field_type ().get ());
+    }
+
+  resolver->get_type_scope ().pop ();
+}
+
+void
+ResolveItem::visit (AST::StaticItem &var)
+{
+  auto decl
+    = CanonicalPath::new_seg (var.get_node_id (), var.get_identifier ());
+  auto path = prefix.append (decl);
+  auto cpath = canonical_prefix.append (decl);
+  mappings->insert_canonical_path (var.get_node_id (), cpath);
+
+  ResolveType::go (var.get_type ().get ());
+  ResolveExpr::go (var.get_expr ().get (), path, cpath);
+}
+
+void
+ResolveItem::visit (AST::ConstantItem &constant)
+{
+  auto decl = CanonicalPath::new_seg (constant.get_node_id (),
+				      constant.get_identifier ());
+  auto path = prefix.append (decl);
+  auto cpath = canonical_prefix.append (decl);
+  mappings->insert_canonical_path (constant.get_node_id (), cpath);
+
+  resolve_visibility (constant.get_visibility ());
+
+  ResolveType::go (constant.get_type ().get ());
+  ResolveExpr::go (constant.get_expr ().get (), path, cpath);
+}
+
+void
+ResolveItem::visit (AST::Function &function)
+{
+  auto decl = CanonicalPath::new_seg (function.get_node_id (),
+				      function.get_function_name ());
+  auto path = prefix.append (decl);
+  auto cpath = canonical_prefix.append (decl);
+
+  mappings->insert_canonical_path (function.get_node_id (), cpath);
+
+  resolve_visibility (function.get_visibility ());
+
+  NodeId scope_node_id = function.get_node_id ();
+  resolver->get_name_scope ().push (scope_node_id);
+  resolver->get_type_scope ().push (scope_node_id);
+  resolver->get_label_scope ().push (scope_node_id);
+  resolver->push_new_name_rib (resolver->get_name_scope ().peek ());
+  resolver->push_new_type_rib (resolver->get_type_scope ().peek ());
+  resolver->push_new_label_rib (resolver->get_type_scope ().peek ());
+
+  if (function.has_generics ())
+    for (auto &generic : function.get_generic_params ())
+      ResolveGenericParam::go (generic.get (), prefix, canonical_prefix);
+
+  // resolve any where clause items
+  if (function.has_where_clause ())
+    ResolveWhereClause::Resolve (function.get_where_clause ());
+
+  if (function.has_return_type ())
+    ResolveType::go (function.get_return_type ().get ());
+
+  // we make a new scope so the names of parameters are resolved and shadowed
+  // correctly
+  for (auto &param : function.get_function_params ())
+    {
+      ResolveType::go (param.get_type ().get ());
+      PatternDeclaration::go (param.get_pattern ().get ());
+
+      // the mutability checker needs to verify for immutable decls the number
+      // of assignments are <1. This marks an implicit assignment
+    }
+
+  // resolve the function body
+  ResolveExpr::go (function.get_definition ().get (), path, cpath);
+
+  resolver->get_name_scope ().pop ();
+  resolver->get_type_scope ().pop ();
+  resolver->get_label_scope ().pop ();
+}
+
+void
+ResolveItem::visit (AST::InherentImpl &impl_block)
+{
+  NodeId scope_node_id = impl_block.get_node_id ();
+  resolver->get_name_scope ().push (scope_node_id);
+  resolver->get_type_scope ().push (scope_node_id);
+  resolver->push_new_name_rib (resolver->get_name_scope ().peek ());
+  resolver->push_new_type_rib (resolver->get_type_scope ().peek ());
+
+  resolve_visibility (impl_block.get_visibility ());
+
+  if (impl_block.has_generics ())
+    for (auto &generic : impl_block.get_generic_params ())
+      ResolveGenericParam::go (generic.get (), prefix, canonical_prefix);
+
+  // resolve any where clause items
+  if (impl_block.has_where_clause ())
+    ResolveWhereClause::Resolve (impl_block.get_where_clause ());
+
+  // FIXME this needs to be protected behind nominal type-checks see:
+  // rustc --explain E0118
+  ResolveType::go (impl_block.get_type ().get ());
+
+  // Setup paths
+  CanonicalPath self_cpath = CanonicalPath::create_empty ();
+  bool ok = ResolveTypeToCanonicalPath::go (impl_block.get_type ().get (),
+					    self_cpath);
+  rust_assert (ok);
+  rust_debug ("AST::InherentImpl resolve Self: {%s}",
+	      self_cpath.get ().c_str ());
+
+  CanonicalPath impl_type = self_cpath;
+  CanonicalPath impl_prefix = prefix.append (impl_type);
+
+  // see https://godbolt.org/z/a3vMbsT6W
+  CanonicalPath cpath = CanonicalPath::create_empty ();
+  if (canonical_prefix.size () <= 1)
+    {
+      cpath = self_cpath;
+    }
+  else
+    {
+      std::string seg_buf = "<impl " + self_cpath.get () + ">";
+      CanonicalPath seg
+	= CanonicalPath::new_seg (impl_block.get_node_id (), seg_buf);
+      cpath = canonical_prefix.append (seg);
+    }
+
+  // done setup paths
+
+  auto Self
+    = CanonicalPath::get_big_self (impl_block.get_type ()->get_node_id ());
+
+  resolver->get_type_scope ().insert (Self,
+				      impl_block.get_type ()->get_node_id (),
+				      impl_block.get_type ()->get_locus ());
+
+  for (auto &impl_item : impl_block.get_impl_items ())
+    {
+      rust_debug (
+	"AST::InherentImpl resolve_impl_item: impl_prefix={%s} cpath={%s}",
+	impl_prefix.get ().c_str (), cpath.get ().c_str ());
+      resolve_impl_item (impl_item.get (), impl_prefix, cpath);
+    }
+
+  resolver->get_type_scope ().peek ()->clear_name (
+    Self, impl_block.get_type ()->get_node_id ());
+
+  resolver->get_type_scope ().pop ();
+  resolver->get_name_scope ().pop ();
+}
+
+void
+ResolveItem::visit (AST::Method &method)
+{
+  auto decl
+    = CanonicalPath::new_seg (method.get_node_id (), method.get_method_name ());
+  auto path = prefix.append (decl);
+  auto cpath = canonical_prefix.append (decl);
+  mappings->insert_canonical_path (method.get_node_id (), cpath);
+
+  NodeId scope_node_id = method.get_node_id ();
+
+  resolve_visibility (method.get_visibility ());
+
+  resolver->get_name_scope ().push (scope_node_id);
+  resolver->get_type_scope ().push (scope_node_id);
+  resolver->get_label_scope ().push (scope_node_id);
+  resolver->push_new_name_rib (resolver->get_name_scope ().peek ());
+  resolver->push_new_type_rib (resolver->get_type_scope ().peek ());
+  resolver->push_new_label_rib (resolver->get_type_scope ().peek ());
+
+  if (method.has_generics ())
+    for (auto &generic : method.get_generic_params ())
+      ResolveGenericParam::go (generic.get (), prefix, canonical_prefix);
+
+  // resolve any where clause items
+  if (method.has_where_clause ())
+    ResolveWhereClause::Resolve (method.get_where_clause ());
+
+  if (method.has_return_type ())
+    ResolveType::go (method.get_return_type ().get ());
+
+  // self turns into (self: Self) as a function param
+  AST::SelfParam &self_param = method.get_self_param ();
+  AST::IdentifierPattern self_pattern (self_param.get_node_id (), "self",
+				       self_param.get_locus (),
+				       self_param.get_has_ref (),
+				       self_param.get_is_mut (),
+				       std::unique_ptr<AST::Pattern> (nullptr));
+
+  std::vector<std::unique_ptr<AST::TypePathSegment>> segments;
+  segments.push_back (std::unique_ptr<AST::TypePathSegment> (
+    new AST::TypePathSegment ("Self", false, self_param.get_locus ())));
+
+  AST::TypePath self_type_path (std::move (segments), self_param.get_locus ());
+
+  ResolveType::go (&self_type_path);
+  PatternDeclaration::go (&self_pattern);
+
+  // we make a new scope so the names of parameters are resolved and shadowed
+  // correctly
+  for (auto &param : method.get_function_params ())
+    {
+      ResolveType::go (param.get_type ().get ());
+      PatternDeclaration::go (param.get_pattern ().get ());
+    }
+
+  // resolve any where clause items
+  if (method.has_where_clause ())
+    ResolveWhereClause::Resolve (method.get_where_clause ());
+
+  // resolve the function body
+  ResolveExpr::go (method.get_definition ().get (), path, cpath);
+
+  resolver->get_name_scope ().pop ();
+  resolver->get_type_scope ().pop ();
+  resolver->get_label_scope ().pop ();
+}
+
+void
+ResolveItem::visit (AST::TraitImpl &impl_block)
+{
+  NodeId scope_node_id = impl_block.get_node_id ();
+
+  resolve_visibility (impl_block.get_visibility ());
+
+  resolver->get_name_scope ().push (scope_node_id);
+  resolver->get_type_scope ().push (scope_node_id);
+  resolver->push_new_name_rib (resolver->get_name_scope ().peek ());
+  resolver->push_new_type_rib (resolver->get_type_scope ().peek ());
+
+  if (impl_block.has_generics ())
+    for (auto &generic : impl_block.get_generic_params ())
+      ResolveGenericParam::go (generic.get (), prefix, canonical_prefix);
+
+  // resolve any where clause items
+  if (impl_block.has_where_clause ())
+    ResolveWhereClause::Resolve (impl_block.get_where_clause ());
+
+  // CanonicalPath canonical_trait_type = CanonicalPath::create_empty ();
+  NodeId trait_resolved_node = ResolveType::go (&impl_block.get_trait_path ());
+  if (trait_resolved_node == UNKNOWN_NODEID)
+    {
+      resolver->get_type_scope ().pop ();
+      resolver->get_name_scope ().pop ();
+      return;
+    }
+
+  //   CanonicalPath canonical_impl_type = CanonicalPath::create_empty ();
+  NodeId type_resolved_node = ResolveType::go (impl_block.get_type ().get ());
+  if (type_resolved_node == UNKNOWN_NODEID)
+    {
+      resolver->get_type_scope ().pop ();
+      resolver->get_name_scope ().pop ();
+      return;
+    }
+
+  bool ok;
+  // setup paths
+  CanonicalPath canonical_trait_type = CanonicalPath::create_empty ();
+  ok = ResolveTypeToCanonicalPath::go (&impl_block.get_trait_path (),
+				       canonical_trait_type);
+  rust_assert (ok);
+
+  rust_debug ("AST::TraitImpl resolve trait type: {%s}",
+	      canonical_trait_type.get ().c_str ());
+
+  CanonicalPath canonical_impl_type = CanonicalPath::create_empty ();
+  ok = ResolveTypeToCanonicalPath::go (impl_block.get_type ().get (),
+				       canonical_impl_type);
+  rust_assert (ok);
+
+  rust_debug ("AST::TraitImpl resolve self: {%s}",
+	      canonical_impl_type.get ().c_str ());
+
+  // raw paths
+  CanonicalPath impl_type_seg = canonical_impl_type;
+  CanonicalPath trait_type_seg = canonical_trait_type;
+  CanonicalPath projection
+    = CanonicalPath::trait_impl_projection_seg (impl_block.get_node_id (),
+						trait_type_seg, impl_type_seg);
+  CanonicalPath impl_prefix = prefix.append (projection);
+
+  // setup canonical-path
+  CanonicalPath canonical_projection
+    = CanonicalPath::trait_impl_projection_seg (impl_block.get_node_id (),
+						canonical_trait_type,
+						canonical_impl_type);
+  CanonicalPath cpath = CanonicalPath::create_empty ();
+  if (canonical_prefix.size () <= 1)
+    {
+      cpath = canonical_projection;
+    }
+  else
+    {
+      std::string projection_str = canonical_projection.get ();
+      std::string seg_buf
+	= "<impl " + projection_str.substr (1, projection_str.size () - 2)
+	  + ">";
+      CanonicalPath seg
+	= CanonicalPath::new_seg (impl_block.get_node_id (), seg_buf);
+      cpath = canonical_prefix.append (seg);
+    }
+
+  // DONE setup canonical-path
+
+  auto Self
+    = CanonicalPath::get_big_self (impl_block.get_type ()->get_node_id ());
+
+  resolver->get_type_scope ().insert (Self,
+				      impl_block.get_type ()->get_node_id (),
+				      impl_block.get_type ()->get_locus ());
+
+  for (auto &impl_item : impl_block.get_impl_items ())
+    {
+      rust_debug (
+	"AST::TraitImpl resolve_impl_item: impl_prefix={%s} cpath={%s}",
+	impl_prefix.get ().c_str (), cpath.get ().c_str ());
+      resolve_impl_item (impl_item.get (), impl_prefix, cpath);
+    }
+
+  resolver->get_type_scope ().peek ()->clear_name (
+    Self, impl_block.get_type ()->get_node_id ());
+  resolver->get_type_scope ().pop ();
+}
+
+void
+ResolveItem::visit (AST::Trait &trait)
+{
+  NodeId scope_node_id = trait.get_node_id ();
+
+  resolve_visibility (trait.get_visibility ());
+
+  resolver->get_name_scope ().push (scope_node_id);
+  resolver->get_type_scope ().push (scope_node_id);
+  resolver->push_new_name_rib (resolver->get_name_scope ().peek ());
+  resolver->push_new_type_rib (resolver->get_type_scope ().peek ());
+
+  // we need to inject an implicit self TypeParam here
+  AST::TypeParam *implicit_self
+    = new AST::TypeParam ("Self", trait.get_locus ());
+  trait.insert_implict_self (
+    std::unique_ptr<AST::GenericParam> (implicit_self));
+  CanonicalPath Self = CanonicalPath::get_big_self (trait.get_node_id ());
+
+  for (auto &generic : trait.get_generic_params ())
+    ResolveGenericParam::go (generic.get (), prefix, canonical_prefix);
+
+  // Self is an implicit TypeParam so lets mark it as such
+  resolver->get_type_scope ().append_reference_for_def (
+    Self.get_node_id (), implicit_self->get_node_id ());
+
+  if (trait.has_type_param_bounds ())
+    {
+      for (auto &bound : trait.get_type_param_bounds ())
+	{
+	  ResolveTypeBound::go (bound.get ());
+	}
+    }
+
+  // resolve any where clause items
+  if (trait.has_where_clause ())
+    ResolveWhereClause::Resolve (trait.get_where_clause ());
+
+  // resolve the paths
+  CanonicalPath path = CanonicalPath::create_empty ();
+  CanonicalPath cpath = CanonicalPath::create_empty ();
+  //
+
+  for (auto &item : trait.get_trait_items ())
+    {
+      ResolveTraitItems::go (item.get (), path, cpath);
+    }
+
+  resolver->get_type_scope ().pop ();
+  resolver->get_name_scope ().pop ();
+}
+
+void
+ResolveItem::visit (AST::ExternBlock &extern_block)
+{
+  resolve_visibility (extern_block.get_visibility ());
+
+  for (auto &item : extern_block.get_extern_items ())
+    {
+      resolve_extern_item (item.get ());
+    }
+}
+
+void
+ResolveItem::resolve_impl_item (AST::TraitImplItem *item,
+				const CanonicalPath &prefix,
+				const CanonicalPath &canonical_prefix)
+{
+  ResolveImplItems::go (item, prefix, canonical_prefix);
+}
+
+void
+ResolveItem::resolve_impl_item (AST::InherentImplItem *item,
+				const CanonicalPath &prefix,
+				const CanonicalPath &canonical_prefix)
+{
+  ResolveImplItems::go (item, prefix, canonical_prefix);
+}
+
+void
+ResolveItem::resolve_extern_item (AST::ExternalItem *item)
+{
+  ResolveExternItem::go (item, prefix, canonical_prefix);
+}
+
+static void
+flatten_glob (const AST::UseTreeGlob &glob,
+	      std::vector<AST::SimplePath> &paths);
+static void
+flatten_rebind (const AST::UseTreeRebind &glob,
+		std::vector<AST::SimplePath> &paths);
+static void
+flatten_list (const AST::UseTreeList &glob,
+	      std::vector<AST::SimplePath> &paths);
+
+static void
+flatten (const AST::UseTree *tree, std::vector<AST::SimplePath> &paths)
+{
+  switch (tree->get_kind ())
+    {
+      case AST::UseTree::Glob: {
+	auto glob = static_cast<const AST::UseTreeGlob *> (tree);
+	flatten_glob (*glob, paths);
+	break;
+      }
+      case AST::UseTree::Rebind: {
+	auto rebind = static_cast<const AST::UseTreeRebind *> (tree);
+	flatten_rebind (*rebind, paths);
+	break;
+      }
+      case AST::UseTree::List: {
+	auto list = static_cast<const AST::UseTreeList *> (tree);
+	flatten_list (*list, paths);
+	break;
+      }
+      break;
+    }
+}
+
+static void
+flatten_glob (const AST::UseTreeGlob &glob, std::vector<AST::SimplePath> &paths)
+{
+  if (glob.has_path ())
+    paths.emplace_back (glob.get_path ());
+}
+
+static void
+flatten_rebind (const AST::UseTreeRebind &rebind,
+		std::vector<AST::SimplePath> &paths)
+{
+  auto path = rebind.get_path ();
+  if (rebind.has_path ())
+    paths.emplace_back (path);
+
+  // FIXME: Do we want to emplace the rebind here as well?
+  if (rebind.has_identifier ())
+    {
+      auto rebind_path = path;
+      auto new_seg = rebind.get_identifier ();
+
+      // Add the identifier as a new path
+      rebind_path.get_segments ().back ()
+	= AST::SimplePathSegment (new_seg, Location ());
+
+      paths.emplace_back (rebind_path);
+    }
+}
+
+static void
+flatten_list (const AST::UseTreeList &list, std::vector<AST::SimplePath> &paths)
+{
+  auto prefix = AST::SimplePath::create_empty ();
+  if (list.has_path ())
+    prefix = list.get_path ();
+
+  for (const auto &tree : list.get_trees ())
+    {
+      auto sub_paths = std::vector<AST::SimplePath> ();
+      flatten (tree.get (), sub_paths);
+
+      for (auto &sub_path : sub_paths)
+	{
+	  auto new_path = prefix;
+	  std::copy (sub_path.get_segments ().begin (),
+		     sub_path.get_segments ().end (),
+		     std::back_inserter (new_path.get_segments ()));
+
+	  paths.emplace_back (new_path);
+	}
+    }
+}
+
+/**
+ * Flatten a UseDeclaration's UseTree into multiple simple paths to resolve.
+ *
+ * Given the following use declarations:
+ * ```
+ * use some::path::to_resolve; #1
+ * use some::path::to_glob::*; #2
+ * use some::path::{one, two}; #2
+ * ```
+ *
+ * In the first case, we simply want to return a vector with a single
+ * SimplePath:
+ * [some::path::to_resolve]
+ *
+ * In the second case, we want to resolve the glob's "origin path":
+ * [some::path::to_glob]
+ *
+ * Finally in the third case, we want to create two SimplePaths to resolve:
+ * [some::path::one, some::path::two]
+ */
+static std::vector<AST::SimplePath>
+flatten_use_dec_to_paths (const AST::UseDeclaration &use_item)
+{
+  auto paths = std::vector<AST::SimplePath> ();
+
+  const auto &tree = use_item.get_tree ();
+  flatten (tree.get (), paths);
+
+  return paths;
+}
+
+void
+ResolveItem::visit (AST::UseDeclaration &use_item)
+{
+  auto to_resolve = flatten_use_dec_to_paths (use_item);
+
+  for (auto &path : to_resolve)
+    ResolvePath::go (&path);
+}
+
+ResolveImplItems::ResolveImplItems (const CanonicalPath &prefix,
+				    const CanonicalPath &canonical_prefix)
+  : ResolveItem (prefix, canonical_prefix)
+{}
+
+void
+ResolveImplItems::go (AST::InherentImplItem *item, const CanonicalPath &prefix,
+		      const CanonicalPath &canonical_prefix)
+{
+  if (item->is_marked_for_strip ())
+    return;
+
+  ResolveImplItems resolver (prefix, canonical_prefix);
+  item->accept_vis (resolver);
+}
+
+void
+ResolveImplItems::go (AST::TraitImplItem *item, const CanonicalPath &prefix,
+		      const CanonicalPath &canonical_prefix)
+{
+  if (item->is_marked_for_strip ())
+    return;
+
+  ResolveImplItems resolver (prefix, canonical_prefix);
+  item->accept_vis (resolver);
+}
+
+void
+ResolveImplItems::visit (AST::TypeAlias &alias)
+{
+  ResolveItem::visit (alias);
+
+  resolve_visibility (alias.get_visibility ());
+
+  // FIXME this stops the erronious unused decls which will be fixed later on
+  resolver->get_type_scope ().append_reference_for_def (alias.get_node_id (),
+							alias.get_node_id ());
+}
+
+void
+ResolveExternItem::go (AST::ExternalItem *item, const CanonicalPath &prefix,
+		       const CanonicalPath &canonical_prefix)
+{
+  ResolveExternItem resolver (prefix, canonical_prefix);
+  item->accept_vis (resolver);
+}
+
+void
+ResolveExternItem::visit (AST::ExternalFunctionItem &function)
+{
+  NodeId scope_node_id = function.get_node_id ();
+  auto decl = CanonicalPath::new_seg (function.get_node_id (),
+				      function.get_identifier ());
+  auto path = prefix.append (decl);
+  auto cpath = canonical_prefix.append (decl);
+
+  mappings->insert_canonical_path (function.get_node_id (), cpath);
+
+  resolve_visibility (function.get_visibility ());
+
+  resolver->get_name_scope ().push (scope_node_id);
+  resolver->get_type_scope ().push (scope_node_id);
+  resolver->get_label_scope ().push (scope_node_id);
+  resolver->push_new_name_rib (resolver->get_name_scope ().peek ());
+  resolver->push_new_type_rib (resolver->get_type_scope ().peek ());
+  resolver->push_new_label_rib (resolver->get_type_scope ().peek ());
+
+  // resolve the generics
+  if (function.has_generics ())
+    for (auto &generic : function.get_generic_params ())
+      ResolveGenericParam::go (generic.get (), prefix, canonical_prefix);
+
+  if (function.has_return_type ())
+    ResolveType::go (function.get_return_type ().get ());
+
+  // we make a new scope so the names of parameters are resolved and shadowed
+  // correctly
+  for (auto &param : function.get_function_params ())
+    {
+      ResolveType::go (param.get_type ().get ());
+    }
+
+  // done
+  resolver->get_name_scope ().pop ();
+  resolver->get_type_scope ().pop ();
+  resolver->get_label_scope ().pop ();
+}
+
+void
+ResolveExternItem::visit (AST::ExternalStaticItem &item)
+{
+  resolve_visibility (item.get_visibility ());
+
+  ResolveType::go (item.get_type ().get ());
+}
+
+} // namespace Resolver
+} // namespace Rust
+
+#if CHECKING_P
+
+namespace selftest {
+
+static void
+rust_flatten_nested_glob (void)
+{
+  auto foo = Rust::AST::SimplePathSegment ("foo", Location ());
+  auto bar = Rust::AST::SimplePathSegment ("bar", Location ());
+  auto foobar = Rust::AST::SimplePath ({foo, bar});
+
+  auto glob
+    = Rust::AST::UseTreeGlob (Rust::AST::UseTreeGlob::PathType::PATH_PREFIXED,
+			      foobar, Location ());
+
+  auto paths = std::vector<Rust::AST::SimplePath> ();
+  Rust::Resolver::flatten_glob (glob, paths);
+
+  ASSERT_TRUE (!paths.empty ());
+  ASSERT_EQ (paths.size (), 1);
+  ASSERT_EQ (paths[0].get_segments ()[0].as_string (), "foo");
+  ASSERT_EQ (paths[0].get_segments ()[1].as_string (), "bar");
+}
+
+static void
+rust_flatten_glob (void)
+{
+  auto frob = Rust::AST::SimplePath::from_str ("frobulator", Location ());
+
+  auto glob
+    = Rust::AST::UseTreeGlob (Rust::AST::UseTreeGlob::PathType::PATH_PREFIXED,
+			      frob, Location ());
+
+  auto paths = std::vector<Rust::AST::SimplePath> ();
+  Rust::Resolver::flatten_glob (glob, paths);
+
+  ASSERT_TRUE (!paths.empty ());
+  ASSERT_EQ (paths.size (), 1);
+  ASSERT_EQ (paths[0], "frobulator");
+}
+
+static void
+rust_flatten_rebind_none (void)
+{
+  auto foo = Rust::AST::SimplePathSegment ("foo", Location ());
+  auto bar = Rust::AST::SimplePathSegment ("bar", Location ());
+  auto foobar = Rust::AST::SimplePath ({foo, bar});
+
+  auto rebind = Rust::AST::UseTreeRebind (Rust::AST::UseTreeRebind::NONE,
+					  foobar, Location ());
+
+  auto paths = std::vector<Rust::AST::SimplePath> ();
+  Rust::Resolver::flatten_rebind (rebind, paths);
+
+  ASSERT_TRUE (!paths.empty ());
+  ASSERT_EQ (paths.size (), 1);
+  ASSERT_EQ (paths[0].get_segments ()[0].as_string (), "foo");
+  ASSERT_EQ (paths[0].get_segments ()[1].as_string (), "bar");
+}
+
+static void
+rust_flatten_rebind (void)
+{
+  auto frob = Rust::AST::SimplePath::from_str ("frobulator", Location ());
+
+  auto rebind = Rust::AST::UseTreeRebind (Rust::AST::UseTreeRebind::IDENTIFIER,
+					  frob, Location (), "saindoux");
+
+  auto paths = std::vector<Rust::AST::SimplePath> ();
+  Rust::Resolver::flatten_rebind (rebind, paths);
+
+  ASSERT_TRUE (!paths.empty ());
+  ASSERT_EQ (paths.size (), 2);
+  ASSERT_EQ (paths[0], "frobulator");
+  ASSERT_EQ (paths[1], "saindoux");
+}
+
+static void
+rust_flatten_rebind_nested (void)
+{
+  auto foo = Rust::AST::SimplePathSegment ("foo", Location ());
+  auto bar = Rust::AST::SimplePathSegment ("bar", Location ());
+  auto baz = Rust::AST::SimplePathSegment ("baz", Location ());
+
+  auto foo_bar_baz = Rust::AST::SimplePath ({foo, bar, baz});
+
+  auto rebind = Rust::AST::UseTreeRebind (Rust::AST::UseTreeRebind::IDENTIFIER,
+					  foo_bar_baz, Location (), "saindoux");
+
+  auto paths = std::vector<Rust::AST::SimplePath> ();
+  Rust::Resolver::flatten_rebind (rebind, paths);
+
+  ASSERT_TRUE (!paths.empty ());
+  ASSERT_EQ (paths.size (), 2);
+  ASSERT_EQ (paths[0].get_segments ()[0].as_string (), "foo");
+  ASSERT_EQ (paths[0].get_segments ()[1].as_string (), "bar");
+  ASSERT_EQ (paths[0].get_segments ()[2].as_string (), "baz");
+  ASSERT_EQ (paths[1].get_segments ()[0].as_string (), "foo");
+  ASSERT_EQ (paths[1].get_segments ()[1].as_string (), "bar");
+  ASSERT_EQ (paths[1].get_segments ()[2].as_string (), "saindoux");
+}
+
+static void
+rust_flatten_list (void)
+{
+  auto foo = Rust::AST::SimplePathSegment ("foo", Location ());
+  auto bar = Rust::AST::SimplePathSegment ("bar", Location ());
+  auto foo_bar = Rust::AST::SimplePath ({foo, bar});
+
+  auto baz = Rust::AST::SimplePath::from_str ("baz", Location ());
+  auto bul = Rust::AST::SimplePath::from_str ("bul", Location ());
+
+  // use foo::bar::{baz, bul};
+
+  auto use0 = std::unique_ptr<Rust::AST::UseTree> (
+    new Rust::AST::UseTreeRebind (Rust::AST::UseTreeRebind::NONE, baz,
+				  Location ()));
+  auto use1 = std::unique_ptr<Rust::AST::UseTree> (
+    new Rust::AST::UseTreeRebind (Rust::AST::UseTreeRebind::NONE, bul,
+				  Location ()));
+
+  auto uses = std::vector<std::unique_ptr<Rust::AST::UseTree>> ();
+  uses.emplace_back (std::move (use0));
+  uses.emplace_back (std::move (use1));
+
+  auto list = Rust::AST::UseTreeList (Rust::AST::UseTreeList::PATH_PREFIXED,
+				      foo_bar, std::move (uses), Location ());
+
+  auto paths = std::vector<Rust::AST::SimplePath> ();
+  Rust::Resolver::flatten_list (list, paths);
+
+  for (auto &path : paths)
+    fprintf (stderr, "%s\n", path.as_string ().c_str ());
+
+  ASSERT_TRUE (!paths.empty ());
+  ASSERT_EQ (paths.size (), 2);
+  ASSERT_EQ (paths[0].get_segments ()[0].as_string (), "foo");
+  ASSERT_EQ (paths[0].get_segments ()[1].as_string (), "bar");
+  ASSERT_EQ (paths[0].get_segments ()[2].as_string (), "baz");
+  ASSERT_EQ (paths[1].get_segments ()[0].as_string (), "foo");
+  ASSERT_EQ (paths[1].get_segments ()[1].as_string (), "bar");
+  ASSERT_EQ (paths[1].get_segments ()[2].as_string (), "bul");
+}
+
+static void
+rust_use_dec_flattening (void)
+{
+  rust_flatten_glob ();
+  rust_flatten_nested_glob ();
+  rust_flatten_rebind_none ();
+  rust_flatten_rebind ();
+  rust_flatten_rebind_nested ();
+  rust_flatten_list ();
+}
+
+void
+rust_simple_path_resolve_test (void)
+{
+  rust_use_dec_flattening ();
+}
+
+} // namespace selftest
+
+#endif // CHECKING_P
diff --git a/gcc/rust/resolve/rust-ast-resolve-item.h b/gcc/rust/resolve/rust-ast-resolve-item.h
new file mode 100644
index 00000000000..ce521f057f6
--- /dev/null
+++ b/gcc/rust/resolve/rust-ast-resolve-item.h
@@ -0,0 +1,149 @@
+// 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_AST_RESOLVE_ITEM_H
+#define RUST_AST_RESOLVE_ITEM_H
+
+#include "rust-ast-full-decls.h"
+#include "rust-ast-resolve-base.h"
+#include "rust-ast-full.h"
+#include "rust-ast-resolve-toplevel.h"
+#include "rust-ast-resolve-type.h"
+#include "rust-ast-resolve-pattern.h"
+#include "rust-ast-resolve-stmt.h"
+#include "config.h"
+
+namespace Rust {
+namespace Resolver {
+
+class ResolveTraitItems : public ResolverBase
+{
+  using Rust::Resolver::ResolverBase::visit;
+
+public:
+  static void go (AST::TraitItem *item, const CanonicalPath &prefix,
+		  const CanonicalPath &canonical_prefix);
+
+  void visit (AST::TraitItemType &type) override;
+  void visit (AST::TraitItemFunc &func) override;
+  void visit (AST::TraitItemMethod &func) override;
+  void visit (AST::TraitItemConst &constant) override;
+
+private:
+  ResolveTraitItems (const CanonicalPath &prefix,
+		     const CanonicalPath &canonical_prefix);
+
+  const CanonicalPath &prefix;
+  const CanonicalPath &canonical_prefix;
+};
+
+class ResolveItem : public ResolverBase
+{
+public:
+  using Rust::Resolver::ResolverBase::visit;
+
+  static void go (AST::Item *item, const CanonicalPath &prefix,
+		  const CanonicalPath &canonical_prefix);
+
+  void visit (AST::TypeAlias &alias) override;
+  void visit (AST::Module &module) override;
+  void visit (AST::TupleStruct &struct_decl) override;
+  void visit (AST::Enum &enum_decl) override;
+  /* EnumItem doesn't need to be handled, no fields.  */
+  void visit (AST::EnumItem &item) override;
+  void visit (AST::EnumItemTuple &item) override;
+  void visit (AST::EnumItemStruct &item) override;
+  void visit (AST::EnumItemDiscriminant &item) override;
+  void visit (AST::StructStruct &struct_decl) override;
+  void visit (AST::Union &union_decl) override;
+  void visit (AST::StaticItem &var) override;
+  void visit (AST::ConstantItem &constant) override;
+  void visit (AST::Function &function) override;
+  void visit (AST::InherentImpl &impl_block) override;
+  void visit (AST::Method &method) override;
+  void visit (AST::TraitImpl &impl_block) override;
+  void visit (AST::Trait &trait) override;
+  void visit (AST::ExternBlock &extern_block) override;
+  void visit (AST::UseDeclaration &) override;
+
+protected:
+  void resolve_impl_item (AST::TraitImplItem *item, const CanonicalPath &prefix,
+			  const CanonicalPath &canonical_prefix);
+  void resolve_impl_item (AST::InherentImplItem *item,
+			  const CanonicalPath &prefix,
+			  const CanonicalPath &canonical_prefix);
+  void resolve_extern_item (AST::ExternalItem *item);
+
+  ResolveItem (const CanonicalPath &prefix,
+	       const CanonicalPath &canonical_prefix);
+
+  const CanonicalPath &prefix;
+  const CanonicalPath &canonical_prefix;
+};
+
+class ResolveImplItems : public ResolveItem
+{
+  using Rust::Resolver::ResolveItem::visit;
+
+public:
+  static void go (AST::InherentImplItem *item, const CanonicalPath &prefix,
+		  const CanonicalPath &canonical_prefix);
+  static void go (AST::TraitImplItem *item, const CanonicalPath &prefix,
+		  const CanonicalPath &canonical_prefix);
+
+  void visit (AST::TypeAlias &alias) override;
+
+private:
+  ResolveImplItems (const CanonicalPath &prefix,
+		    const CanonicalPath &canonical_prefix);
+};
+
+class ResolveExternItem : public ResolverBase
+{
+  using Rust::Resolver::ResolverBase::visit;
+
+public:
+  static void go (AST::ExternalItem *item, const CanonicalPath &prefix,
+		  const CanonicalPath &canonical_prefix);
+
+  void visit (AST::ExternalFunctionItem &function) override;
+  void visit (AST::ExternalStaticItem &item) override;
+
+private:
+  ResolveExternItem (const CanonicalPath &prefix,
+		     const CanonicalPath &canonical_prefix)
+    : ResolverBase (), prefix (prefix), canonical_prefix (canonical_prefix)
+  {}
+
+  const CanonicalPath &prefix;
+  const CanonicalPath &canonical_prefix;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#if CHECKING_P
+
+namespace selftest {
+extern void
+rust_simple_path_resolve_test (void);
+} // namespace selftest
+
+#endif // CHECKING_P
+
+#endif // RUST_AST_RESOLVE_ITEM_H
diff --git a/gcc/rust/resolve/rust-ast-resolve-path.cc b/gcc/rust/resolve/rust-ast-resolve-path.cc
new file mode 100644
index 00000000000..b139c6a8720
--- /dev/null
+++ b/gcc/rust/resolve/rust-ast-resolve-path.cc
@@ -0,0 +1,384 @@
+// 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/>.
+
+#include "rust-ast-resolve-path.h"
+#include "rust-ast-resolve-type.h"
+#include "rust-path.h"
+
+namespace Rust {
+namespace Resolver {
+
+ResolvePath::ResolvePath () : ResolverBase () {}
+
+void
+ResolvePath::go (AST::PathInExpression *expr)
+{
+  ResolvePath resolver;
+  resolver.resolve_path (expr);
+}
+
+void
+ResolvePath::go (AST::QualifiedPathInExpression *expr)
+{
+  ResolvePath resolver;
+  resolver.resolve_path (expr);
+}
+
+void
+ResolvePath::go (AST::SimplePath *expr)
+{
+  ResolvePath resolver;
+  resolver.resolve_path (expr);
+}
+
+void
+ResolvePath::resolve_path (AST::PathInExpression *expr)
+{
+  NodeId resolved_node_id = UNKNOWN_NODEID;
+  NodeId module_scope_id = resolver->peek_current_module_scope ();
+  NodeId previous_resolved_node_id = module_scope_id;
+  for (size_t i = 0; i < expr->get_segments ().size (); i++)
+    {
+      auto &segment = expr->get_segments ().at (i);
+      const AST::PathIdentSegment &ident_seg = segment.get_ident_segment ();
+      bool is_first_segment = i == 0;
+      resolved_node_id = UNKNOWN_NODEID;
+
+      bool in_middle_of_path = i > 0;
+      if (in_middle_of_path && segment.is_lower_self_seg ())
+	{
+	  // error[E0433]: failed to resolve: `self` in paths can only be used
+	  // in start position
+	  rust_error_at (segment.get_locus (),
+			 "failed to resolve: %<%s%> in paths can only be used "
+			 "in start position",
+			 segment.as_string ().c_str ());
+	  return;
+	}
+
+      NodeId crate_scope_id = resolver->peek_crate_module_scope ();
+      if (segment.is_crate_path_seg ())
+	{
+	  // what is the current crate scope node id?
+	  module_scope_id = crate_scope_id;
+	  previous_resolved_node_id = module_scope_id;
+	  resolver->insert_resolved_name (segment.get_node_id (),
+					  module_scope_id);
+	  continue;
+	}
+      else if (segment.is_super_path_seg ())
+	{
+	  if (module_scope_id == crate_scope_id)
+	    {
+	      rust_error_at (segment.get_locus (),
+			     "cannot use %<super%> at the crate scope");
+	      return;
+	    }
+
+	  module_scope_id = resolver->peek_parent_module_scope ();
+	  previous_resolved_node_id = module_scope_id;
+	  resolver->insert_resolved_name (segment.get_node_id (),
+					  module_scope_id);
+	  continue;
+	}
+
+      // resolve any generic args
+      if (segment.has_generic_args ())
+	ResolveGenericArgs::go (segment.get_generic_args ());
+
+      // logic is awkward here there are a few cases
+      //
+      // T::Default
+      // mod::foo::impl_item
+      // super::super::module::item
+      // self
+      // self::foo
+      // self::foo::baz
+      //
+      // T::Default we can only resolve the T and cant do anything about Default
+      // its dependant on associated types
+      //
+      // mod::foo::impl_item
+      // we can resolve mod::foo but nothing about impl_item but we need to
+      // _always resolve generic arguments
+      //
+      // self is a simple single lookup
+      //
+      // we have module_scope_id for the next module_scope to lookup
+      // resolved_node_id is the thing we have resolve this segment to
+      //
+      // new algo?
+      // we can only use module resolution when the previous segment is either
+      // unknown or equal to this module_scope_id
+      //
+      // can only use old resolution when previous segment is unkown
+
+      if (is_first_segment)
+	{
+	  // name scope first
+	  NodeId resolved_node = UNKNOWN_NODEID;
+	  const CanonicalPath path
+	    = CanonicalPath::new_seg (segment.get_node_id (),
+				      ident_seg.as_string ());
+	  if (resolver->get_name_scope ().lookup (path, &resolved_node))
+	    {
+	      resolver->insert_resolved_name (segment.get_node_id (),
+					      resolved_node);
+	      resolved_node_id = resolved_node;
+	    }
+	  // check the type scope
+	  else if (resolver->get_type_scope ().lookup (path, &resolved_node))
+	    {
+	      resolver->insert_resolved_type (segment.get_node_id (),
+					      resolved_node);
+	      resolved_node_id = resolved_node;
+	    }
+	  else if (segment.is_lower_self_seg ())
+	    {
+	      module_scope_id = crate_scope_id;
+	      previous_resolved_node_id = module_scope_id;
+	      resolver->insert_resolved_name (segment.get_node_id (),
+					      module_scope_id);
+	      continue;
+	    }
+	  else
+	    {
+	      // no error handling here since we might be able to resolve via
+	      // the module hierarchy and handle errors at the end
+	    }
+	}
+
+      if (resolved_node_id == UNKNOWN_NODEID
+	  && previous_resolved_node_id == module_scope_id)
+	{
+	  Optional<CanonicalPath &> resolved_child
+	    = mappings->lookup_module_child (module_scope_id,
+					     ident_seg.as_string ());
+	  if (resolved_child.is_some ())
+	    {
+	      NodeId resolved_node = resolved_child->get_node_id ();
+	      if (resolver->get_name_scope ().decl_was_declared_here (
+		    resolved_node))
+		{
+		  resolved_node_id = resolved_node;
+		  resolver->insert_resolved_name (segment.get_node_id (),
+						  resolved_node);
+		}
+	      else if (resolver->get_type_scope ().decl_was_declared_here (
+			 resolved_node))
+		{
+		  resolved_node_id = resolved_node;
+		  resolver->insert_resolved_type (segment.get_node_id (),
+						  resolved_node);
+		}
+	      else
+		{
+		  rust_error_at (segment.get_locus (),
+				 "Cannot find path %<%s%> in this scope",
+				 segment.as_string ().c_str ());
+		  return;
+		}
+	    }
+	}
+
+      bool did_resolve_segment = resolved_node_id != UNKNOWN_NODEID;
+      if (did_resolve_segment)
+	{
+	  if (mappings->node_is_module (resolved_node_id)
+	      || mappings->node_is_crate (resolved_node_id))
+	    {
+	      module_scope_id = resolved_node_id;
+	    }
+	  previous_resolved_node_id = resolved_node_id;
+	}
+      else if (is_first_segment)
+	{
+	  rust_error_at (segment.get_locus (),
+			 "Cannot find path %<%s%> in this scope",
+			 segment.as_string ().c_str ());
+	  return;
+	}
+    }
+
+  resolved_node = resolved_node_id;
+  if (resolved_node_id != UNKNOWN_NODEID)
+    {
+      // name scope first
+      if (resolver->get_name_scope ().decl_was_declared_here (resolved_node_id))
+	{
+	  resolver->insert_resolved_name (expr->get_node_id (),
+					  resolved_node_id);
+	}
+      // check the type scope
+      else if (resolver->get_type_scope ().decl_was_declared_here (
+		 resolved_node_id))
+	{
+	  resolver->insert_resolved_type (expr->get_node_id (),
+					  resolved_node_id);
+	}
+      else
+	{
+	  gcc_unreachable ();
+	}
+    }
+}
+
+void
+ResolvePath::resolve_path (AST::QualifiedPathInExpression *expr)
+{
+  AST::QualifiedPathType &root_segment = expr->get_qualified_path_type ();
+  ResolveType::go (root_segment.get_type ().get ());
+  if (root_segment.has_as_clause ())
+    ResolveType::go (&root_segment.get_as_type_path ());
+
+  for (auto &segment : expr->get_segments ())
+    {
+      // we cant actually do anything with the segment itself since this is all
+      // the job of the type system to figure it out but we can resolve any
+      // generic arguments used
+      if (segment.has_generic_args ())
+	ResolveGenericArgs::go (segment.get_generic_args ());
+    }
+}
+
+void
+ResolvePath::resolve_path (AST::SimplePath *expr)
+{
+  NodeId crate_scope_id = resolver->peek_crate_module_scope ();
+  NodeId module_scope_id = resolver->peek_current_module_scope ();
+
+  NodeId resolved_node_id = UNKNOWN_NODEID;
+  for (size_t i = 0; i < expr->get_segments ().size (); i++)
+    {
+      auto &segment = expr->get_segments ().at (i);
+      bool is_first_segment = i == 0;
+      resolved_node_id = UNKNOWN_NODEID;
+
+      if (segment.is_crate_path_seg ())
+	{
+	  // what is the current crate scope node id?
+	  module_scope_id = crate_scope_id;
+	  resolver->insert_resolved_name (segment.get_node_id (),
+					  module_scope_id);
+	  continue;
+	}
+      else if (segment.is_super_path_seg ())
+	{
+	  if (module_scope_id == crate_scope_id)
+	    {
+	      rust_error_at (segment.get_locus (),
+			     "cannot use %<super%> at the crate scope");
+	      return;
+	    }
+
+	  module_scope_id = resolver->peek_parent_module_scope ();
+	  resolver->insert_resolved_name (segment.get_node_id (),
+					  module_scope_id);
+	  continue;
+	}
+
+      Optional<CanonicalPath &> resolved_child
+	= mappings->lookup_module_child (module_scope_id,
+					 segment.get_segment_name ());
+      if (resolved_child.is_some ())
+	{
+	  NodeId resolved_node = resolved_child->get_node_id ();
+	  if (resolver->get_name_scope ().decl_was_declared_here (
+		resolved_node))
+	    {
+	      resolved_node_id = resolved_node;
+	      resolver->insert_resolved_name (segment.get_node_id (),
+					      resolved_node);
+	    }
+	  else if (resolver->get_type_scope ().decl_was_declared_here (
+		     resolved_node))
+	    {
+	      resolved_node_id = resolved_node;
+	      resolver->insert_resolved_type (segment.get_node_id (),
+					      resolved_node);
+	    }
+	  else
+	    {
+	      rust_error_at (segment.get_locus (),
+			     "Cannot find path %<%s%> in this scope",
+			     segment.as_string ().c_str ());
+	      return;
+	    }
+	}
+
+      if (resolved_node_id == UNKNOWN_NODEID && is_first_segment)
+	{
+	  // name scope first
+	  NodeId resolved_node = UNKNOWN_NODEID;
+	  const CanonicalPath path
+	    = CanonicalPath::new_seg (segment.get_node_id (),
+				      segment.get_segment_name ());
+	  if (resolver->get_name_scope ().lookup (path, &resolved_node))
+	    {
+	      resolved_node_id = resolved_node;
+	      resolver->insert_resolved_name (segment.get_node_id (),
+					      resolved_node);
+	    }
+	  // check the type scope
+	  else if (resolver->get_type_scope ().lookup (path, &resolved_node))
+	    {
+	      resolved_node_id = resolved_node;
+	      resolver->insert_resolved_type (segment.get_node_id (),
+					      resolved_node);
+	    }
+	}
+
+      if (resolved_node_id == UNKNOWN_NODEID)
+	{
+	  rust_error_at (segment.get_locus (),
+			 "cannot find simple path segment %<%s%> in this scope",
+			 segment.as_string ().c_str ());
+	  return;
+	}
+
+      if (mappings->node_is_module (resolved_node_id))
+	{
+	  module_scope_id = resolved_node_id;
+	}
+    }
+
+  resolved_node = resolved_node_id;
+  if (resolved_node_id != UNKNOWN_NODEID)
+    {
+      // name scope first
+      if (resolver->get_name_scope ().decl_was_declared_here (resolved_node_id))
+	{
+	  resolver->insert_resolved_name (expr->get_node_id (),
+					  resolved_node_id);
+	}
+      // check the type scope
+      else if (resolver->get_type_scope ().decl_was_declared_here (
+		 resolved_node_id))
+	{
+	  resolver->insert_resolved_type (expr->get_node_id (),
+					  resolved_node_id);
+	}
+      else
+	{
+	  gcc_unreachable ();
+	}
+    }
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/resolve/rust-ast-resolve-path.h b/gcc/rust/resolve/rust-ast-resolve-path.h
new file mode 100644
index 00000000000..a9af0c5819c
--- /dev/null
+++ b/gcc/rust/resolve/rust-ast-resolve-path.h
@@ -0,0 +1,52 @@
+// 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_AST_RESOLVE_PATH_H
+#define RUST_AST_RESOLVE_PATH_H
+
+#include "rust-ast-resolve-base.h"
+
+namespace Rust {
+namespace Resolver {
+
+class ResolvePath : public ResolverBase
+{
+  using Rust::Resolver::ResolverBase::visit;
+
+public:
+  static void go (AST::PathInExpression *expr);
+  static void go (AST::QualifiedPathInExpression *expr);
+  static void go (AST::SimplePath *expr);
+
+private:
+  ResolvePath ();
+
+  void resolve_path (AST::PathInExpression *expr);
+  void resolve_path (AST::QualifiedPathInExpression *expr);
+  void resolve_path (AST::SimplePath *expr);
+
+  void
+  resolve_simple_path_segments (CanonicalPath prefix, size_t offs,
+				const std::vector<AST::SimplePathSegment> &segs,
+				NodeId expr_node_id, Location expr_locus);
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // !RUST_AST_RESOLVE_PATH_H
diff --git a/gcc/rust/resolve/rust-ast-resolve-pattern.cc b/gcc/rust/resolve/rust-ast-resolve-pattern.cc
new file mode 100644
index 00000000000..9386d36d25e
--- /dev/null
+++ b/gcc/rust/resolve/rust-ast-resolve-pattern.cc
@@ -0,0 +1,163 @@
+// 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/>.
+
+#include "rust-ast-resolve-pattern.h"
+#include "rust-ast-resolve-path.h"
+#include "rust-ast-resolve-expr.h"
+
+namespace Rust {
+namespace Resolver {
+
+void
+PatternDeclaration::visit (AST::PathInExpression &pattern)
+{
+  ResolvePath::go (&pattern);
+}
+
+void
+PatternDeclaration::visit (AST::TupleStructPattern &pattern)
+{
+  ResolvePath::go (&pattern.get_path ());
+
+  std::unique_ptr<AST::TupleStructItems> &items = pattern.get_items ();
+  switch (items->get_item_type ())
+    {
+      case AST::TupleStructItems::RANGE: {
+	// TODO
+	gcc_unreachable ();
+      }
+      break;
+
+      case AST::TupleStructItems::NO_RANGE: {
+	AST::TupleStructItemsNoRange &items_no_range
+	  = static_cast<AST::TupleStructItemsNoRange &> (*items.get ());
+
+	for (auto &inner_pattern : items_no_range.get_patterns ())
+	  {
+	    PatternDeclaration::go (inner_pattern.get ());
+	  }
+      }
+      break;
+    }
+}
+
+void
+PatternDeclaration::visit (AST::StructPattern &pattern)
+{
+  ResolvePath::go (&pattern.get_path ());
+
+  auto &struct_pattern_elems = pattern.get_struct_pattern_elems ();
+  for (auto &field : struct_pattern_elems.get_struct_pattern_fields ())
+    {
+      switch (field->get_item_type ())
+	{
+	  case AST::StructPatternField::ItemType::TUPLE_PAT: {
+	    // TODO
+	    gcc_unreachable ();
+	  }
+	  break;
+
+	  case AST::StructPatternField::ItemType::IDENT_PAT: {
+	    // TODO
+	    gcc_unreachable ();
+	  }
+	  break;
+
+	  case AST::StructPatternField::ItemType::IDENT: {
+	    AST::StructPatternFieldIdent &ident
+	      = static_cast<AST::StructPatternFieldIdent &> (*field.get ());
+
+	    resolver->get_name_scope ().insert (
+	      CanonicalPath::new_seg (ident.get_node_id (),
+				      ident.get_identifier ()),
+	      ident.get_node_id (), ident.get_locus ());
+	  }
+	  break;
+	}
+    }
+
+  // TODO
+  rust_assert (!struct_pattern_elems.has_etc ());
+}
+
+void
+PatternDeclaration::visit (AST::TuplePattern &pattern)
+{
+  std::unique_ptr<AST::TuplePatternItems> &items = pattern.get_items ();
+  switch (items->get_pattern_type ())
+    {
+      case AST::TuplePatternItems::TuplePatternItemType::MULTIPLE: {
+	AST::TuplePatternItemsMultiple &ref
+	  = *static_cast<AST::TuplePatternItemsMultiple *> (
+	    pattern.get_items ().get ());
+
+	for (auto &p : ref.get_patterns ())
+	  p->accept_vis (*this);
+      }
+      break;
+
+      case AST::TuplePatternItems::TuplePatternItemType::RANGED: {
+	AST::TuplePatternItemsRanged &ref
+	  = *static_cast<AST::TuplePatternItemsRanged *> (
+	    pattern.get_items ().get ());
+
+	for (auto &p : ref.get_lower_patterns ())
+	  p->accept_vis (*this);
+	for (auto &p : ref.get_upper_patterns ())
+	  p->accept_vis (*this);
+      }
+      break;
+    }
+}
+
+static void
+resolve_range_pattern_bound (AST::RangePatternBound *bound)
+{
+  switch (bound->get_bound_type ())
+    {
+    case AST::RangePatternBound::RangePatternBoundType::LITERAL:
+      // Nothing to resolve for a literal.
+      break;
+
+      case AST::RangePatternBound::RangePatternBoundType::PATH: {
+	AST::RangePatternBoundPath &ref
+	  = *static_cast<AST::RangePatternBoundPath *> (bound);
+
+	ResolvePath::go (&ref.get_path ());
+      }
+      break;
+
+      case AST::RangePatternBound::RangePatternBoundType::QUALPATH: {
+	AST::RangePatternBoundQualPath &ref
+	  = *static_cast<AST::RangePatternBoundQualPath *> (bound);
+
+	ResolvePath::go (&ref.get_qualified_path ());
+      }
+      break;
+    }
+}
+
+void
+PatternDeclaration::visit (AST::RangePattern &pattern)
+{
+  resolve_range_pattern_bound (pattern.get_upper_bound ().get ());
+  resolve_range_pattern_bound (pattern.get_lower_bound ().get ());
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/resolve/rust-ast-resolve-pattern.h b/gcc/rust/resolve/rust-ast-resolve-pattern.h
new file mode 100644
index 00000000000..fcbb23fdf08
--- /dev/null
+++ b/gcc/rust/resolve/rust-ast-resolve-pattern.h
@@ -0,0 +1,98 @@
+// 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_AST_RESOLVE_PATTERN_H
+#define RUST_AST_RESOLVE_PATTERN_H
+
+#include "rust-ast-resolve-base.h"
+#include "rust-ast-full.h"
+
+namespace Rust {
+namespace Resolver {
+
+class ResolvePattern : public ResolverBase
+{
+  using Rust::Resolver::ResolverBase::visit;
+
+public:
+  static void go (AST::Pattern *pattern)
+  {
+    ResolvePattern resolver;
+    pattern->accept_vis (resolver);
+  }
+
+  void visit (AST::IdentifierPattern &pattern) override
+  {
+    if (resolver->get_name_scope ().lookup (
+	  CanonicalPath::new_seg (pattern.get_node_id (), pattern.get_ident ()),
+	  &resolved_node))
+      {
+	resolver->insert_resolved_name (pattern.get_node_id (), resolved_node);
+      }
+  }
+
+private:
+  ResolvePattern () : ResolverBase () {}
+};
+
+class PatternDeclaration : public ResolverBase
+{
+  using Rust::Resolver::ResolverBase::visit;
+
+public:
+  static void go (AST::Pattern *pattern)
+  {
+    PatternDeclaration resolver;
+    pattern->accept_vis (resolver);
+  };
+
+  void visit (AST::IdentifierPattern &pattern) override
+  {
+    // if we have a duplicate id this then allows for shadowing correctly
+    // as new refs to this decl will match back here so it is ok to overwrite
+    resolver->get_name_scope ().insert (
+      CanonicalPath::new_seg (pattern.get_node_id (), pattern.get_ident ()),
+      pattern.get_node_id (), pattern.get_locus ());
+  }
+
+  void visit (AST::WildcardPattern &pattern) override
+  {
+    resolver->get_name_scope ().insert (
+      CanonicalPath::new_seg (pattern.get_node_id (), "_"),
+      pattern.get_node_id (), pattern.get_locus ());
+  }
+
+  // cases in a match expression
+  void visit (AST::PathInExpression &pattern) override;
+
+  void visit (AST::StructPattern &pattern) override;
+
+  void visit (AST::TupleStructPattern &pattern) override;
+
+  void visit (AST::TuplePattern &pattern) override;
+
+  void visit (AST::RangePattern &pattern) override;
+
+private:
+  PatternDeclaration () : ResolverBase () {}
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_AST_RESOLVE_PATTERN_H
diff --git a/gcc/rust/resolve/rust-ast-resolve-stmt.cc b/gcc/rust/resolve/rust-ast-resolve-stmt.cc
new file mode 100644
index 00000000000..1ce3df0891c
--- /dev/null
+++ b/gcc/rust/resolve/rust-ast-resolve-stmt.cc
@@ -0,0 +1,38 @@
+// 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/>.
+
+#include "rust-ast-resolve-item.h"
+#include "rust-ast-resolve-stmt.h"
+
+namespace Rust {
+namespace Resolver {
+
+void
+ResolveStmt::visit (AST::ExternBlock &extern_block)
+{
+  resolve_visibility (extern_block.get_visibility ());
+  for (auto &item : extern_block.get_extern_items ())
+    {
+      ResolveToplevelExternItem::go (item.get (),
+				     CanonicalPath::create_empty ());
+      ResolveExternItem::go (item.get (), prefix, canonical_prefix);
+    }
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/resolve/rust-ast-resolve-stmt.h b/gcc/rust/resolve/rust-ast-resolve-stmt.h
new file mode 100644
index 00000000000..6f21bc35a33
--- /dev/null
+++ b/gcc/rust/resolve/rust-ast-resolve-stmt.h
@@ -0,0 +1,378 @@
+// 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_AST_RESOLVE_STMT_H
+#define RUST_AST_RESOLVE_STMT_H
+
+#include "rust-ast-resolve-base.h"
+#include "rust-ast-full.h"
+#include "rust-ast-resolve-type.h"
+#include "rust-ast-resolve-pattern.h"
+#include "rust-ast-resolve-expr.h"
+
+namespace Rust {
+namespace Resolver {
+
+class ResolveStmt : public ResolverBase
+{
+  using Rust::Resolver::ResolverBase::visit;
+
+public:
+  static void go (AST::Stmt *stmt, const CanonicalPath &prefix,
+		  const CanonicalPath &canonical_prefix,
+		  const CanonicalPath &enum_prefix)
+  {
+    if (stmt->is_marked_for_strip ())
+      return;
+
+    ResolveStmt resolver (prefix, canonical_prefix, enum_prefix);
+    stmt->accept_vis (resolver);
+  }
+
+  void visit (AST::ExprStmtWithBlock &stmt) override
+  {
+    ResolveExpr::go (stmt.get_expr ().get (), prefix, canonical_prefix);
+  }
+
+  void visit (AST::ExprStmtWithoutBlock &stmt) override
+  {
+    ResolveExpr::go (stmt.get_expr ().get (), prefix, canonical_prefix);
+  }
+
+  void visit (AST::ConstantItem &constant) override
+  {
+    auto decl = CanonicalPath::new_seg (constant.get_node_id (),
+					constant.get_identifier ());
+    auto path = decl; // this ensures we have the correct relative resolution
+    auto cpath = canonical_prefix.append (decl);
+    mappings->insert_canonical_path (constant.get_node_id (), cpath);
+
+    resolver->get_name_scope ().insert (
+      path, constant.get_node_id (), constant.get_locus (), false,
+      [&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	RichLocation r (constant.get_locus ());
+	r.add_range (locus);
+	rust_error_at (r, "redefined multiple times");
+      });
+
+    ResolveType::go (constant.get_type ().get ());
+    ResolveExpr::go (constant.get_expr ().get (), prefix, canonical_prefix);
+  }
+
+  void visit (AST::LetStmt &stmt) override
+  {
+    if (stmt.has_init_expr ())
+      {
+	ResolveExpr::go (stmt.get_init_expr ().get (), prefix,
+			 canonical_prefix);
+      }
+
+    PatternDeclaration::go (stmt.get_pattern ().get ());
+    if (stmt.has_type ())
+      ResolveType::go (stmt.get_type ().get ());
+  }
+
+  void visit (AST::TupleStruct &struct_decl) override
+  {
+    auto decl = CanonicalPath::new_seg (struct_decl.get_node_id (),
+					struct_decl.get_identifier ());
+    auto path = decl; // this ensures we have the correct relative resolution
+    auto cpath = canonical_prefix.append (decl);
+    mappings->insert_canonical_path (struct_decl.get_node_id (), cpath);
+
+    resolver->get_type_scope ().insert (
+      path, struct_decl.get_node_id (), struct_decl.get_locus (), false,
+      [&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	RichLocation r (struct_decl.get_locus ());
+	r.add_range (locus);
+	rust_error_at (r, "redefined multiple times");
+      });
+
+    NodeId scope_node_id = struct_decl.get_node_id ();
+    resolver->get_type_scope ().push (scope_node_id);
+
+    if (struct_decl.has_generics ())
+      {
+	for (auto &generic : struct_decl.get_generic_params ())
+	  ResolveGenericParam::go (generic.get (), prefix, canonical_prefix);
+      }
+
+    for (AST::TupleField &field : struct_decl.get_fields ())
+      ResolveType::go (field.get_field_type ().get ());
+
+    resolver->get_type_scope ().pop ();
+  }
+
+  void visit (AST::Enum &enum_decl) override
+  {
+    auto decl = CanonicalPath::new_seg (enum_decl.get_node_id (),
+					enum_decl.get_identifier ());
+    auto path = decl; // this ensures we have the correct relative resolution
+    auto cpath = canonical_prefix.append (decl);
+    mappings->insert_canonical_path (enum_decl.get_node_id (), cpath);
+
+    resolver->get_type_scope ().insert (
+      path, enum_decl.get_node_id (), enum_decl.get_locus (), false,
+      [&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	RichLocation r (enum_decl.get_locus ());
+	r.add_range (locus);
+	rust_error_at (r, "redefined multiple times");
+      });
+
+    NodeId scope_node_id = enum_decl.get_node_id ();
+    resolver->get_type_scope ().push (scope_node_id);
+
+    if (enum_decl.has_generics ())
+      {
+	for (auto &generic : enum_decl.get_generic_params ())
+	  ResolveGenericParam::go (generic.get (), prefix, canonical_prefix);
+      }
+
+    for (auto &variant : enum_decl.get_variants ())
+      ResolveStmt::go (variant.get (), path, canonical_prefix, path);
+
+    resolver->get_type_scope ().pop ();
+  }
+
+  void visit (AST::EnumItem &item) override
+  {
+    auto decl = enum_prefix.append (
+      CanonicalPath::new_seg (item.get_node_id (), item.get_identifier ()));
+    auto path = decl; // this ensures we have the correct relative resolution
+    auto cpath = canonical_prefix.append (decl);
+    mappings->insert_canonical_path (item.get_node_id (), cpath);
+
+    resolver->get_type_scope ().insert (
+      path, item.get_node_id (), item.get_locus (), false,
+      [&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	RichLocation r (item.get_locus ());
+	r.add_range (locus);
+	rust_error_at (r, "redefined multiple times");
+      });
+
+    // Done, no fields.
+  }
+
+  void visit (AST::EnumItemTuple &item) override
+  {
+    auto decl = enum_prefix.append (
+      CanonicalPath::new_seg (item.get_node_id (), item.get_identifier ()));
+    auto path = decl; // this ensures we have the correct relative resolution
+    auto cpath = canonical_prefix.append (decl);
+    mappings->insert_canonical_path (item.get_node_id (), cpath);
+
+    resolver->get_type_scope ().insert (
+      path, item.get_node_id (), item.get_locus (), false,
+      [&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	RichLocation r (item.get_locus ());
+	r.add_range (locus);
+	rust_error_at (r, "redefined multiple times");
+      });
+
+    for (auto &field : item.get_tuple_fields ())
+      {
+	if (field.get_field_type ()->is_marked_for_strip ())
+	  continue;
+
+	ResolveType::go (field.get_field_type ().get ());
+      }
+  }
+
+  void visit (AST::EnumItemStruct &item) override
+  {
+    auto decl = enum_prefix.append (
+      CanonicalPath::new_seg (item.get_node_id (), item.get_identifier ()));
+    auto path = decl; // this ensures we have the correct relative resolution
+    auto cpath = canonical_prefix.append (decl);
+    mappings->insert_canonical_path (item.get_node_id (), cpath);
+
+    resolver->get_type_scope ().insert (
+      path, item.get_node_id (), item.get_locus (), false,
+      [&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	RichLocation r (item.get_locus ());
+	r.add_range (locus);
+	rust_error_at (r, "redefined multiple times");
+      });
+
+    for (auto &field : item.get_struct_fields ())
+      {
+	if (field.get_field_type ()->is_marked_for_strip ())
+	  continue;
+
+	ResolveType::go (field.get_field_type ().get ());
+      }
+  }
+
+  void visit (AST::EnumItemDiscriminant &item) override
+  {
+    auto decl = enum_prefix.append (
+      CanonicalPath::new_seg (item.get_node_id (), item.get_identifier ()));
+    auto path = decl; // this ensures we have the correct relative resolution
+    auto cpath = canonical_prefix.append (decl);
+    mappings->insert_canonical_path (item.get_node_id (), cpath);
+
+    resolver->get_type_scope ().insert (
+      path, item.get_node_id (), item.get_locus (), false,
+      [&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	RichLocation r (item.get_locus ());
+	r.add_range (locus);
+	rust_error_at (r, "redefined multiple times");
+      });
+
+    // Done, no fields.
+  }
+
+  void visit (AST::StructStruct &struct_decl) override
+  {
+    auto decl = CanonicalPath::new_seg (struct_decl.get_node_id (),
+					struct_decl.get_identifier ());
+    auto path = decl; // this ensures we have the correct relative resolution
+    auto cpath = canonical_prefix.append (decl);
+    mappings->insert_canonical_path (struct_decl.get_node_id (), cpath);
+
+    resolver->get_type_scope ().insert (
+      path, struct_decl.get_node_id (), struct_decl.get_locus (), false,
+      [&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	RichLocation r (struct_decl.get_locus ());
+	r.add_range (locus);
+	rust_error_at (r, "redefined multiple times");
+      });
+
+    NodeId scope_node_id = struct_decl.get_node_id ();
+    resolver->get_type_scope ().push (scope_node_id);
+
+    if (struct_decl.has_generics ())
+      {
+	for (auto &generic : struct_decl.get_generic_params ())
+	  ResolveGenericParam::go (generic.get (), prefix, canonical_prefix);
+      }
+
+    for (AST::StructField &field : struct_decl.get_fields ())
+      {
+	if (field.get_field_type ()->is_marked_for_strip ())
+	  continue;
+
+	ResolveType::go (field.get_field_type ().get ());
+      }
+
+    resolver->get_type_scope ().pop ();
+  }
+
+  void visit (AST::Union &union_decl) override
+  {
+    auto decl = CanonicalPath::new_seg (union_decl.get_node_id (),
+					union_decl.get_identifier ());
+    auto path = decl; // this ensures we have the correct relative resolution
+    auto cpath = canonical_prefix.append (decl);
+    mappings->insert_canonical_path (union_decl.get_node_id (), cpath);
+
+    resolver->get_type_scope ().insert (
+      path, union_decl.get_node_id (), union_decl.get_locus (), false,
+      [&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	RichLocation r (union_decl.get_locus ());
+	r.add_range (locus);
+	rust_error_at (r, "redefined multiple times");
+      });
+
+    NodeId scope_node_id = union_decl.get_node_id ();
+    resolver->get_type_scope ().push (scope_node_id);
+
+    if (union_decl.has_generics ())
+      for (auto &generic : union_decl.get_generic_params ())
+	ResolveGenericParam::go (generic.get (), prefix, canonical_prefix);
+
+    for (AST::StructField &field : union_decl.get_variants ())
+      {
+	if (field.get_field_type ()->is_marked_for_strip ())
+	  continue;
+
+	ResolveType::go (field.get_field_type ().get ());
+      }
+
+    resolver->get_type_scope ().pop ();
+  }
+
+  void visit (AST::Function &function) override
+  {
+    auto decl = CanonicalPath::new_seg (function.get_node_id (),
+					function.get_function_name ());
+    auto path = decl; // this ensures we have the correct relative resolution
+    auto cpath = canonical_prefix.append (decl);
+    mappings->insert_canonical_path (function.get_node_id (), cpath);
+
+    resolver->get_name_scope ().insert (
+      path, function.get_node_id (), function.get_locus (), false,
+      [&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	RichLocation r (function.get_locus ());
+	r.add_range (locus);
+	rust_error_at (r, "redefined multiple times");
+      });
+
+    NodeId scope_node_id = function.get_node_id ();
+    resolver->get_name_scope ().push (scope_node_id);
+    resolver->get_type_scope ().push (scope_node_id);
+    resolver->get_label_scope ().push (scope_node_id);
+    resolver->push_new_name_rib (resolver->get_name_scope ().peek ());
+    resolver->push_new_type_rib (resolver->get_type_scope ().peek ());
+    resolver->push_new_label_rib (resolver->get_type_scope ().peek ());
+
+    if (function.has_generics ())
+      for (auto &generic : function.get_generic_params ())
+	ResolveGenericParam::go (generic.get (), prefix, canonical_prefix);
+
+    if (function.has_return_type ())
+      ResolveType::go (function.get_return_type ().get ());
+
+    // we make a new scope so the names of parameters are resolved and shadowed
+    // correctly
+    for (auto &param : function.get_function_params ())
+      {
+	ResolveType::go (param.get_type ().get ());
+	PatternDeclaration::go (param.get_pattern ().get ());
+      }
+
+    // resolve the function body
+    ResolveExpr::go (function.get_definition ().get (), path, cpath);
+
+    resolver->get_name_scope ().pop ();
+    resolver->get_type_scope ().pop ();
+    resolver->get_label_scope ().pop ();
+  }
+
+  void visit (AST::ExternBlock &extern_block) override;
+
+private:
+  ResolveStmt (const CanonicalPath &prefix,
+	       const CanonicalPath &canonical_prefix,
+	       const CanonicalPath &enum_prefix)
+    : ResolverBase (), prefix (prefix), canonical_prefix (canonical_prefix),
+      enum_prefix (enum_prefix)
+  {}
+
+  const CanonicalPath &prefix;
+  const CanonicalPath &canonical_prefix;
+
+  /* item declaration statements are not given a canonical path, but enum items
+   * (variants) do inherit the enum path/identifier name.  */
+  const CanonicalPath &enum_prefix;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_AST_RESOLVE_STMT_H
diff --git a/gcc/rust/resolve/rust-ast-resolve-struct-expr-field.cc b/gcc/rust/resolve/rust-ast-resolve-struct-expr-field.cc
new file mode 100644
index 00000000000..4d8b6c788f3
--- /dev/null
+++ b/gcc/rust/resolve/rust-ast-resolve-struct-expr-field.cc
@@ -0,0 +1,61 @@
+// 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/>.
+
+#include "rust-ast-resolve-struct-expr-field.h"
+#include "rust-ast-resolve-expr.h"
+
+namespace Rust {
+namespace Resolver {
+
+void
+ResolveStructExprField::go (AST::StructExprField *field,
+			    const CanonicalPath &prefix,
+			    const CanonicalPath &canonical_prefix)
+{
+  ResolveStructExprField resolver (prefix, canonical_prefix);
+  field->accept_vis (resolver);
+}
+
+ResolveStructExprField::ResolveStructExprField (
+  const CanonicalPath &prefix, const CanonicalPath &canonical_prefix)
+  : ResolverBase (), prefix (prefix), canonical_prefix (canonical_prefix)
+{}
+
+void
+ResolveStructExprField::visit (AST::StructExprFieldIdentifierValue &field)
+{
+  ResolveExpr::go (field.get_value ().get (), prefix, canonical_prefix);
+}
+
+void
+ResolveStructExprField::visit (AST::StructExprFieldIndexValue &field)
+{
+  ResolveExpr::go (field.get_value ().get (), prefix, canonical_prefix);
+}
+
+void
+ResolveStructExprField::visit (AST::StructExprFieldIdentifier &field)
+{
+  AST::IdentifierExpr expr (field.get_field_name (), {}, field.get_locus ());
+  expr.set_node_id (field.get_node_id ());
+
+  ResolveExpr::go (&expr, prefix, canonical_prefix);
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/resolve/rust-ast-resolve-struct-expr-field.h b/gcc/rust/resolve/rust-ast-resolve-struct-expr-field.h
new file mode 100644
index 00000000000..ce60b136e4b
--- /dev/null
+++ b/gcc/rust/resolve/rust-ast-resolve-struct-expr-field.h
@@ -0,0 +1,55 @@
+// 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_AST_RESOLVE_STRUCT_EXPR_FIELD
+#define RUST_AST_RESOLVE_STRUCT_EXPR_FIELD
+
+#include "rust-ast-resolve-base.h"
+#include "rust-ast-full.h"
+
+namespace Rust {
+namespace Resolver {
+
+// this resolves values being assigned not that the field actually exists yet.
+
+class ResolveStructExprField : public ResolverBase
+{
+  using Rust::Resolver::ResolverBase::visit;
+
+public:
+  static void go (AST::StructExprField *field, const CanonicalPath &prefix,
+		  const CanonicalPath &canonical_prefix);
+
+  void visit (AST::StructExprFieldIdentifierValue &field) override;
+
+  void visit (AST::StructExprFieldIndexValue &field) override;
+
+  void visit (AST::StructExprFieldIdentifier &field) override;
+
+private:
+  ResolveStructExprField (const CanonicalPath &prefix,
+			  const CanonicalPath &canonical_prefix);
+
+  const CanonicalPath &prefix;
+  const CanonicalPath &canonical_prefix;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_AST_RESOLVE_STRUCT_EXPR_FIELD
diff --git a/gcc/rust/resolve/rust-ast-resolve-toplevel.h b/gcc/rust/resolve/rust-ast-resolve-toplevel.h
new file mode 100644
index 00000000000..43ae8e47673
--- /dev/null
+++ b/gcc/rust/resolve/rust-ast-resolve-toplevel.h
@@ -0,0 +1,460 @@
+// 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_AST_RESOLVE_TOPLEVEL_H
+#define RUST_AST_RESOLVE_TOPLEVEL_H
+
+#include "rust-ast-resolve-base.h"
+#include "rust-ast-resolve-type.h"
+#include "rust-ast-resolve-implitem.h"
+#include "rust-ast-full.h"
+#include "rust-name-resolver.h"
+#include "rust-session-manager.h"
+
+namespace Rust {
+namespace Resolver {
+
+class ResolveTopLevel : public ResolverBase
+{
+  using Rust::Resolver::ResolverBase::visit;
+
+public:
+  static void go (AST::Item *item, const CanonicalPath &prefix,
+		  const CanonicalPath &canonical_prefix)
+  {
+    if (item->is_marked_for_strip ())
+      return;
+
+    ResolveTopLevel resolver (prefix, canonical_prefix);
+    item->accept_vis (resolver);
+
+    NodeId current_module = resolver.resolver->peek_current_module_scope ();
+    resolver.mappings->insert_child_item_to_parent_module_mapping (
+      item->get_node_id (), current_module);
+  }
+
+  void visit (AST::Module &module) override
+  {
+    auto mod
+      = CanonicalPath::new_seg (module.get_node_id (), module.get_name ());
+    auto path = prefix.append (mod);
+    auto cpath = canonical_prefix.append (mod);
+
+    resolver->get_name_scope ().insert (
+      path, module.get_node_id (), module.get_locus (), false,
+      [&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	RichLocation r (module.get_locus ());
+	r.add_range (locus);
+	rust_error_at (r, "redefined multiple times");
+      });
+
+    NodeId current_module = resolver->peek_current_module_scope ();
+    mappings->insert_module_child_item (current_module, mod);
+    mappings->insert_module_child (current_module, module.get_node_id ());
+
+    resolver->push_new_module_scope (module.get_node_id ());
+    for (auto &item : module.get_items ())
+      ResolveTopLevel::go (item.get (), path, cpath);
+
+    resolver->pop_module_scope ();
+
+    mappings->insert_canonical_path (module.get_node_id (), cpath);
+  }
+
+  void visit (AST::TypeAlias &alias) override
+  {
+    auto talias = CanonicalPath::new_seg (alias.get_node_id (),
+					  alias.get_new_type_name ());
+    auto path = prefix.append (talias);
+    auto cpath = canonical_prefix.append (talias);
+
+    resolver->get_type_scope ().insert (
+      path, alias.get_node_id (), alias.get_locus (), false,
+      [&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	RichLocation r (alias.get_locus ());
+	r.add_range (locus);
+	rust_error_at (r, "redefined multiple times");
+      });
+
+    NodeId current_module = resolver->peek_current_module_scope ();
+    mappings->insert_module_child_item (current_module, talias);
+    mappings->insert_canonical_path (alias.get_node_id (), cpath);
+  }
+
+  void visit (AST::TupleStruct &struct_decl) override
+  {
+    auto decl = CanonicalPath::new_seg (struct_decl.get_node_id (),
+					struct_decl.get_identifier ());
+    auto path = prefix.append (decl);
+    auto cpath = canonical_prefix.append (decl);
+
+    resolver->get_type_scope ().insert (
+      path, struct_decl.get_node_id (), struct_decl.get_locus (), false,
+      [&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	RichLocation r (struct_decl.get_locus ());
+	r.add_range (locus);
+	rust_error_at (r, "redefined multiple times");
+      });
+
+    NodeId current_module = resolver->peek_current_module_scope ();
+    mappings->insert_module_child_item (current_module, decl);
+    mappings->insert_canonical_path (struct_decl.get_node_id (), cpath);
+  }
+
+  void visit (AST::Enum &enum_decl) override
+  {
+    auto decl = CanonicalPath::new_seg (enum_decl.get_node_id (),
+					enum_decl.get_identifier ());
+    auto path = prefix.append (decl);
+    auto cpath = canonical_prefix.append (decl);
+
+    resolver->get_type_scope ().insert (
+      path, enum_decl.get_node_id (), enum_decl.get_locus (), false,
+      [&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	RichLocation r (enum_decl.get_locus ());
+	r.add_range (locus);
+	rust_error_at (r, "redefined multiple times");
+      });
+
+    for (auto &variant : enum_decl.get_variants ())
+      ResolveTopLevel::go (variant.get (), path, cpath);
+
+    NodeId current_module = resolver->peek_current_module_scope ();
+    mappings->insert_module_child_item (current_module, decl);
+    mappings->insert_canonical_path (enum_decl.get_node_id (), cpath);
+  }
+
+  void visit (AST::EnumItem &item) override
+  {
+    auto decl
+      = CanonicalPath::new_seg (item.get_node_id (), item.get_identifier ());
+    auto path = prefix.append (decl);
+    auto cpath = canonical_prefix.append (decl);
+
+    resolver->get_type_scope ().insert (
+      path, item.get_node_id (), item.get_locus (), false,
+      [&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	RichLocation r (item.get_locus ());
+	r.add_range (locus);
+	rust_error_at (r, "redefined multiple times");
+      });
+
+    mappings->insert_canonical_path (item.get_node_id (), cpath);
+  }
+
+  void visit (AST::EnumItemTuple &item) override
+  {
+    auto decl
+      = CanonicalPath::new_seg (item.get_node_id (), item.get_identifier ());
+    auto path = prefix.append (decl);
+    auto cpath = canonical_prefix.append (decl);
+
+    resolver->get_type_scope ().insert (
+      path, item.get_node_id (), item.get_locus (), false,
+      [&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	RichLocation r (item.get_locus ());
+	r.add_range (locus);
+	rust_error_at (r, "redefined multiple times");
+      });
+
+    mappings->insert_canonical_path (item.get_node_id (), cpath);
+  }
+
+  void visit (AST::EnumItemStruct &item) override
+  {
+    auto decl
+      = CanonicalPath::new_seg (item.get_node_id (), item.get_identifier ());
+    auto path = prefix.append (decl);
+    auto cpath = canonical_prefix.append (decl);
+
+    resolver->get_type_scope ().insert (
+      path, item.get_node_id (), item.get_locus (), false,
+      [&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	RichLocation r (item.get_locus ());
+	r.add_range (locus);
+	rust_error_at (r, "redefined multiple times");
+      });
+
+    mappings->insert_canonical_path (item.get_node_id (), cpath);
+  }
+
+  void visit (AST::EnumItemDiscriminant &item) override
+  {
+    auto decl
+      = CanonicalPath::new_seg (item.get_node_id (), item.get_identifier ());
+    auto path = prefix.append (decl);
+    auto cpath = canonical_prefix.append (decl);
+
+    resolver->get_type_scope ().insert (
+      path, item.get_node_id (), item.get_locus (), false,
+      [&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	RichLocation r (item.get_locus ());
+	r.add_range (locus);
+	rust_error_at (r, "redefined multiple times");
+      });
+
+    mappings->insert_canonical_path (item.get_node_id (), cpath);
+  }
+
+  void visit (AST::StructStruct &struct_decl) override
+  {
+    auto decl = CanonicalPath::new_seg (struct_decl.get_node_id (),
+					struct_decl.get_identifier ());
+    auto path = prefix.append (decl);
+    auto cpath = canonical_prefix.append (decl);
+
+    resolver->get_type_scope ().insert (
+      path, struct_decl.get_node_id (), struct_decl.get_locus (), false,
+      [&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	RichLocation r (struct_decl.get_locus ());
+	r.add_range (locus);
+	rust_error_at (r, "redefined multiple times");
+      });
+
+    NodeId current_module = resolver->peek_current_module_scope ();
+    mappings->insert_module_child_item (current_module, decl);
+    mappings->insert_canonical_path (struct_decl.get_node_id (), cpath);
+  }
+
+  void visit (AST::Union &union_decl) override
+  {
+    auto decl = CanonicalPath::new_seg (union_decl.get_node_id (),
+					union_decl.get_identifier ());
+    auto path = prefix.append (decl);
+    auto cpath = canonical_prefix.append (decl);
+
+    resolver->get_type_scope ().insert (
+      path, union_decl.get_node_id (), union_decl.get_locus (), false,
+      [&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	RichLocation r (union_decl.get_locus ());
+	r.add_range (locus);
+	rust_error_at (r, "redefined multiple times");
+      });
+
+    NodeId current_module = resolver->peek_current_module_scope ();
+    mappings->insert_module_child_item (current_module, decl);
+    mappings->insert_canonical_path (union_decl.get_node_id (), cpath);
+  }
+
+  void visit (AST::StaticItem &var) override
+  {
+    auto decl
+      = CanonicalPath::new_seg (var.get_node_id (), var.get_identifier ());
+    auto path = prefix.append (decl);
+    auto cpath = canonical_prefix.append (decl);
+
+    resolver->get_name_scope ().insert (
+      path, var.get_node_id (), var.get_locus (), false,
+      [&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	RichLocation r (var.get_locus ());
+	r.add_range (locus);
+	rust_error_at (r, "redefined multiple times");
+      });
+
+    NodeId current_module = resolver->peek_current_module_scope ();
+    mappings->insert_module_child_item (current_module, decl);
+    mappings->insert_canonical_path (var.get_node_id (), cpath);
+  }
+
+  void visit (AST::ConstantItem &constant) override
+  {
+    auto decl = CanonicalPath::new_seg (constant.get_node_id (),
+					constant.get_identifier ());
+    auto path = prefix.append (decl);
+    auto cpath = canonical_prefix.append (decl);
+
+    resolver->get_name_scope ().insert (
+      path, constant.get_node_id (), constant.get_locus (), false,
+      [&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	RichLocation r (constant.get_locus ());
+	r.add_range (locus);
+	rust_error_at (r, "redefined multiple times");
+      });
+
+    NodeId current_module = resolver->peek_current_module_scope ();
+    mappings->insert_module_child_item (current_module, decl);
+    mappings->insert_canonical_path (constant.get_node_id (), cpath);
+  }
+
+  void visit (AST::Function &function) override
+  {
+    auto decl = CanonicalPath::new_seg (function.get_node_id (),
+					function.get_function_name ());
+    auto path = prefix.append (decl);
+    auto cpath = canonical_prefix.append (decl);
+
+    resolver->get_name_scope ().insert (
+      path, function.get_node_id (), function.get_locus (), false,
+      [&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	RichLocation r (function.get_locus ());
+	r.add_range (locus);
+	rust_error_at (r, "redefined multiple times");
+      });
+
+    NodeId current_module = resolver->peek_current_module_scope ();
+    mappings->insert_module_child_item (current_module, decl);
+    mappings->insert_canonical_path (function.get_node_id (), cpath);
+  }
+
+  void visit (AST::InherentImpl &impl_block) override
+  {
+    std::string raw_impl_type_path = impl_block.get_type ()->as_string ();
+    CanonicalPath impl_type
+      = CanonicalPath::new_seg (impl_block.get_type ()->get_node_id (),
+				raw_impl_type_path);
+    CanonicalPath impl_prefix = prefix.append (impl_type);
+
+    for (auto &impl_item : impl_block.get_impl_items ())
+      ResolveToplevelImplItem::go (impl_item.get (), impl_prefix);
+  }
+
+  void visit (AST::TraitImpl &impl_block) override
+  {
+    std::string raw_impl_type_path = impl_block.get_type ()->as_string ();
+    CanonicalPath impl_type_seg
+      = CanonicalPath::new_seg (impl_block.get_type ()->get_node_id (),
+				raw_impl_type_path);
+
+    std::string raw_trait_type_path = impl_block.get_trait_path ().as_string ();
+    CanonicalPath trait_type_seg
+      = CanonicalPath::new_seg (impl_block.get_trait_path ().get_node_id (),
+				raw_trait_type_path);
+
+    CanonicalPath projection
+      = CanonicalPath::trait_impl_projection_seg (impl_block.get_node_id (),
+						  trait_type_seg,
+						  impl_type_seg);
+    CanonicalPath impl_prefix = prefix.append (projection);
+
+    resolver->get_name_scope ().insert (
+      impl_prefix, impl_block.get_node_id (), impl_block.get_locus (), false,
+      [&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	RichLocation r (impl_block.get_locus ());
+	r.add_range (locus);
+	rust_error_at (r, "redefined multiple times");
+      });
+
+    for (auto &impl_item : impl_block.get_impl_items ())
+      ResolveToplevelImplItem::go (impl_item.get (), impl_prefix);
+  }
+
+  void visit (AST::Trait &trait) override
+  {
+    auto decl
+      = CanonicalPath::new_seg (trait.get_node_id (), trait.get_identifier ());
+    auto path = prefix.append (decl);
+    auto cpath = canonical_prefix.append (decl);
+
+    resolver->get_type_scope ().insert (
+      path, trait.get_node_id (), trait.get_locus (), false,
+      [&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	RichLocation r (trait.get_locus ());
+	r.add_range (locus);
+	rust_error_at (r, "redefined multiple times");
+      });
+
+    for (auto &item : trait.get_trait_items ())
+      ResolveTopLevelTraitItems::go (item.get (), path, cpath);
+
+    NodeId current_module = resolver->peek_current_module_scope ();
+    mappings->insert_module_child_item (current_module, decl);
+    mappings->insert_canonical_path (trait.get_node_id (), cpath);
+  }
+
+  void visit (AST::ExternBlock &extern_block) override
+  {
+    for (auto &item : extern_block.get_extern_items ())
+      {
+	ResolveToplevelExternItem::go (item.get (), prefix);
+      }
+  }
+
+  void visit (AST::ExternCrate &extern_crate) override
+  {
+    if (extern_crate.is_marked_for_strip ())
+      return;
+
+    NodeId resolved_crate = UNKNOWN_NODEID;
+    if (extern_crate.references_self ())
+      {
+	CrateNum crate_num = mappings->get_current_crate ();
+	bool ok = mappings->crate_num_to_nodeid (crate_num, resolved_crate);
+	rust_assert (ok);
+      }
+    else
+      {
+	CrateNum found_crate_num = UNKNOWN_CREATENUM;
+	bool found
+	  = mappings->lookup_crate_name (extern_crate.get_referenced_crate (),
+					 found_crate_num);
+	if (!found)
+	  {
+	    rust_error_at (extern_crate.get_locus (), "unknown crate %<%s%>",
+			   extern_crate.get_referenced_crate ().c_str ());
+	    return;
+	  }
+
+	bool ok
+	  = mappings->crate_num_to_nodeid (found_crate_num, resolved_crate);
+	if (!ok)
+	  {
+	    rust_internal_error_at (extern_crate.get_locus (),
+				    "failed to resolve crate to nodeid");
+	    return;
+	  }
+      }
+
+    if (resolved_crate == UNKNOWN_NODEID)
+      {
+	rust_error_at (extern_crate.get_locus (), "failed to resolve crate");
+	return;
+      }
+
+    // mark the node as resolved
+    resolver->insert_resolved_name (extern_crate.get_node_id (),
+				    resolved_crate);
+    CanonicalPath decl
+      = extern_crate.has_as_clause ()
+	  ? CanonicalPath::new_seg (extern_crate.get_node_id (),
+				    extern_crate.get_as_clause ())
+	  : CanonicalPath::new_seg (extern_crate.get_node_id (),
+				    extern_crate.get_referenced_crate ());
+
+    resolver->get_type_scope ().insert (
+      decl, resolved_crate, extern_crate.get_locus (), false,
+      [&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	RichLocation r (extern_crate.get_locus ());
+	r.add_range (locus);
+	rust_error_at (r, "redefined multiple times");
+      });
+  }
+
+private:
+  ResolveTopLevel (const CanonicalPath &prefix,
+		   const CanonicalPath &canonical_prefix)
+    : ResolverBase (), prefix (prefix), canonical_prefix (canonical_prefix)
+  {}
+
+  const CanonicalPath &prefix;
+  const CanonicalPath &canonical_prefix;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_AST_RESOLVE_TOPLEVEL_H
diff --git a/gcc/rust/resolve/rust-ast-resolve-type.cc b/gcc/rust/resolve/rust-ast-resolve-type.cc
new file mode 100644
index 00000000000..6b08613755a
--- /dev/null
+++ b/gcc/rust/resolve/rust-ast-resolve-type.cc
@@ -0,0 +1,582 @@
+// 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/>.
+
+#include "rust-ast-resolve-type.h"
+#include "rust-ast-resolve-expr.h"
+
+namespace Rust {
+namespace Resolver {
+
+// rust-ast-resolve-type.h
+
+void
+ResolveType::visit (AST::ArrayType &type)
+{
+  type.get_elem_type ()->accept_vis (*this);
+  ResolveExpr::go (type.get_size_expr ().get (), CanonicalPath::create_empty (),
+		   CanonicalPath::create_empty ());
+}
+
+void
+ResolveType::visit (AST::TraitObjectTypeOneBound &type)
+{
+  ResolveTypeBound::go (&type.get_trait_bound ());
+}
+
+void
+ResolveType::visit (AST::TraitObjectType &type)
+{
+  for (auto &bound : type.get_type_param_bounds ())
+    {
+      /* NodeId bound_resolved_id = */
+      ResolveTypeBound::go (bound.get ());
+    }
+}
+
+void
+ResolveType::visit (AST::ReferenceType &type)
+{
+  resolved_node = ResolveType::go (type.get_type_referenced ().get ());
+}
+
+void
+ResolveType::visit (AST::RawPointerType &type)
+{
+  resolved_node = ResolveType::go (type.get_type_pointed_to ().get ());
+}
+
+void
+ResolveType::visit (AST::InferredType &type)
+{
+  // FIXME
+}
+
+void
+ResolveType::visit (AST::NeverType &type)
+{
+  // FIXME
+}
+
+void
+ResolveType::visit (AST::SliceType &type)
+{
+  resolved_node = ResolveType::go (type.get_elem_type ().get ());
+}
+
+// resolve relative type-paths
+
+bool
+ResolveRelativeTypePath::go (AST::TypePath &path, NodeId &resolved_node_id)
+{
+  auto resolver = Resolver::get ();
+  auto mappings = Analysis::Mappings::get ();
+
+  NodeId module_scope_id = resolver->peek_current_module_scope ();
+  NodeId previous_resolved_node_id = module_scope_id;
+  for (size_t i = 0; i < path.get_segments ().size (); i++)
+    {
+      auto &segment = path.get_segments ().at (i);
+      const AST::PathIdentSegment &ident_seg = segment->get_ident_segment ();
+      bool is_first_segment = i == 0;
+      resolved_node_id = UNKNOWN_NODEID;
+
+      bool in_middle_of_path = i > 0;
+      if (in_middle_of_path && segment->is_lower_self_seg ())
+	{
+	  // error[E0433]: failed to resolve: `self` in paths can only be used
+	  // in start position
+	  rust_error_at (segment->get_locus (),
+			 "failed to resolve: %<%s%> in paths can only be used "
+			 "in start position",
+			 segment->as_string ().c_str ());
+	  return false;
+	}
+
+      NodeId crate_scope_id = resolver->peek_crate_module_scope ();
+      if (segment->is_crate_path_seg ())
+	{
+	  // what is the current crate scope node id?
+	  module_scope_id = crate_scope_id;
+	  previous_resolved_node_id = module_scope_id;
+	  resolver->insert_resolved_name (segment->get_node_id (),
+					  module_scope_id);
+
+	  continue;
+	}
+      else if (segment->is_super_path_seg ())
+	{
+	  if (module_scope_id == crate_scope_id)
+	    {
+	      rust_error_at (segment->get_locus (),
+			     "cannot use super at the crate scope");
+	      return false;
+	    }
+
+	  module_scope_id = resolver->peek_parent_module_scope ();
+	  previous_resolved_node_id = module_scope_id;
+	  resolver->insert_resolved_name (segment->get_node_id (),
+					  module_scope_id);
+	  continue;
+	}
+
+      switch (segment->get_type ())
+	{
+	  case AST::TypePathSegment::SegmentType::GENERIC: {
+	    AST::TypePathSegmentGeneric *s
+	      = static_cast<AST::TypePathSegmentGeneric *> (segment.get ());
+	    if (s->has_generic_args ())
+	      ResolveGenericArgs::go (s->get_generic_args ());
+	  }
+	  break;
+
+	case AST::TypePathSegment::SegmentType::REG:
+	  // nothing to do
+	  break;
+
+	case AST::TypePathSegment::SegmentType::FUNCTION:
+	  gcc_unreachable ();
+	  break;
+	}
+
+      if (is_first_segment)
+	{
+	  // name scope first
+	  NodeId resolved_node = UNKNOWN_NODEID;
+	  const CanonicalPath path
+	    = CanonicalPath::new_seg (segment->get_node_id (),
+				      ident_seg.as_string ());
+	  if (resolver->get_type_scope ().lookup (path, &resolved_node))
+	    {
+	      resolver->insert_resolved_type (segment->get_node_id (),
+					      resolved_node);
+	      resolved_node_id = resolved_node;
+	    }
+	  else if (resolver->get_name_scope ().lookup (path, &resolved_node))
+	    {
+	      resolver->insert_resolved_name (segment->get_node_id (),
+					      resolved_node);
+	      resolved_node_id = resolved_node;
+	    }
+	  else if (segment->is_lower_self_seg ())
+	    {
+	      // what is the current crate scope node id?
+	      module_scope_id = crate_scope_id;
+	      previous_resolved_node_id = module_scope_id;
+	      resolver->insert_resolved_name (segment->get_node_id (),
+					      module_scope_id);
+
+	      continue;
+	    }
+	}
+
+      if (resolved_node_id == UNKNOWN_NODEID
+	  && previous_resolved_node_id == module_scope_id)
+	{
+	  Optional<CanonicalPath &> resolved_child
+	    = mappings->lookup_module_child (module_scope_id,
+					     ident_seg.as_string ());
+	  if (resolved_child.is_some ())
+	    {
+	      NodeId resolved_node = resolved_child->get_node_id ();
+	      if (resolver->get_name_scope ().decl_was_declared_here (
+		    resolved_node))
+		{
+		  resolved_node_id = resolved_node;
+		  resolver->insert_resolved_name (segment->get_node_id (),
+						  resolved_node);
+		}
+	      else if (resolver->get_type_scope ().decl_was_declared_here (
+			 resolved_node))
+		{
+		  resolved_node_id = resolved_node;
+		  resolver->insert_resolved_type (segment->get_node_id (),
+						  resolved_node);
+		}
+	      else
+		{
+		  rust_error_at (segment->get_locus (),
+				 "Cannot find path %<%s%> in this scope",
+				 segment->as_string ().c_str ());
+		  return false;
+		}
+	    }
+	}
+
+      bool did_resolve_segment = resolved_node_id != UNKNOWN_NODEID;
+      if (did_resolve_segment)
+	{
+	  if (mappings->node_is_module (resolved_node_id)
+	      || mappings->node_is_crate (resolved_node_id))
+	    {
+	      module_scope_id = resolved_node_id;
+	    }
+	  previous_resolved_node_id = resolved_node_id;
+	}
+      else if (is_first_segment)
+	{
+	  rust_error_at (segment->get_locus (),
+			 "failed to resolve TypePath: %s in this scope",
+			 segment->as_string ().c_str ());
+	  return false;
+	}
+    }
+
+  if (resolved_node_id != UNKNOWN_NODEID)
+    {
+      // name scope first
+      if (resolver->get_name_scope ().decl_was_declared_here (resolved_node_id))
+	{
+	  resolver->insert_resolved_name (path.get_node_id (),
+					  resolved_node_id);
+	}
+      // check the type scope
+      else if (resolver->get_type_scope ().decl_was_declared_here (
+		 resolved_node_id))
+	{
+	  resolver->insert_resolved_type (path.get_node_id (),
+					  resolved_node_id);
+	}
+      else
+	{
+	  gcc_unreachable ();
+	}
+    }
+
+  return true;
+}
+
+// qualified type paths
+
+ResolveRelativeQualTypePath::ResolveRelativeQualTypePath ()
+  : failure_flag (false)
+{}
+
+bool
+ResolveRelativeQualTypePath::go (AST::QualifiedPathInType &path)
+{
+  ResolveRelativeQualTypePath o;
+
+  // resolve the type and trait path
+  auto &qualified_path = path.get_qualified_path_type ();
+  if (!o.resolve_qual_seg (qualified_path))
+    return false;
+
+  // qualified types are similar to other paths in that we cannot guarantee
+  // that we can resolve the path at name resolution. We must look up
+  // associated types and type information to figure this out properly
+
+  std::unique_ptr<AST::TypePathSegment> &associated
+    = path.get_associated_segment ();
+
+  associated->accept_vis (o);
+  if (o.failure_flag)
+    return false;
+
+  for (auto &seg : path.get_segments ())
+    {
+      seg->accept_vis (o);
+      if (o.failure_flag)
+	return false;
+    }
+
+  return true;
+}
+
+bool
+ResolveRelativeQualTypePath::resolve_qual_seg (AST::QualifiedPathType &seg)
+{
+  if (seg.is_error ())
+    {
+      rust_error_at (seg.get_locus (), "segment has error: %s",
+		     seg.as_string ().c_str ());
+      return false;
+    }
+
+  auto type = seg.get_type ().get ();
+  NodeId type_resolved_node = ResolveType::go (type);
+  if (type_resolved_node == UNKNOWN_NODEID)
+    return false;
+
+  if (!seg.has_as_clause ())
+    return true;
+
+  NodeId trait_resolved_node = ResolveType::go (&seg.get_as_type_path ());
+  if (trait_resolved_node == UNKNOWN_NODEID)
+    return false;
+
+  return true;
+}
+
+void
+ResolveRelativeQualTypePath::visit (AST::TypePathSegmentGeneric &seg)
+{
+  if (seg.is_error ())
+    {
+      failure_flag = true;
+      rust_error_at (seg.get_locus (), "segment has error: %s",
+		     seg.as_string ().c_str ());
+      return;
+    }
+
+  ResolveGenericArgs::go (seg.get_generic_args ());
+}
+
+void
+ResolveRelativeQualTypePath::visit (AST::TypePathSegment &seg)
+{
+  if (seg.is_error ())
+    {
+      failure_flag = true;
+      rust_error_at (seg.get_locus (), "segment has error: %s",
+		     seg.as_string ().c_str ());
+      return;
+    }
+}
+
+// resolve to canonical path
+
+bool
+ResolveTypeToCanonicalPath::go (AST::Type *type, CanonicalPath &result)
+{
+  ResolveTypeToCanonicalPath resolver;
+  type->accept_vis (resolver);
+  result = resolver.result;
+  return !resolver.result.is_empty ();
+}
+
+void
+ResolveTypeToCanonicalPath::visit (AST::TypePath &path)
+{
+  NodeId resolved_node = UNKNOWN_NODEID;
+  if (!resolver->lookup_resolved_name (path.get_node_id (), &resolved_node))
+    {
+      resolver->lookup_resolved_type (path.get_node_id (), &resolved_node);
+    }
+
+  if (resolved_node == UNKNOWN_NODEID)
+    return;
+
+  const CanonicalPath *type_path = nullptr;
+  if (mappings->lookup_canonical_path (resolved_node, &type_path))
+    {
+      auto &final_seg = path.get_segments ().back ();
+      switch (final_seg->get_type ())
+	{
+	  case AST::TypePathSegment::SegmentType::GENERIC: {
+	    AST::TypePathSegmentGeneric *s
+	      = static_cast<AST::TypePathSegmentGeneric *> (final_seg.get ());
+
+	    std::vector<CanonicalPath> args;
+	    if (s->has_generic_args ())
+	      {
+		ResolveGenericArgs::go (s->get_generic_args ());
+		for (auto &generic : s->get_generic_args ().get_generic_args ())
+		  {
+		    // FIXME: What do we want to do here in case there is a
+		    // constant or an ambiguous const generic?
+		    // TODO: At that point, will all generics have been
+		    // disambiguated? Can we thus canonical resolve types and
+		    // const and `gcc_unreachable` on ambiguous types?
+		    // This is probably fine as we just want to canonicalize
+		    // types, right?
+		    if (generic.get_kind () == AST::GenericArg::Kind::Type)
+		      {
+			CanonicalPath arg = CanonicalPath::create_empty ();
+			bool ok = ResolveTypeToCanonicalPath::go (
+			  generic.get_type ().get (), arg);
+			if (ok)
+			  args.push_back (std::move (arg));
+		      }
+		  }
+	      }
+
+	    result = *type_path;
+	    if (!args.empty ())
+	      {
+		// append this onto the path
+		std::string buf;
+		for (size_t i = 0; i < args.size (); i++)
+		  {
+		    bool has_next = (i + 1) < args.size ();
+		    const auto &arg = args.at (i);
+
+		    buf += arg.get ();
+		    if (has_next)
+		      buf += ", ";
+		  }
+
+		std::string arg_seg = "<" + buf + ">";
+		CanonicalPath argument_seg
+		  = CanonicalPath::new_seg (s->get_node_id (), arg_seg);
+		result = result.append (argument_seg);
+	      }
+	  }
+	  break;
+
+	default:
+	  result = *type_path;
+	  break;
+	}
+    }
+}
+
+void
+ResolveTypeToCanonicalPath::visit (AST::ReferenceType &type)
+{
+  CanonicalPath path = CanonicalPath::create_empty ();
+  bool ok
+    = ResolveTypeToCanonicalPath::go (type.get_type_referenced ().get (), path);
+  if (ok)
+    {
+      std::string ref_type_str = type.is_mut () ? "mut" : "";
+      std::string ref_path = "&" + ref_type_str + " " + path.get ();
+      result = CanonicalPath::new_seg (type.get_node_id (), ref_path);
+    }
+}
+
+void
+ResolveTypeToCanonicalPath::visit (AST::RawPointerType &type)
+{
+  CanonicalPath path = CanonicalPath::create_empty ();
+  bool ok
+    = ResolveTypeToCanonicalPath::go (type.get_type_pointed_to ().get (), path);
+  if (ok)
+    {
+      std::string ptr_type_str
+	= type.get_pointer_type () == AST::RawPointerType::CONST ? "const"
+								 : "mut";
+      std::string ptr_path = "*" + ptr_type_str + " " + path.get ();
+      result = CanonicalPath::new_seg (type.get_node_id (), ptr_path);
+    }
+}
+
+void
+ResolveTypeToCanonicalPath::visit (AST::SliceType &type)
+{
+  CanonicalPath path = CanonicalPath::create_empty ();
+  bool ok = ResolveTypeToCanonicalPath::go (type.get_elem_type ().get (), path);
+  if (ok)
+    {
+      std::string slice_path = "[" + path.get () + "]";
+      result = CanonicalPath::new_seg (type.get_node_id (), slice_path);
+    }
+}
+
+void
+ResolveTypeToCanonicalPath::visit (AST::TraitObjectTypeOneBound &type)
+{
+  CanonicalPath path = CanonicalPath::create_empty ();
+  bool ok
+    = ResolveTypeToCanonicalPath::go (&type.get_trait_bound ().get_type_path (),
+				      path);
+  if (ok)
+    {
+      std::string slice_path = "<dyn " + path.get () + ">";
+      result = CanonicalPath::new_seg (type.get_node_id (), slice_path);
+    }
+}
+
+void
+ResolveTypeToCanonicalPath::visit (AST::TraitObjectType &type)
+{
+  // FIXME is this actually allowed? dyn A+B
+  gcc_unreachable ();
+}
+
+ResolveTypeToCanonicalPath::ResolveTypeToCanonicalPath ()
+  : ResolverBase (), result (CanonicalPath::create_empty ())
+{}
+
+bool
+ResolveGenericArgs::is_const_value_name (const CanonicalPath &path)
+{
+  NodeId resolved;
+  auto found = resolver->get_name_scope ().lookup (path, &resolved);
+
+  return found;
+}
+
+bool
+ResolveGenericArgs::is_type_name (const CanonicalPath &path)
+{
+  NodeId resolved;
+  auto found = resolver->get_type_scope ().lookup (path, &resolved);
+
+  return found;
+}
+
+void
+ResolveGenericArgs::disambiguate (AST::GenericArg &arg)
+{
+  auto path = canonical_prefix.append (
+    CanonicalPath::new_seg (UNKNOWN_NODEID, arg.get_path ()));
+
+  auto is_type = is_type_name (path);
+  auto is_value = is_const_value_name (path);
+
+  // In case we cannot find anything, we resolve the ambiguity to a type.
+  // This causes the typechecker to error out properly and when necessary.
+  // But types also take priority over const values in the case of
+  // ambiguities, hence the weird control flow
+  if (is_type || (!is_type && !is_value))
+    arg = arg.disambiguate_to_type ();
+  else if (is_value)
+    arg = arg.disambiguate_to_const ();
+}
+
+void
+ResolveGenericArgs::resolve_disambiguated_generic (AST::GenericArg &arg)
+{
+  switch (arg.get_kind ())
+    {
+    case AST::GenericArg::Kind::Const:
+      ResolveExpr::go (arg.get_expression ().get (), prefix, canonical_prefix);
+      break;
+    case AST::GenericArg::Kind::Type:
+      ResolveType::go (arg.get_type ().get ());
+      break;
+    default:
+      gcc_unreachable ();
+    }
+}
+void
+ResolveGenericArgs::go (AST::GenericArgs &generic_args)
+{
+  auto empty = CanonicalPath::create_empty ();
+
+  go (generic_args, empty, empty);
+}
+
+void
+ResolveGenericArgs::go (AST::GenericArgs &generic_args,
+			const CanonicalPath &prefix,
+			const CanonicalPath &canonical_prefix)
+{
+  auto resolver = ResolveGenericArgs (prefix, canonical_prefix);
+
+  for (auto &arg : generic_args.get_generic_args ())
+    {
+      if (arg.get_kind () == AST::GenericArg::Kind::Either)
+	resolver.disambiguate (arg);
+
+      resolver.resolve_disambiguated_generic (arg);
+    }
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/resolve/rust-ast-resolve-type.h b/gcc/rust/resolve/rust-ast-resolve-type.h
new file mode 100644
index 00000000000..5a71268c0d4
--- /dev/null
+++ b/gcc/rust/resolve/rust-ast-resolve-type.h
@@ -0,0 +1,290 @@
+// 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_AST_RESOLVE_TYPE_H
+#define RUST_AST_RESOLVE_TYPE_H
+
+#include "rust-ast-resolve-base.h"
+#include "rust-ast-resolve-expr.h"
+#include "rust-ast-full.h"
+
+namespace Rust {
+namespace Resolver {
+
+class ResolveRelativeTypePath
+{
+public:
+  static bool go (AST::TypePath &path, NodeId &resolved_node_id);
+};
+
+class ResolveRelativeQualTypePath : public ResolverBase
+{
+  using ResolverBase::visit;
+
+public:
+  static bool go (AST::QualifiedPathInType &path);
+
+  void visit (AST::TypePathSegmentGeneric &seg) override;
+
+  void visit (AST::TypePathSegment &seg) override;
+
+protected:
+  bool resolve_qual_seg (AST::QualifiedPathType &seg);
+
+private:
+  ResolveRelativeQualTypePath ();
+
+  bool failure_flag;
+};
+
+class ResolveType : public ResolverBase
+{
+  using Rust::Resolver::ResolverBase::visit;
+
+public:
+  static NodeId go (AST::Type *type)
+  {
+    ResolveType resolver;
+    type->accept_vis (resolver);
+    return resolver.resolved_node;
+  }
+
+  void visit (AST::BareFunctionType &fntype) override
+  {
+    for (auto &param : fntype.get_function_params ())
+      ResolveType::go (param.get_type ().get ());
+
+    if (fntype.has_return_type ())
+      ResolveType::go (fntype.get_return_type ().get ());
+  }
+
+  void visit (AST::TupleType &tuple) override
+  {
+    if (tuple.is_unit_type ())
+      {
+	resolved_node = resolver->get_unit_type_node_id ();
+	return;
+      }
+
+    for (auto &elem : tuple.get_elems ())
+      ResolveType::go (elem.get ());
+  }
+
+  void visit (AST::TypePath &path) override
+  {
+    ResolveRelativeTypePath::go (path, resolved_node);
+  }
+
+  void visit (AST::QualifiedPathInType &path) override
+  {
+    ResolveRelativeQualTypePath::go (path);
+  }
+
+  void visit (AST::ArrayType &type) override;
+
+  void visit (AST::ReferenceType &type) override;
+
+  void visit (AST::InferredType &type) override;
+
+  void visit (AST::NeverType &type) override;
+
+  void visit (AST::RawPointerType &type) override;
+
+  void visit (AST::TraitObjectTypeOneBound &type) override;
+
+  void visit (AST::TraitObjectType &type) override;
+
+  void visit (AST::SliceType &type) override;
+
+private:
+  ResolveType () : ResolverBase () {}
+};
+
+class ResolveTypeBound : public ResolverBase
+{
+  using Rust::Resolver::ResolverBase::visit;
+
+public:
+  static NodeId go (AST::TypeParamBound *type)
+  {
+    ResolveTypeBound resolver;
+    type->accept_vis (resolver);
+    return resolver.resolved_node;
+  };
+
+  void visit (AST::TraitBound &bound) override
+  {
+    resolved_node = ResolveType::go (&bound.get_type_path ());
+  }
+
+private:
+  ResolveTypeBound () : ResolverBase () {}
+};
+
+class ResolveGenericParam : public ResolverBase
+{
+  using Rust::Resolver::ResolverBase::visit;
+
+public:
+  static NodeId go (AST::GenericParam *param, const CanonicalPath &prefix,
+		    const CanonicalPath &canonical_prefix)
+  {
+    ResolveGenericParam resolver (prefix, canonical_prefix);
+    param->accept_vis (resolver);
+    return resolver.resolved_node;
+  }
+
+  void visit (AST::ConstGenericParam &param) override
+  {
+    ResolveType::go (param.get_type ().get ());
+
+    if (param.has_default_value ())
+      ResolveExpr::go (param.get_default_value ().get_expression ().get (),
+		       prefix, canonical_prefix);
+
+    ok = true;
+  }
+
+  void visit (AST::TypeParam &param) override
+  {
+    // if it has a type lets resolve it
+    if (param.has_type ())
+      ResolveType::go (param.get_type ().get ());
+
+    if (param.has_type_param_bounds ())
+      {
+	for (auto &bound : param.get_type_param_bounds ())
+	  {
+	    ResolveTypeBound::go (bound.get ());
+	  }
+      }
+
+    auto seg = CanonicalPath::new_seg (param.get_node_id (),
+				       param.get_type_representation ());
+    resolver->get_type_scope ().insert (
+      seg, param.get_node_id (), param.get_locus (), false,
+      [&] (const CanonicalPath &, NodeId, Location locus) -> void {
+	rust_error_at (param.get_locus (),
+		       "generic param redefined multiple times");
+	rust_error_at (locus, "was defined here");
+      });
+
+    mappings->insert_canonical_path (param.get_node_id (), seg);
+  }
+
+private:
+  ResolveGenericParam (const CanonicalPath &prefix,
+		       const CanonicalPath &canonical_prefix)
+    : ResolverBase (), ok (false), prefix (prefix),
+      canonical_prefix (canonical_prefix)
+  {}
+
+  bool ok;
+  const CanonicalPath &prefix;
+  const CanonicalPath &canonical_prefix;
+};
+
+class ResolveWhereClause : public ResolverBase
+{
+  using Rust::Resolver::ResolverBase::visit;
+
+public:
+  static void Resolve (AST::WhereClause &where_clause)
+  {
+    ResolveWhereClause r;
+    for (auto &clause : where_clause.get_items ())
+      clause->accept_vis (r);
+  }
+
+  void visit (AST::TypeBoundWhereClauseItem &item) override
+  {
+    ResolveType::go (item.get_type ().get ());
+    if (item.has_type_param_bounds ())
+      {
+	for (auto &bound : item.get_type_param_bounds ())
+	  {
+	    ResolveTypeBound::go (bound.get ());
+	  }
+      }
+  }
+
+private:
+  ResolveWhereClause () : ResolverBase () {}
+};
+
+class ResolveTypeToCanonicalPath : public ResolverBase
+{
+  using Rust::Resolver::ResolverBase::visit;
+
+public:
+  static bool go (AST::Type *type, CanonicalPath &result);
+
+  void visit (AST::TypePath &path) override;
+
+  void visit (AST::ReferenceType &type) override;
+
+  void visit (AST::RawPointerType &type) override;
+
+  void visit (AST::SliceType &type) override;
+
+  void visit (AST::TraitObjectTypeOneBound &type) override;
+
+  void visit (AST::TraitObjectType &type) override;
+
+private:
+  ResolveTypeToCanonicalPath ();
+
+  CanonicalPath result;
+};
+
+class ResolveGenericArgs : public ResolverBase
+{
+  using Rust::Resolver::ResolverBase::visit;
+
+public:
+  static void go (AST::GenericArgs &generic_args);
+  static void go (AST::GenericArgs &generic_args, const CanonicalPath &prefix,
+		  const CanonicalPath &canonical_prefix);
+
+private:
+  ResolveGenericArgs (const CanonicalPath &prefix,
+		      const CanonicalPath &canonical_prefix)
+    : ResolverBase (), prefix (prefix), canonical_prefix (canonical_prefix)
+  {}
+
+  bool is_type_name (const CanonicalPath &path);
+  bool is_const_value_name (const CanonicalPath &path);
+
+  /**
+   * Resolve a disambiguated generic arg
+   */
+  void disambiguate (AST::GenericArg &arg);
+
+  /**
+   * Resolve a disambiguated generic arg
+   */
+  void resolve_disambiguated_generic (AST::GenericArg &arg);
+
+  const CanonicalPath &prefix;
+  const CanonicalPath &canonical_prefix;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_AST_RESOLVE_TYPE_H
diff --git a/gcc/rust/resolve/rust-ast-resolve.cc b/gcc/rust/resolve/rust-ast-resolve.cc
new file mode 100644
index 00000000000..93fa7c8761c
--- /dev/null
+++ b/gcc/rust/resolve/rust-ast-resolve.cc
@@ -0,0 +1,115 @@
+// 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/>.
+
+#include "rust-ast-resolve.h"
+#include "rust-ast-full.h"
+#include "rust-tyty.h"
+#include "rust-ast-resolve-toplevel.h"
+#include "rust-ast-resolve-item.h"
+#include "rust-ast-resolve-expr.h"
+#include "rust-ast-resolve-struct-expr-field.h"
+
+extern bool
+saw_errors (void);
+
+namespace Rust {
+namespace Resolver {
+
+// NameResolution
+
+NameResolution *
+NameResolution::get ()
+{
+  static NameResolution *instance;
+  if (instance == nullptr)
+    instance = new NameResolution ();
+
+  return instance;
+}
+
+NameResolution::NameResolution ()
+  : resolver (Resolver::get ()), mappings (Analysis::Mappings::get ())
+{
+  // these are global
+  resolver->get_type_scope ().push (mappings->get_next_node_id ());
+  resolver->insert_builtin_types (resolver->get_type_scope ().peek ());
+  resolver->push_new_type_rib (resolver->get_type_scope ().peek ());
+}
+
+void
+NameResolution::Resolve (AST::Crate &crate)
+{
+  auto resolver = get ();
+  resolver->go (crate);
+}
+
+void
+NameResolution::go (AST::Crate &crate)
+{
+  // lookup current crate name
+  CrateNum cnum = mappings->get_current_crate ();
+  std::string crate_name;
+  bool ok = mappings->get_crate_name (cnum, crate_name);
+  rust_assert (ok);
+
+  // setup the ribs
+  NodeId scope_node_id = crate.get_node_id ();
+  resolver->get_name_scope ().push (scope_node_id);
+  resolver->get_type_scope ().push (scope_node_id);
+  resolver->get_label_scope ().push (scope_node_id);
+  resolver->push_new_name_rib (resolver->get_name_scope ().peek ());
+  resolver->push_new_type_rib (resolver->get_type_scope ().peek ());
+  resolver->push_new_label_rib (resolver->get_type_scope ().peek ());
+
+  // get the root segment
+  CanonicalPath crate_prefix
+    = CanonicalPath::new_seg (scope_node_id, crate_name);
+  crate_prefix.set_crate_num (cnum);
+
+  // setup a dummy crate node
+  resolver->get_name_scope ().insert (
+    CanonicalPath::new_seg (crate.get_node_id (), "__$$crate__"),
+    crate.get_node_id (), Location ());
+
+  // setup the root scope
+  resolver->push_new_module_scope (scope_node_id);
+
+  // first gather the top-level namespace names then we drill down so this
+  // allows for resolving forward declarations since an impl block might have
+  // a Self type Foo which is defined after the impl block for example.
+  for (auto it = crate.items.begin (); it != crate.items.end (); it++)
+    ResolveTopLevel::go (it->get (), CanonicalPath::create_empty (),
+			 crate_prefix);
+
+  // FIXME remove this
+  if (saw_errors ())
+    {
+      resolver->pop_module_scope ();
+      return;
+    }
+
+  // next we can drill down into the items and their scopes
+  for (auto it = crate.items.begin (); it != crate.items.end (); it++)
+    ResolveItem::go (it->get (), CanonicalPath::create_empty (), crate_prefix);
+
+  // done
+  resolver->pop_module_scope ();
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/resolve/rust-ast-resolve.h b/gcc/rust/resolve/rust-ast-resolve.h
new file mode 100644
index 00000000000..a2e10d5c742
--- /dev/null
+++ b/gcc/rust/resolve/rust-ast-resolve.h
@@ -0,0 +1,50 @@
+// 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_AST_RESOLVE_H
+#define RUST_AST_RESOLVE_H
+
+#include "rust-name-resolver.h"
+#include "rust-ast-full.h"
+#include "rust-hir-map.h"
+
+namespace Rust {
+namespace Resolver {
+
+class NameResolution
+{
+public:
+  static void Resolve (AST::Crate &crate);
+
+  static NameResolution *get ();
+
+  ~NameResolution () {}
+
+private:
+  void go (AST::Crate &crate);
+
+  NameResolution ();
+
+  Resolver *resolver;
+  Analysis::Mappings *mappings;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_AST_RESOLVE_H
diff --git a/gcc/rust/resolve/rust-ast-verify-assignee.h b/gcc/rust/resolve/rust-ast-verify-assignee.h
new file mode 100644
index 00000000000..74551cb014d
--- /dev/null
+++ b/gcc/rust/resolve/rust-ast-verify-assignee.h
@@ -0,0 +1,84 @@
+// 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_AST_VERIFY_ASSIGNEE
+#define RUST_AST_VERIFY_ASSIGNEE
+
+#include "rust-ast-resolve-base.h"
+#include "rust-ast-full.h"
+
+namespace Rust {
+namespace Resolver {
+
+class VerifyAsignee : public ResolverBase
+{
+  using Rust::Resolver::ResolverBase::visit;
+
+public:
+  static bool go (AST::Expr *assignee, NodeId parent)
+  {
+    VerifyAsignee checker (parent);
+    assignee->accept_vis (checker);
+    if (!checker.ok)
+      rust_error_at (assignee->get_locus (),
+		     "invalid left-hand side of assignment");
+    return checker.ok;
+  }
+
+  void visit (AST::ArrayIndexExpr &expr) override
+  {
+    expr.get_array_expr ()->accept_vis (*this);
+  }
+
+  void visit (AST::FieldAccessExpr &expr) override
+  {
+    expr.get_receiver_expr ()->accept_vis (*this);
+  }
+
+  void visit (AST::TupleIndexExpr &expr) override
+  {
+    expr.get_tuple_expr ()->accept_vis (*this);
+  }
+
+  void visit (AST::IdentifierExpr &expr) override
+  {
+    if (!resolver->get_name_scope ().lookup (
+	  CanonicalPath::new_seg (expr.get_node_id (), expr.as_string ()),
+	  &resolved_node))
+      return;
+
+    ok = true;
+  }
+
+  void visit (AST::DereferenceExpr &expr) override
+  {
+    expr.get_dereferenced_expr ()->accept_vis (*this);
+  }
+
+  void visit (AST::PathInExpression &expr) override { ok = true; }
+
+private:
+  VerifyAsignee (NodeId parent) : ResolverBase (), ok (false) {}
+
+  bool ok;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_AST_VERIFY_ASSIGNEE
diff --git a/gcc/rust/resolve/rust-name-resolver.cc b/gcc/rust/resolve/rust-name-resolver.cc
new file mode 100644
index 00000000000..fb7087425c1
--- /dev/null
+++ b/gcc/rust/resolve/rust-name-resolver.cc
@@ -0,0 +1,503 @@
+// 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/>.
+
+#include "rust-name-resolver.h"
+#include "rust-ast-full.h"
+
+#define MKBUILTIN_TYPE(_X, _R, _TY)                                            \
+  do                                                                           \
+    {                                                                          \
+      AST::PathIdentSegment seg (_X, Linemap::predeclared_location ());        \
+      auto typePath = ::std::unique_ptr<AST::TypePathSegment> (                \
+	new AST::TypePathSegment (::std::move (seg), false,                    \
+				  Linemap::predeclared_location ()));          \
+      ::std::vector< ::std::unique_ptr<AST::TypePathSegment> > segs;           \
+      segs.push_back (::std::move (typePath));                                 \
+      auto builtin_type                                                        \
+	= new AST::TypePath (::std::move (segs),                               \
+			     Linemap::predeclared_location (), false);         \
+      _R.push_back (builtin_type);                                             \
+      tyctx->insert_builtin (_TY->get_ref (), builtin_type->get_node_id (),    \
+			     _TY);                                             \
+      mappings->insert_node_to_hir (builtin_type->get_node_id (),              \
+				    _TY->get_ref ());                          \
+      mappings->insert_canonical_path (                                        \
+	builtin_type->get_node_id (),                                          \
+	CanonicalPath::new_seg (builtin_type->get_node_id (), _X));            \
+    }                                                                          \
+  while (0)
+
+namespace Rust {
+namespace Resolver {
+
+Rib::Rib (CrateNum crateNum, NodeId node_id)
+  : crate_num (crateNum), node_id (node_id),
+    mappings (Analysis::Mappings::get ())
+{}
+
+void
+Rib::insert_name (
+  const CanonicalPath &path, NodeId id, Location locus, bool shadow,
+  std::function<void (const CanonicalPath &, NodeId, Location)> dup_cb)
+{
+  auto it = path_mappings.find (path);
+  bool path_already_exists = it != path_mappings.end ();
+  if (path_already_exists && !shadow)
+    {
+      const auto &decl = decls_within_rib.find (it->second);
+      if (decl != decls_within_rib.end ())
+	dup_cb (path, it->second, decl->second);
+      else
+	dup_cb (path, it->second, locus);
+
+      return;
+    }
+
+  path_mappings[path] = id;
+  reverse_path_mappings.insert (std::pair<NodeId, CanonicalPath> (id, path));
+  decls_within_rib.insert (std::pair<NodeId, Location> (id, locus));
+  references[id] = {};
+}
+
+bool
+Rib::lookup_name (const CanonicalPath &ident, NodeId *id)
+{
+  auto it = path_mappings.find (ident);
+  if (it == path_mappings.end ())
+    return false;
+
+  *id = it->second;
+  return true;
+}
+
+void
+Rib::clear_name (const CanonicalPath &ident, NodeId id)
+{
+  auto ii = path_mappings.find (ident);
+  if (ii != path_mappings.end ())
+    path_mappings.erase (ii);
+
+  auto ij = reverse_path_mappings.find (id);
+  if (ij != reverse_path_mappings.end ())
+    reverse_path_mappings.erase (ij);
+
+  auto ik = decls_within_rib.find (id);
+  if (ik != decls_within_rib.end ())
+    decls_within_rib.erase (ik);
+}
+
+void
+Rib::append_reference_for_def (NodeId def, NodeId ref)
+{
+  references[def].insert (ref);
+}
+
+bool
+Rib::have_references_for_node (NodeId def) const
+{
+  auto it = references.find (def);
+  if (it == references.end ())
+    return false;
+
+  return !it->second.empty ();
+}
+
+bool
+Rib::decl_was_declared_here (NodeId def) const
+{
+  for (auto &it : decls_within_rib)
+    {
+      if (it.first == def)
+	return true;
+    }
+  return false;
+}
+
+void
+Rib::debug () const
+{
+  fprintf (stderr, "%s\n", debug_str ().c_str ());
+}
+
+std::string
+Rib::debug_str () const
+{
+  std::string buffer;
+  for (const auto &it : path_mappings)
+    {
+      buffer += it.first.get () + "=" + std::to_string (it.second);
+      buffer += ",";
+    }
+  return "{" + buffer + "}";
+}
+
+Scope::Scope (CrateNum crate_num) : crate_num (crate_num) {}
+
+void
+Scope::insert (
+  const CanonicalPath &ident, NodeId id, Location locus, bool shadow,
+  std::function<void (const CanonicalPath &, NodeId, Location)> dup_cb)
+{
+  peek ()->insert_name (ident, id, locus, shadow, dup_cb);
+}
+
+void
+Scope::insert (const CanonicalPath &ident, NodeId id, Location locus)
+{
+  peek ()->insert_name (ident, id, locus, true,
+			[] (const CanonicalPath &, NodeId, Location) -> void {
+			});
+}
+
+bool
+Scope::lookup (const CanonicalPath &ident, NodeId *id)
+{
+  NodeId lookup = UNKNOWN_NODEID;
+  iterate ([&] (Rib *r) mutable -> bool {
+    if (r->lookup_name (ident, &lookup))
+      return false;
+    return true;
+  });
+
+  *id = lookup;
+  return lookup != UNKNOWN_NODEID;
+}
+
+void
+Scope::iterate (std::function<bool (Rib *)> cb)
+{
+  for (auto it = stack.rbegin (); it != stack.rend (); ++it)
+    {
+      if (!cb (*it))
+	return;
+    }
+}
+
+void
+Scope::iterate (std::function<bool (const Rib *)> cb) const
+{
+  for (auto it = stack.rbegin (); it != stack.rend (); ++it)
+    {
+      if (!cb (*it))
+	return;
+    }
+}
+
+Rib *
+Scope::peek ()
+{
+  return stack.back ();
+}
+
+void
+Scope::push (NodeId id)
+{
+  stack.push_back (new Rib (get_crate_num (), id));
+}
+
+Rib *
+Scope::pop ()
+{
+  Rib *r = peek ();
+  stack.pop_back ();
+  return r;
+}
+
+void
+Scope::append_reference_for_def (NodeId refId, NodeId defId)
+{
+  bool ok = false;
+  iterate ([&] (Rib *r) mutable -> bool {
+    if (r->decl_was_declared_here (defId))
+      {
+	ok = true;
+	r->append_reference_for_def (defId, refId);
+      }
+    return true;
+  });
+  rust_assert (ok);
+}
+
+bool
+Scope::decl_was_declared_here (NodeId def) const
+{
+  bool found = false;
+  iterate ([&] (const Rib *r) -> bool {
+    if (r->decl_was_declared_here (def))
+      {
+	found = true;
+	return false;
+      }
+    return true;
+  });
+  return found;
+}
+
+Resolver::Resolver ()
+  : mappings (Analysis::Mappings::get ()), tyctx (TypeCheckContext::get ()),
+    name_scope (Scope (mappings->get_current_crate ())),
+    type_scope (Scope (mappings->get_current_crate ())),
+    label_scope (Scope (mappings->get_current_crate ())),
+    macro_scope (Scope (mappings->get_current_crate ())),
+    global_type_node_id (UNKNOWN_NODEID), unit_ty_node_id (UNKNOWN_NODEID)
+{
+  generate_builtins ();
+}
+
+Resolver *
+Resolver::get ()
+{
+  static Resolver *instance;
+  if (instance == nullptr)
+    instance = new Resolver ();
+
+  return instance;
+}
+
+void
+Resolver::push_new_name_rib (Rib *r)
+{
+  rust_assert (name_ribs.find (r->get_node_id ()) == name_ribs.end ());
+  name_ribs[r->get_node_id ()] = r;
+}
+
+void
+Resolver::push_new_type_rib (Rib *r)
+{
+  if (type_ribs.size () == 0)
+    global_type_node_id = r->get_node_id ();
+
+  rust_assert (type_ribs.find (r->get_node_id ()) == type_ribs.end ());
+  type_ribs[r->get_node_id ()] = r;
+}
+
+void
+Resolver::push_new_label_rib (Rib *r)
+{
+  rust_assert (label_ribs.find (r->get_node_id ()) == label_ribs.end ());
+  label_ribs[r->get_node_id ()] = r;
+}
+
+void
+Resolver::push_new_macro_rib (Rib *r)
+{
+  rust_assert (label_ribs.find (r->get_node_id ()) == label_ribs.end ());
+  macro_ribs[r->get_node_id ()] = r;
+}
+
+bool
+Resolver::find_name_rib (NodeId id, Rib **rib)
+{
+  auto it = name_ribs.find (id);
+  if (it == name_ribs.end ())
+    return false;
+
+  *rib = it->second;
+  return true;
+}
+
+bool
+Resolver::find_type_rib (NodeId id, Rib **rib)
+{
+  auto it = type_ribs.find (id);
+  if (it == type_ribs.end ())
+    return false;
+
+  *rib = it->second;
+  return true;
+}
+
+bool
+Resolver::find_macro_rib (NodeId id, Rib **rib)
+{
+  auto it = macro_ribs.find (id);
+  if (it == macro_ribs.end ())
+    return false;
+
+  *rib = it->second;
+  return true;
+}
+
+void
+Resolver::insert_builtin_types (Rib *r)
+{
+  auto builtins = get_builtin_types ();
+  for (auto &builtin : builtins)
+    {
+      CanonicalPath builtin_path
+	= CanonicalPath::new_seg (builtin->get_node_id (),
+				  builtin->as_string ());
+      r->insert_name (builtin_path, builtin->get_node_id (),
+		      Linemap::predeclared_location (), false,
+		      [] (const CanonicalPath &, NodeId, Location) -> void {});
+    }
+}
+
+std::vector<AST::Type *> &
+Resolver::get_builtin_types ()
+{
+  return builtins;
+}
+
+void
+Resolver::generate_builtins ()
+{
+  auto u8
+    = new TyTy::UintType (mappings->get_next_hir_id (), TyTy::UintType::U8);
+  auto u16
+    = new TyTy::UintType (mappings->get_next_hir_id (), TyTy::UintType::U16);
+  auto u32
+    = new TyTy::UintType (mappings->get_next_hir_id (), TyTy::UintType::U32);
+  auto u64
+    = new TyTy::UintType (mappings->get_next_hir_id (), TyTy::UintType::U64);
+  auto u128
+    = new TyTy::UintType (mappings->get_next_hir_id (), TyTy::UintType::U128);
+  auto i8 = new TyTy::IntType (mappings->get_next_hir_id (), TyTy::IntType::I8);
+  auto i16
+    = new TyTy::IntType (mappings->get_next_hir_id (), TyTy::IntType::I16);
+  auto i32
+    = new TyTy::IntType (mappings->get_next_hir_id (), TyTy::IntType::I32);
+  auto i64
+    = new TyTy::IntType (mappings->get_next_hir_id (), TyTy::IntType::I64);
+  auto i128
+    = new TyTy::IntType (mappings->get_next_hir_id (), TyTy::IntType::I128);
+  auto rbool = new TyTy::BoolType (mappings->get_next_hir_id ());
+  auto f32
+    = new TyTy::FloatType (mappings->get_next_hir_id (), TyTy::FloatType::F32);
+  auto f64
+    = new TyTy::FloatType (mappings->get_next_hir_id (), TyTy::FloatType::F64);
+  auto usize = new TyTy::USizeType (mappings->get_next_hir_id ());
+  auto isize = new TyTy::ISizeType (mappings->get_next_hir_id ());
+  auto char_tyty = new TyTy::CharType (mappings->get_next_hir_id ());
+  auto str = new TyTy::StrType (mappings->get_next_hir_id ());
+  auto never = new TyTy::NeverType (mappings->get_next_hir_id ());
+
+  MKBUILTIN_TYPE ("u8", builtins, u8);
+  MKBUILTIN_TYPE ("u16", builtins, u16);
+  MKBUILTIN_TYPE ("u32", builtins, u32);
+  MKBUILTIN_TYPE ("u64", builtins, u64);
+  MKBUILTIN_TYPE ("u128", builtins, u128);
+  MKBUILTIN_TYPE ("i8", builtins, i8);
+  MKBUILTIN_TYPE ("i16", builtins, i16);
+  MKBUILTIN_TYPE ("i32", builtins, i32);
+  MKBUILTIN_TYPE ("i64", builtins, i64);
+  MKBUILTIN_TYPE ("i128", builtins, i128);
+  MKBUILTIN_TYPE ("bool", builtins, rbool);
+  MKBUILTIN_TYPE ("f32", builtins, f32);
+  MKBUILTIN_TYPE ("f64", builtins, f64);
+  MKBUILTIN_TYPE ("usize", builtins, usize);
+  MKBUILTIN_TYPE ("isize", builtins, isize);
+  MKBUILTIN_TYPE ("char", builtins, char_tyty);
+  MKBUILTIN_TYPE ("str", builtins, str);
+  MKBUILTIN_TYPE ("!", builtins, never);
+
+  // unit type ()
+  TyTy::TupleType *unit_tyty
+    = TyTy::TupleType::get_unit_type (mappings->get_next_hir_id ());
+  std::vector<std::unique_ptr<AST::Type> > elems;
+  AST::TupleType *unit_type
+    = new AST::TupleType (std::move (elems), Linemap::predeclared_location ());
+  builtins.push_back (unit_type);
+  tyctx->insert_builtin (unit_tyty->get_ref (), unit_type->get_node_id (),
+			 unit_tyty);
+  set_unit_type_node_id (unit_type->get_node_id ());
+}
+
+void
+Resolver::insert_resolved_name (NodeId refId, NodeId defId)
+{
+  resolved_names[refId] = defId;
+  get_name_scope ().append_reference_for_def (refId, defId);
+}
+
+bool
+Resolver::lookup_resolved_name (NodeId refId, NodeId *defId)
+{
+  auto it = resolved_names.find (refId);
+  if (it == resolved_names.end ())
+    return false;
+
+  *defId = it->second;
+  return true;
+}
+
+void
+Resolver::insert_resolved_type (NodeId refId, NodeId defId)
+{
+  // auto it = resolved_types.find (refId);
+  // rust_assert (it == resolved_types.end ());
+
+  resolved_types[refId] = defId;
+  get_type_scope ().append_reference_for_def (refId, defId);
+}
+
+bool
+Resolver::lookup_resolved_type (NodeId refId, NodeId *defId)
+{
+  auto it = resolved_types.find (refId);
+  if (it == resolved_types.end ())
+    return false;
+
+  *defId = it->second;
+  return true;
+}
+
+void
+Resolver::insert_resolved_label (NodeId refId, NodeId defId)
+{
+  auto it = resolved_labels.find (refId);
+  rust_assert (it == resolved_labels.end ());
+
+  resolved_labels[refId] = defId;
+  get_label_scope ().append_reference_for_def (refId, defId);
+}
+
+bool
+Resolver::lookup_resolved_label (NodeId refId, NodeId *defId)
+{
+  auto it = resolved_labels.find (refId);
+  if (it == resolved_labels.end ())
+    return false;
+
+  *defId = it->second;
+  return true;
+}
+
+void
+Resolver::insert_resolved_macro (NodeId refId, NodeId defId)
+{
+  auto it = resolved_macros.find (refId);
+  rust_assert (it == resolved_macros.end ());
+
+  resolved_labels[refId] = defId;
+  get_label_scope ().append_reference_for_def (refId, defId);
+}
+
+bool
+Resolver::lookup_resolved_macro (NodeId refId, NodeId *defId)
+{
+  auto it = resolved_macros.find (refId);
+  if (it == resolved_macros.end ())
+    return false;
+
+  *defId = it->second;
+  return true;
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/resolve/rust-name-resolver.h b/gcc/rust/resolve/rust-name-resolver.h
new file mode 100644
index 00000000000..014628a87c9
--- /dev/null
+++ b/gcc/rust/resolve/rust-name-resolver.h
@@ -0,0 +1,212 @@
+// 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_NAME_RESOLVER_H
+#define RUST_NAME_RESOLVER_H
+
+#include "rust-system.h"
+#include "rust-canonical-path.h"
+#include "rust-hir-map.h"
+#include "rust-hir-type-check.h"
+
+namespace Rust {
+namespace Resolver {
+
+class Rib
+{
+public:
+  // Rust uses local_def_ids assigned by def_collector on the AST
+  // lets use NodeId instead
+  Rib (CrateNum crateNum, NodeId node_id);
+
+  // this takes the relative paths of items within a compilation unit for lookup
+  void insert_name (
+    const CanonicalPath &path, NodeId id, Location locus, bool shadow,
+    std::function<void (const CanonicalPath &, NodeId, Location)> dup_cb);
+
+  bool lookup_canonical_path (const NodeId &id, CanonicalPath *ident);
+  bool lookup_name (const CanonicalPath &ident, NodeId *id);
+  void clear_name (const CanonicalPath &ident, NodeId id);
+  void append_reference_for_def (NodeId def, NodeId ref);
+  bool have_references_for_node (NodeId def) const;
+  bool decl_was_declared_here (NodeId def) const;
+  void debug () const;
+  std::string debug_str () const;
+
+  CrateNum get_crate_num () const { return crate_num; }
+  NodeId get_node_id () const { return node_id; }
+  std::map<NodeId, Location> &get_declarations () { return decls_within_rib; }
+
+private:
+  CrateNum crate_num;
+  NodeId node_id;
+  std::map<CanonicalPath, NodeId> path_mappings;
+  std::map<NodeId, CanonicalPath> reverse_path_mappings;
+  std::map<NodeId, Location> decls_within_rib;
+  std::map<NodeId, std::set<NodeId>> references;
+  Analysis::Mappings *mappings;
+};
+
+class Scope
+{
+public:
+  Scope (CrateNum crate_num);
+
+  void
+  insert (const CanonicalPath &ident, NodeId id, Location locus, bool shadow,
+	  std::function<void (const CanonicalPath &, NodeId, Location)> dup_cb);
+
+  void insert (const CanonicalPath &ident, NodeId id, Location locus);
+  bool lookup (const CanonicalPath &ident, NodeId *id);
+
+  void iterate (std::function<bool (Rib *)> cb);
+  void iterate (std::function<bool (const Rib *)> cb) const;
+
+  Rib *peek ();
+  void push (NodeId id);
+  Rib *pop ();
+
+  bool decl_was_declared_here (NodeId def) const;
+  void append_reference_for_def (NodeId refId, NodeId defId);
+
+  CrateNum get_crate_num () const { return crate_num; }
+
+private:
+  CrateNum crate_num;
+  std::vector<Rib *> stack;
+};
+
+class Resolver
+{
+public:
+  static Resolver *get ();
+  ~Resolver () {}
+
+  // these builtin types
+  void insert_builtin_types (Rib *r);
+
+  // these will be required for type resolution passes to
+  // map back to tyty nodes
+  std::vector<AST::Type *> &get_builtin_types ();
+
+  void push_new_name_rib (Rib *r);
+  void push_new_type_rib (Rib *r);
+  void push_new_label_rib (Rib *r);
+  void push_new_macro_rib (Rib *r);
+
+  bool find_name_rib (NodeId id, Rib **rib);
+  bool find_type_rib (NodeId id, Rib **rib);
+  bool find_label_rib (NodeId id, Rib **rib);
+  bool find_macro_rib (NodeId id, Rib **rib);
+
+  void insert_resolved_name (NodeId refId, NodeId defId);
+  bool lookup_resolved_name (NodeId refId, NodeId *defId);
+
+  void insert_resolved_type (NodeId refId, NodeId defId);
+  bool lookup_resolved_type (NodeId refId, NodeId *defId);
+
+  void insert_resolved_label (NodeId refId, NodeId defId);
+  bool lookup_resolved_label (NodeId refId, NodeId *defId);
+
+  void insert_resolved_macro (NodeId refId, NodeId defId);
+  bool lookup_resolved_macro (NodeId refId, NodeId *defId);
+
+  // proxy for scoping
+  Scope &get_name_scope () { return name_scope; }
+  Scope &get_type_scope () { return type_scope; }
+  Scope &get_label_scope () { return label_scope; }
+  Scope &get_macro_scope () { return macro_scope; }
+
+  NodeId get_global_type_node_id () { return global_type_node_id; }
+  void set_unit_type_node_id (NodeId id) { unit_ty_node_id = id; }
+  NodeId get_unit_type_node_id () { return unit_ty_node_id; }
+
+  void push_new_module_scope (NodeId module_id)
+  {
+    current_module_stack.push_back (module_id);
+  }
+
+  void pop_module_scope ()
+  {
+    rust_assert (!current_module_stack.empty ());
+    current_module_stack.pop_back ();
+  }
+
+  NodeId peek_current_module_scope () const
+  {
+    rust_assert (!current_module_stack.empty ());
+    return current_module_stack.back ();
+  }
+
+  NodeId peek_crate_module_scope () const
+  {
+    rust_assert (!current_module_stack.empty ());
+    return current_module_stack.front ();
+  }
+
+  NodeId peek_parent_module_scope () const
+  {
+    rust_assert (current_module_stack.size () > 1);
+    return current_module_stack.at (current_module_stack.size () - 2);
+  }
+
+private:
+  Resolver ();
+
+  void generate_builtins ();
+
+  Analysis::Mappings *mappings;
+  TypeCheckContext *tyctx;
+
+  std::vector<AST::Type *> builtins;
+
+  Scope name_scope;
+  Scope type_scope;
+  Scope label_scope;
+  Scope macro_scope;
+
+  NodeId global_type_node_id;
+  NodeId unit_ty_node_id;
+
+  // map a AST Node to a Rib
+  std::map<NodeId, Rib *> name_ribs;
+  std::map<NodeId, Rib *> type_ribs;
+  std::map<NodeId, Rib *> label_ribs;
+  std::map<NodeId, Rib *> macro_ribs;
+
+  // Rust uses DefIds to namespace these under a crate_num
+  // but then it uses the def_collector to assign local_defids
+  // to each ast node as well. not sure if this is going to fit
+  // with gcc very well to compile a full crate in one go but we will
+  // see.
+
+  // these are of the form ref->Def-NodeId
+  // we need two namespaces one for names and ones for types
+  std::map<NodeId, NodeId> resolved_names;
+  std::map<NodeId, NodeId> resolved_types;
+  std::map<NodeId, NodeId> resolved_labels;
+  std::map<NodeId, NodeId> resolved_macros;
+
+  // keep track of the current module scope ids
+  std::vector<NodeId> current_module_stack;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_NAME_RESOLVER_H
-- 
2.25.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [PATCH Rust front-end v2 13/37] gccrs: Add second intermedite representation called HIR
  2022-08-24 11:59 Rust frontend patches v2 herron.philip
                   ` (11 preceding siblings ...)
  2022-08-24 11:59 ` [PATCH Rust front-end v2 12/37] gccrs: Add name resolution pass to " herron.philip
@ 2022-08-24 11:59 ` herron.philip
  2022-08-24 11:59 ` [PATCH Rust front-end v2 14/37] gccrs: Add AST to HIR lowering pass herron.philip
                   ` (24 subsequent siblings)
  37 siblings, 0 replies; 59+ messages in thread
From: herron.philip @ 2022-08-24 11:59 UTC (permalink / raw)
  To: gcc-patches; +Cc: gcc-rust, Philip Herron

From: Philip Herron <philip.herron@embecosm.com>

This gives the front-end a chance to desugar alot of the AST such as:
- Remove distinction between functions and methods
- Remove Macros
- Remove IdentifierExpr's
- Remove duplicate attribute structures
---
 gcc/rust/hir/tree/rust-hir-expr.h       | 4194 ++++++++++++++++++
 gcc/rust/hir/tree/rust-hir-full-decls.h |  232 +
 gcc/rust/hir/tree/rust-hir-full-test.cc | 5292 +++++++++++++++++++++++
 gcc/rust/hir/tree/rust-hir-full.h       |   30 +
 gcc/rust/hir/tree/rust-hir-item.h       | 3207 ++++++++++++++
 gcc/rust/hir/tree/rust-hir-path.h       | 1013 +++++
 gcc/rust/hir/tree/rust-hir-pattern.h    | 1356 ++++++
 gcc/rust/hir/tree/rust-hir-stmt.h       |  273 ++
 gcc/rust/hir/tree/rust-hir-type.h       |  860 ++++
 gcc/rust/hir/tree/rust-hir-visitor.h    |  493 +++
 gcc/rust/hir/tree/rust-hir.h            |  921 ++++
 11 files changed, 17871 insertions(+)
 create mode 100644 gcc/rust/hir/tree/rust-hir-expr.h
 create mode 100644 gcc/rust/hir/tree/rust-hir-full-decls.h
 create mode 100644 gcc/rust/hir/tree/rust-hir-full-test.cc
 create mode 100644 gcc/rust/hir/tree/rust-hir-full.h
 create mode 100644 gcc/rust/hir/tree/rust-hir-item.h
 create mode 100644 gcc/rust/hir/tree/rust-hir-path.h
 create mode 100644 gcc/rust/hir/tree/rust-hir-pattern.h
 create mode 100644 gcc/rust/hir/tree/rust-hir-stmt.h
 create mode 100644 gcc/rust/hir/tree/rust-hir-type.h
 create mode 100644 gcc/rust/hir/tree/rust-hir-visitor.h
 create mode 100644 gcc/rust/hir/tree/rust-hir.h

diff --git a/gcc/rust/hir/tree/rust-hir-expr.h b/gcc/rust/hir/tree/rust-hir-expr.h
new file mode 100644
index 00000000000..83278529646
--- /dev/null
+++ b/gcc/rust/hir/tree/rust-hir-expr.h
@@ -0,0 +1,4194 @@
+// 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_HIR_EXPR_H
+#define RUST_HIR_EXPR_H
+
+#include "rust-common.h"
+#include "rust-ast-full-decls.h"
+#include "rust-hir.h"
+#include "rust-hir-path.h"
+#include "operator.h"
+
+namespace Rust {
+namespace HIR {
+
+// HIR node for an expression with an accompanying block - abstract
+class ExprWithBlock : public Expr
+{
+  // TODO: should this mean that a BlockExpr should be a member variable?
+protected:
+  ExprWithBlock (Analysis::NodeMapping mappings,
+		 AST::AttrVec outer_attrs = AST::AttrVec ())
+    : Expr (std::move (mappings), std::move (outer_attrs))
+  {}
+
+  // pure virtual clone implementation
+  virtual ExprWithBlock *clone_expr_with_block_impl () const = 0;
+
+  // prevent having to define multiple clone expressions
+  ExprWithBlock *clone_expr_impl () const override
+  {
+    return clone_expr_with_block_impl ();
+  }
+
+public:
+  // Unique pointer custom clone function
+  std::unique_ptr<ExprWithBlock> clone_expr_with_block () const
+  {
+    return std::unique_ptr<ExprWithBlock> (clone_expr_with_block_impl ());
+  }
+
+  BlockType get_block_expr_type () const final override
+  {
+    return BlockType::WITH_BLOCK;
+  };
+};
+
+// Literals? Or literal base?
+class LiteralExpr : public ExprWithoutBlock
+{
+  Literal literal;
+  Location locus;
+
+public:
+  std::string as_string () const override
+  {
+    return "( " + literal.as_string () + " (" + get_mappings ().as_string ()
+	   + "))";
+  }
+
+  Literal::LitType get_lit_type () const { return literal.get_lit_type (); }
+
+  LiteralExpr (Analysis::NodeMapping mappings, std::string value_as_string,
+	       Literal::LitType type, PrimitiveCoreType type_hint,
+	       Location locus, AST::AttrVec outer_attrs)
+    : ExprWithoutBlock (std::move (mappings), std::move (outer_attrs)),
+      literal (std::move (value_as_string), type, type_hint), locus (locus)
+  {}
+
+  LiteralExpr (Analysis::NodeMapping mappings, Literal literal, Location locus,
+	       AST::AttrVec outer_attrs)
+    : ExprWithoutBlock (std::move (mappings), std::move (outer_attrs)),
+      literal (std::move (literal)), locus (locus)
+  {}
+
+  // Unique pointer custom clone function
+  std::unique_ptr<LiteralExpr> clone_literal_expr () const
+  {
+    return std::unique_ptr<LiteralExpr> (clone_literal_expr_impl ());
+  }
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  Literal &get_literal () { return literal; }
+  const Literal &get_literal () const { return literal; }
+
+  ExprType get_expression_type () const override final { return ExprType::Lit; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  LiteralExpr *clone_expr_impl () const override
+  {
+    return new LiteralExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  LiteralExpr *clone_expr_without_block_impl () const override
+  {
+    return new LiteralExpr (*this);
+  }
+
+  /* not virtual as currently no subclasses of LiteralExpr, but could be in
+   * future */
+  /*virtual*/ LiteralExpr *clone_literal_expr_impl () const
+  {
+    return new LiteralExpr (*this);
+  }
+};
+
+/* Represents an expression using unary or binary operators as HIR node. Can be
+ * overloaded. */
+class OperatorExpr : public ExprWithoutBlock
+{
+  // TODO: create binary and unary operator subclasses?
+public:
+  Location locus;
+
+protected:
+  /* Variable must be protected to allow derived classes to use it as a first
+   * class citizen */
+  std::unique_ptr<Expr> main_or_left_expr;
+
+  // Constructor (only for initialisation of expr purposes)
+  OperatorExpr (Analysis::NodeMapping mappings,
+		std::unique_ptr<Expr> main_or_left_expr,
+		AST::AttrVec outer_attribs, Location locus)
+    : ExprWithoutBlock (std::move (mappings), std::move (outer_attribs)),
+      locus (locus), main_or_left_expr (std::move (main_or_left_expr))
+  {}
+
+  // Copy constructor (only for initialisation of expr purposes)
+  OperatorExpr (OperatorExpr const &other)
+    : ExprWithoutBlock (other), locus (other.locus),
+      main_or_left_expr (other.main_or_left_expr->clone_expr ())
+  {}
+
+  // Overload assignment operator to deep copy expr
+  OperatorExpr &operator= (OperatorExpr const &other)
+  {
+    ExprWithoutBlock::operator= (other);
+    main_or_left_expr = other.main_or_left_expr->clone_expr ();
+    locus = other.locus;
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  OperatorExpr (OperatorExpr &&other) = default;
+  OperatorExpr &operator= (OperatorExpr &&other) = default;
+
+public:
+  Location get_locus () const override final { return locus; }
+
+  std::unique_ptr<Expr> &get_expr () { return main_or_left_expr; }
+
+  ExprType get_expression_type () const override final
+  {
+    return ExprType::Operator;
+  }
+};
+
+/* Unary prefix & or &mut (or && and &&mut) borrow operator. Cannot be
+ * overloaded. */
+class BorrowExpr : public OperatorExpr
+{
+  Mutability mut;
+  bool double_borrow;
+
+public:
+  std::string as_string () const override;
+
+  BorrowExpr (Analysis::NodeMapping mappings,
+	      std::unique_ptr<Expr> borrow_lvalue, Mutability mut,
+	      bool is_double_borrow, AST::AttrVec outer_attribs, Location locus)
+    : OperatorExpr (std::move (mappings), std::move (borrow_lvalue),
+		    std::move (outer_attribs), locus),
+      mut (mut), double_borrow (is_double_borrow)
+  {}
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  Mutability get_mut () const { return mut; }
+  bool is_mut () const { return mut == Mutability::Mut; }
+  bool get_is_double_borrow () const { return double_borrow; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  BorrowExpr *clone_expr_impl () const override
+  {
+    return new BorrowExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  BorrowExpr *clone_expr_without_block_impl () const override
+  {
+    return new BorrowExpr (*this);
+  }
+};
+
+// Unary prefix * deference operator
+class DereferenceExpr : public OperatorExpr
+{
+public:
+  std::string as_string () const override;
+
+  // Constructor calls OperatorExpr's protected constructor
+  DereferenceExpr (Analysis::NodeMapping mappings,
+		   std::unique_ptr<Expr> deref_lvalue,
+		   AST::AttrVec outer_attribs, Location locus)
+    : OperatorExpr (std::move (mappings), std::move (deref_lvalue),
+		    std::move (outer_attribs), locus)
+  {}
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  DereferenceExpr *clone_expr_impl () const override
+  {
+    return new DereferenceExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  DereferenceExpr *clone_expr_without_block_impl () const override
+  {
+    return new DereferenceExpr (*this);
+  }
+};
+
+// Unary postfix ? error propogation operator. Cannot be overloaded.
+class ErrorPropagationExpr : public OperatorExpr
+{
+public:
+  std::string as_string () const override;
+
+  // Constructor calls OperatorExpr's protected constructor
+  ErrorPropagationExpr (Analysis::NodeMapping mappings,
+			std::unique_ptr<Expr> potential_error_value,
+			AST::AttrVec outer_attribs, Location locus)
+    : OperatorExpr (std::move (mappings), std::move (potential_error_value),
+		    std::move (outer_attribs), locus)
+  {}
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ErrorPropagationExpr *clone_expr_impl () const override
+  {
+    return new ErrorPropagationExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ErrorPropagationExpr *clone_expr_without_block_impl () const override
+  {
+    return new ErrorPropagationExpr (*this);
+  }
+};
+
+// Unary prefix - or ! negation or NOT operators.
+class NegationExpr : public OperatorExpr
+{
+public:
+  using ExprType = NegationOperator;
+
+private:
+  /* Note: overload negation via std::ops::Neg and not via std::ops::Not
+   * Negation only works for signed integer and floating-point types, NOT only
+   * works for boolean and integer types (via bitwise NOT) */
+  ExprType expr_type;
+
+public:
+  std::string as_string () const override;
+
+  ExprType get_expr_type () const { return expr_type; }
+
+  // Constructor calls OperatorExpr's protected constructor
+  NegationExpr (Analysis::NodeMapping mappings,
+		std::unique_ptr<Expr> negated_value, ExprType expr_kind,
+		AST::AttrVec outer_attribs, Location locus)
+    : OperatorExpr (std::move (mappings), std::move (negated_value),
+		    std::move (outer_attribs), locus),
+      expr_type (expr_kind)
+  {}
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  NegationExpr *clone_expr_impl () const override
+  {
+    return new NegationExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  NegationExpr *clone_expr_without_block_impl () const override
+  {
+    return new NegationExpr (*this);
+  }
+};
+
+// Infix binary operators. +, -, *, /, %, &, |, ^, <<, >>
+class ArithmeticOrLogicalExpr : public OperatorExpr
+{
+public:
+  using ExprType = ArithmeticOrLogicalOperator;
+
+private:
+  // Note: overloading trait specified in comments
+  ExprType expr_type;
+
+  std::unique_ptr<Expr> right_expr;
+
+public:
+  std::string as_string () const override;
+
+  ExprType get_expr_type () const { return expr_type; }
+
+  // Constructor calls OperatorExpr's protected constructor
+  ArithmeticOrLogicalExpr (Analysis::NodeMapping mappings,
+			   std::unique_ptr<Expr> left_value,
+			   std::unique_ptr<Expr> right_value,
+			   ExprType expr_kind, Location locus)
+    : OperatorExpr (std::move (mappings), std::move (left_value),
+		    AST::AttrVec (), locus),
+      expr_type (expr_kind), right_expr (std::move (right_value))
+  {}
+  // outer attributes not allowed
+
+  // Copy constructor - probably required due to unique pointer
+  ArithmeticOrLogicalExpr (ArithmeticOrLogicalExpr const &other)
+    : OperatorExpr (other), expr_type (other.expr_type),
+      right_expr (other.right_expr->clone_expr ())
+  {}
+
+  // Overload assignment operator
+  ArithmeticOrLogicalExpr &operator= (ArithmeticOrLogicalExpr const &other)
+  {
+    OperatorExpr::operator= (other);
+    // main_or_left_expr = other.main_or_left_expr->clone_expr();
+    right_expr = other.right_expr->clone_expr ();
+    expr_type = other.expr_type;
+
+    return *this;
+  }
+
+  // move constructors
+  ArithmeticOrLogicalExpr (ArithmeticOrLogicalExpr &&other) = default;
+  ArithmeticOrLogicalExpr &operator= (ArithmeticOrLogicalExpr &&other)
+    = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  void visit_lhs (HIRFullVisitor &vis) { main_or_left_expr->accept_vis (vis); }
+  void visit_rhs (HIRFullVisitor &vis) { right_expr->accept_vis (vis); }
+
+  Expr *get_lhs () { return main_or_left_expr.get (); }
+  Expr *get_rhs () { return right_expr.get (); }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ArithmeticOrLogicalExpr *clone_expr_impl () const override
+  {
+    return new ArithmeticOrLogicalExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ArithmeticOrLogicalExpr *clone_expr_without_block_impl () const override
+  {
+    return new ArithmeticOrLogicalExpr (*this);
+  }
+};
+
+// Infix binary comparison operators. ==, !=, <, <=, >, >=
+class ComparisonExpr : public OperatorExpr
+{
+public:
+  using ExprType = ComparisonOperator;
+
+private:
+  // Note: overloading trait specified in comments
+  ExprType expr_type;
+
+  std::unique_ptr<Expr> right_expr;
+
+public:
+  std::string as_string () const override;
+
+  ExprType get_expr_type () const { return expr_type; }
+
+  // Constructor requires pointers for polymorphism
+  ComparisonExpr (Analysis::NodeMapping mappings,
+		  std::unique_ptr<Expr> left_value,
+		  std::unique_ptr<Expr> right_value, ExprType comparison_kind,
+		  Location locus)
+    : OperatorExpr (std::move (mappings), std::move (left_value),
+		    AST::AttrVec (), locus),
+      expr_type (comparison_kind), right_expr (std::move (right_value))
+  {}
+  // outer attributes not allowed
+
+  // Copy constructor also calls OperatorExpr's protected constructor
+  ComparisonExpr (ComparisonExpr const &other)
+    : OperatorExpr (other), expr_type (other.expr_type),
+      right_expr (other.right_expr->clone_expr ())
+  {}
+
+  // Overload assignment operator to deep copy
+  ComparisonExpr &operator= (ComparisonExpr const &other)
+  {
+    OperatorExpr::operator= (other);
+    // main_or_left_expr = other.main_or_left_expr->clone_expr();
+    right_expr = other.right_expr->clone_expr ();
+    expr_type = other.expr_type;
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  ComparisonExpr (ComparisonExpr &&other) = default;
+  ComparisonExpr &operator= (ComparisonExpr &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  Expr *get_lhs () { return main_or_left_expr.get (); }
+  Expr *get_rhs () { return right_expr.get (); }
+
+  ExprType get_kind () { return expr_type; }
+
+  /* TODO: implement via a function call to std::cmp::PartialEq::eq(&op1, &op2)
+   * maybe? */
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ComparisonExpr *clone_expr_impl () const override
+  {
+    return new ComparisonExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ComparisonExpr *clone_expr_without_block_impl () const override
+  {
+    return new ComparisonExpr (*this);
+  }
+};
+
+// Infix binary lazy boolean logical operators && and ||.
+class LazyBooleanExpr : public OperatorExpr
+{
+public:
+  using ExprType = LazyBooleanOperator;
+
+private:
+  ExprType expr_type;
+
+  std::unique_ptr<Expr> right_expr;
+
+public:
+  // Constructor calls OperatorExpr's protected constructor
+  LazyBooleanExpr (Analysis::NodeMapping mappings,
+		   std::unique_ptr<Expr> left_bool_expr,
+		   std::unique_ptr<Expr> right_bool_expr, ExprType expr_kind,
+		   Location locus)
+    : OperatorExpr (std::move (mappings), std::move (left_bool_expr),
+		    AST::AttrVec (), locus),
+      expr_type (expr_kind), right_expr (std::move (right_bool_expr))
+  {}
+  // outer attributes not allowed
+
+  // Copy constructor also calls OperatorExpr's protected constructor
+  LazyBooleanExpr (LazyBooleanExpr const &other)
+    : OperatorExpr (other), expr_type (other.expr_type),
+      right_expr (other.right_expr->clone_expr ())
+  {}
+
+  // Overload assignment operator to deep copy
+  LazyBooleanExpr &operator= (LazyBooleanExpr const &other)
+  {
+    OperatorExpr::operator= (other);
+    // main_or_left_expr = other.main_or_left_expr->clone_expr();
+    right_expr = other.right_expr->clone_expr ();
+    expr_type = other.expr_type;
+
+    return *this;
+  }
+
+  // move constructors
+  LazyBooleanExpr (LazyBooleanExpr &&other) = default;
+  LazyBooleanExpr &operator= (LazyBooleanExpr &&other) = default;
+
+  std::string as_string () const override;
+
+  ExprType get_expr_type () const { return expr_type; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  Expr *get_lhs () { return main_or_left_expr.get (); }
+
+  Expr *get_rhs () { return right_expr.get (); }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  LazyBooleanExpr *clone_expr_impl () const override
+  {
+    return new LazyBooleanExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  LazyBooleanExpr *clone_expr_without_block_impl () const override
+  {
+    return new LazyBooleanExpr (*this);
+  }
+};
+
+// Binary infix "as" chir expression.
+class TypeCastExpr : public OperatorExpr
+{
+  std::unique_ptr<Type> type_to_convert_to;
+
+  // Note: only certain type casts allowed, outlined in reference
+public:
+  std::string as_string () const override;
+
+  // Constructor requires calling protected constructor of OperatorExpr
+  TypeCastExpr (Analysis::NodeMapping mappings,
+		std::unique_ptr<Expr> expr_to_cast,
+		std::unique_ptr<Type> type_to_cast_to, Location locus)
+    : OperatorExpr (std::move (mappings), std::move (expr_to_cast),
+		    AST::AttrVec (), locus),
+      type_to_convert_to (std::move (type_to_cast_to))
+  {}
+  // outer attributes not allowed
+
+  // Copy constructor also requires calling protected constructor
+  TypeCastExpr (TypeCastExpr const &other)
+    : OperatorExpr (other),
+      type_to_convert_to (other.type_to_convert_to->clone_type ())
+  {}
+
+  // Overload assignment operator to deep copy
+  TypeCastExpr &operator= (TypeCastExpr const &other)
+  {
+    OperatorExpr::operator= (other);
+    // main_or_left_expr = other.main_or_left_expr->clone_expr();
+    type_to_convert_to = other.type_to_convert_to->clone_type ();
+
+    return *this;
+  }
+
+  // move constructors as not supported in c++03
+  TypeCastExpr (TypeCastExpr &&other) = default;
+  TypeCastExpr &operator= (TypeCastExpr &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  std::unique_ptr<Expr> &get_casted_expr ()
+  {
+    rust_assert (main_or_left_expr != nullptr);
+    return main_or_left_expr;
+  }
+
+  std::unique_ptr<Type> &get_type_to_convert_to ()
+  {
+    rust_assert (type_to_convert_to != nullptr);
+    return type_to_convert_to;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TypeCastExpr *clone_expr_impl () const override
+  {
+    return new TypeCastExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TypeCastExpr *clone_expr_without_block_impl () const override
+  {
+    return new TypeCastExpr (*this);
+  }
+};
+
+// Binary assignment expression.
+class AssignmentExpr : public OperatorExpr
+{
+  std::unique_ptr<Expr> right_expr;
+
+public:
+  std::string as_string () const override;
+
+  // Call OperatorExpr constructor to initialise left_expr
+  AssignmentExpr (Analysis::NodeMapping mappings,
+		  std::unique_ptr<Expr> value_to_assign_to,
+		  std::unique_ptr<Expr> value_to_assign, Location locus)
+    : OperatorExpr (std::move (mappings), std::move (value_to_assign_to),
+		    AST::AttrVec (), locus),
+      right_expr (std::move (value_to_assign))
+  {}
+  // outer attributes not allowed
+
+  // Call OperatorExpr constructor in copy constructor, as well as clone
+  AssignmentExpr (AssignmentExpr const &other)
+    : OperatorExpr (other), right_expr (other.right_expr->clone_expr ())
+  {}
+
+  // Overload assignment operator to clone unique_ptr right_expr
+  AssignmentExpr &operator= (AssignmentExpr const &other)
+  {
+    OperatorExpr::operator= (other);
+    // main_or_left_expr = other.main_or_left_expr->clone_expr();
+    right_expr = other.right_expr->clone_expr ();
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  AssignmentExpr (AssignmentExpr &&other) = default;
+  AssignmentExpr &operator= (AssignmentExpr &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  void visit_lhs (HIRFullVisitor &vis) { main_or_left_expr->accept_vis (vis); }
+  void visit_rhs (HIRFullVisitor &vis) { right_expr->accept_vis (vis); }
+
+  Expr *get_lhs () { return main_or_left_expr.get (); }
+  Expr *get_rhs () { return right_expr.get (); }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  AssignmentExpr *clone_expr_impl () const override
+  {
+    return new AssignmentExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  AssignmentExpr *clone_expr_without_block_impl () const override
+  {
+    return new AssignmentExpr (*this);
+  }
+};
+
+class CompoundAssignmentExpr : public OperatorExpr
+{
+public:
+  using ExprType = ArithmeticOrLogicalOperator;
+
+private:
+  // Note: overloading trait specified in comments
+  ExprType expr_type;
+  std::unique_ptr<Expr> right_expr;
+
+public:
+  std::string as_string () const override;
+
+  ExprType get_expr_type () const { return expr_type; }
+
+  // Use pointers in constructor to enable polymorphism
+  CompoundAssignmentExpr (Analysis::NodeMapping mappings,
+			  std::unique_ptr<Expr> value_to_assign_to,
+			  std::unique_ptr<Expr> value_to_assign,
+			  ExprType expr_kind, Location locus)
+    : OperatorExpr (std::move (mappings), std::move (value_to_assign_to),
+		    AST::AttrVec (), locus),
+      expr_type (expr_kind), right_expr (std::move (value_to_assign))
+  {}
+  // outer attributes not allowed
+
+  // Have clone in copy constructor
+  CompoundAssignmentExpr (CompoundAssignmentExpr const &other)
+    : OperatorExpr (other), expr_type (other.expr_type),
+      right_expr (other.right_expr->clone_expr ())
+  {}
+
+  // Overload assignment operator to clone
+  CompoundAssignmentExpr &operator= (CompoundAssignmentExpr const &other)
+  {
+    OperatorExpr::operator= (other);
+    // main_or_left_expr = other.main_or_left_expr->clone_expr();
+    right_expr = other.right_expr->clone_expr ();
+    expr_type = other.expr_type;
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  CompoundAssignmentExpr (CompoundAssignmentExpr &&other) = default;
+  CompoundAssignmentExpr &operator= (CompoundAssignmentExpr &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  std::unique_ptr<Expr> &get_left_expr ()
+  {
+    rust_assert (main_or_left_expr != nullptr);
+    return main_or_left_expr;
+  }
+
+  std::unique_ptr<Expr> &get_right_expr ()
+  {
+    rust_assert (right_expr != nullptr);
+    return right_expr;
+  }
+
+  void visit_lhs (HIRFullVisitor &vis) { main_or_left_expr->accept_vis (vis); }
+  void visit_rhs (HIRFullVisitor &vis) { right_expr->accept_vis (vis); }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  CompoundAssignmentExpr *clone_expr_without_block_impl () const override
+  {
+    return new CompoundAssignmentExpr (*this);
+  }
+};
+
+// Expression in parentheses (i.e. like literally just any 3 + (2 * 6))
+class GroupedExpr : public ExprWithoutBlock
+{
+  AST::AttrVec inner_attrs;
+  std::unique_ptr<Expr> expr_in_parens;
+
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  AST::AttrVec get_inner_attrs () const { return inner_attrs; }
+
+  GroupedExpr (Analysis::NodeMapping mappings,
+	       std::unique_ptr<Expr> parenthesised_expr,
+	       AST::AttrVec inner_attribs, AST::AttrVec outer_attribs,
+	       Location locus)
+    : ExprWithoutBlock (std::move (mappings), std::move (outer_attribs)),
+      inner_attrs (std::move (inner_attribs)),
+      expr_in_parens (std::move (parenthesised_expr)), locus (locus)
+  {}
+
+  // Copy constructor includes clone for expr_in_parens
+  GroupedExpr (GroupedExpr const &other)
+    : ExprWithoutBlock (other), inner_attrs (other.inner_attrs),
+      expr_in_parens (other.expr_in_parens->clone_expr ()), locus (other.locus)
+  {}
+
+  // Overloaded assignment operator to clone expr_in_parens
+  GroupedExpr &operator= (GroupedExpr const &other)
+  {
+    ExprWithoutBlock::operator= (other);
+    inner_attrs = other.inner_attrs;
+    expr_in_parens = other.expr_in_parens->clone_expr ();
+    locus = other.locus;
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  GroupedExpr (GroupedExpr &&other) = default;
+  GroupedExpr &operator= (GroupedExpr &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  std::unique_ptr<Expr> &get_expr_in_parens ()
+  {
+    rust_assert (expr_in_parens != nullptr);
+    return expr_in_parens;
+  }
+
+  ExprType get_expression_type () const override final
+  {
+    return ExprType::Grouped;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  GroupedExpr *clone_expr_impl () const override
+  {
+    return new GroupedExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  GroupedExpr *clone_expr_without_block_impl () const override
+  {
+    return new GroupedExpr (*this);
+  }
+};
+
+// Base array initialisation internal element representation thing (abstract)
+// aka ArrayElements
+class ArrayElems
+{
+public:
+  enum ArrayExprType
+  {
+    VALUES,
+    COPIED,
+  };
+
+  ArrayElems (Analysis::NodeMapping mappings) : mappings (mappings){};
+
+  virtual ~ArrayElems () {}
+
+  // Unique pointer custom clone ArrayElems function
+  std::unique_ptr<ArrayElems> clone_array_elems () const
+  {
+    return std::unique_ptr<ArrayElems> (clone_array_elems_impl ());
+  }
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (HIRFullVisitor &vis) = 0;
+
+  virtual ArrayExprType get_array_expr_type () const = 0;
+
+  Analysis::NodeMapping &get_mappings () { return mappings; }
+
+protected:
+  // pure virtual clone implementation
+  virtual ArrayElems *clone_array_elems_impl () const = 0;
+
+  Analysis::NodeMapping mappings;
+};
+
+// Value array elements
+class ArrayElemsValues : public ArrayElems
+{
+  std::vector<std::unique_ptr<Expr> > values;
+
+  // TODO: should this store location data?
+
+public:
+  ArrayElemsValues (Analysis::NodeMapping mappings,
+		    std::vector<std::unique_ptr<Expr> > elems)
+    : ArrayElems (mappings), values (std::move (elems))
+  {}
+
+  // copy constructor with vector clone
+  ArrayElemsValues (ArrayElemsValues const &other) : ArrayElems (other)
+  {
+    values.reserve (other.values.size ());
+    for (const auto &e : other.values)
+      values.push_back (e->clone_expr ());
+  }
+
+  // overloaded assignment operator with vector clone
+  ArrayElemsValues &operator= (ArrayElemsValues const &other)
+  {
+    values.reserve (other.values.size ());
+    for (const auto &e : other.values)
+      values.push_back (e->clone_expr ());
+
+    return *this;
+  }
+
+  // move constructors
+  ArrayElemsValues (ArrayElemsValues &&other) = default;
+  ArrayElemsValues &operator= (ArrayElemsValues &&other) = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  size_t get_num_elements () const { return values.size (); }
+
+  std::vector<std::unique_ptr<Expr> > &get_values () { return values; }
+
+  ArrayElems::ArrayExprType get_array_expr_type () const override final
+  {
+    return ArrayElems::ArrayExprType::VALUES;
+  }
+
+protected:
+  ArrayElemsValues *clone_array_elems_impl () const override
+  {
+    return new ArrayElemsValues (*this);
+  }
+};
+
+// Copied array element and number of copies
+class ArrayElemsCopied : public ArrayElems
+{
+  std::unique_ptr<Expr> elem_to_copy;
+  std::unique_ptr<Expr> num_copies;
+
+public:
+  // Constructor requires pointers for polymorphism
+  ArrayElemsCopied (Analysis::NodeMapping mappings,
+		    std::unique_ptr<Expr> copied_elem,
+		    std::unique_ptr<Expr> copy_amount)
+    : ArrayElems (mappings), elem_to_copy (std::move (copied_elem)),
+      num_copies (std::move (copy_amount))
+  {}
+
+  // Copy constructor required due to unique_ptr - uses custom clone
+  ArrayElemsCopied (ArrayElemsCopied const &other)
+    : ArrayElems (other), elem_to_copy (other.elem_to_copy->clone_expr ()),
+      num_copies (other.num_copies->clone_expr ())
+  {}
+
+  // Overloaded assignment operator for deep copying
+  ArrayElemsCopied &operator= (ArrayElemsCopied const &other)
+  {
+    elem_to_copy = other.elem_to_copy->clone_expr ();
+    num_copies = other.num_copies->clone_expr ();
+
+    return *this;
+  }
+
+  // move constructors
+  ArrayElemsCopied (ArrayElemsCopied &&other) = default;
+  ArrayElemsCopied &operator= (ArrayElemsCopied &&other) = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  Expr *get_elem_to_copy () { return elem_to_copy.get (); }
+
+  Expr *get_num_copies_expr () { return num_copies.get (); }
+
+  ArrayElems::ArrayExprType get_array_expr_type () const override final
+  {
+    return ArrayElems::ArrayExprType::COPIED;
+  }
+
+protected:
+  ArrayElemsCopied *clone_array_elems_impl () const override
+  {
+    return new ArrayElemsCopied (*this);
+  }
+};
+
+// Array definition-ish expression
+class ArrayExpr : public ExprWithoutBlock
+{
+  AST::AttrVec inner_attrs;
+  std::unique_ptr<ArrayElems> internal_elements;
+
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  AST::AttrVec get_inner_attrs () const { return inner_attrs; }
+
+  // Returns whether array expr has array elems or if it is just empty.
+  bool has_array_elems () const { return internal_elements != nullptr; }
+
+  // Constructor requires ArrayElems pointer
+  ArrayExpr (Analysis::NodeMapping mappings,
+	     std::unique_ptr<ArrayElems> array_elems,
+	     AST::AttrVec inner_attribs, AST::AttrVec outer_attribs,
+	     Location locus)
+    : ExprWithoutBlock (std::move (mappings), std::move (outer_attribs)),
+      inner_attrs (std::move (inner_attribs)),
+      internal_elements (std::move (array_elems)), locus (locus)
+  {}
+
+  // Copy constructor requires cloning ArrayElems for polymorphism to hold
+  ArrayExpr (ArrayExpr const &other)
+    : ExprWithoutBlock (other), inner_attrs (other.inner_attrs),
+      locus (other.locus)
+  {
+    if (other.has_array_elems ())
+      internal_elements = other.internal_elements->clone_array_elems ();
+  }
+
+  // Overload assignment operator to clone internal_elements
+  ArrayExpr &operator= (ArrayExpr const &other)
+  {
+    ExprWithoutBlock::operator= (other);
+    inner_attrs = other.inner_attrs;
+    if (other.has_array_elems ())
+      internal_elements = other.internal_elements->clone_array_elems ();
+    locus = other.locus;
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  ArrayExpr (ArrayExpr &&other) = default;
+  ArrayExpr &operator= (ArrayExpr &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  ArrayElems *get_internal_elements () { return internal_elements.get (); };
+
+  ExprType get_expression_type () const override final
+  {
+    return ExprType::Array;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ArrayExpr *clone_expr_impl () const override { return new ArrayExpr (*this); }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ArrayExpr *clone_expr_without_block_impl () const override
+  {
+    return new ArrayExpr (*this);
+  }
+};
+
+class ArrayIndexExpr : public ExprWithoutBlock
+{
+  std::unique_ptr<Expr> array_expr;
+  std::unique_ptr<Expr> index_expr;
+
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  ArrayIndexExpr (Analysis::NodeMapping mappings,
+		  std::unique_ptr<Expr> array_expr,
+		  std::unique_ptr<Expr> array_index_expr,
+		  AST::AttrVec outer_attribs, Location locus)
+    : ExprWithoutBlock (std::move (mappings), std::move (outer_attribs)),
+      array_expr (std::move (array_expr)),
+      index_expr (std::move (array_index_expr)), locus (locus)
+  {}
+
+  // Copy constructor requires special cloning due to unique_ptr
+  ArrayIndexExpr (ArrayIndexExpr const &other)
+    : ExprWithoutBlock (other), array_expr (other.array_expr->clone_expr ()),
+      index_expr (other.index_expr->clone_expr ()), locus (other.locus)
+  {}
+
+  // Overload assignment operator to clone unique_ptrs
+  ArrayIndexExpr &operator= (ArrayIndexExpr const &other)
+  {
+    ExprWithoutBlock::operator= (other);
+    array_expr = other.array_expr->clone_expr ();
+    index_expr = other.index_expr->clone_expr ();
+    // outer_attrs = other.outer_attrs;
+    locus = other.locus;
+
+    return *this;
+  }
+
+  // move constructors
+  ArrayIndexExpr (ArrayIndexExpr &&other) = default;
+  ArrayIndexExpr &operator= (ArrayIndexExpr &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  Expr *get_array_expr () { return array_expr.get (); }
+  Expr *get_index_expr () { return index_expr.get (); }
+
+  ExprType get_expression_type () const override final
+  {
+    return ExprType::ArrayIndex;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ArrayIndexExpr *clone_expr_impl () const override
+  {
+    return new ArrayIndexExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ArrayIndexExpr *clone_expr_without_block_impl () const override
+  {
+    return new ArrayIndexExpr (*this);
+  }
+};
+
+// HIR representation of a tuple
+class TupleExpr : public ExprWithoutBlock
+{
+  AST::AttrVec inner_attrs;
+
+  std::vector<std::unique_ptr<Expr> > tuple_elems;
+  // replaces (inlined version of) TupleElements
+
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  AST::AttrVec get_inner_attrs () const { return inner_attrs; }
+
+  TupleExpr (Analysis::NodeMapping mappings,
+	     std::vector<std::unique_ptr<Expr> > tuple_elements,
+	     AST::AttrVec inner_attribs, AST::AttrVec outer_attribs,
+	     Location locus)
+    : ExprWithoutBlock (std::move (mappings), std::move (outer_attribs)),
+      inner_attrs (std::move (inner_attribs)),
+      tuple_elems (std::move (tuple_elements)), locus (locus)
+  {}
+
+  // copy constructor with vector clone
+  TupleExpr (TupleExpr const &other)
+    : ExprWithoutBlock (other), inner_attrs (other.inner_attrs),
+      locus (other.locus)
+  {
+    tuple_elems.reserve (other.tuple_elems.size ());
+    for (const auto &e : other.tuple_elems)
+      tuple_elems.push_back (e->clone_expr ());
+  }
+
+  // overloaded assignment operator to vector clone
+  TupleExpr &operator= (TupleExpr const &other)
+  {
+    ExprWithoutBlock::operator= (other);
+    inner_attrs = other.inner_attrs;
+    locus = other.locus;
+
+    tuple_elems.reserve (other.tuple_elems.size ());
+    for (const auto &e : other.tuple_elems)
+      tuple_elems.push_back (e->clone_expr ());
+
+    return *this;
+  }
+
+  // move constructors
+  TupleExpr (TupleExpr &&other) = default;
+  TupleExpr &operator= (TupleExpr &&other) = default;
+
+  /* Note: syntactically, can disambiguate single-element tuple from parens with
+   * comma, i.e. (0,) rather than (0) */
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  const std::vector<std::unique_ptr<Expr> > &get_tuple_elems () const
+  {
+    return tuple_elems;
+  }
+  std::vector<std::unique_ptr<Expr> > &get_tuple_elems ()
+  {
+    return tuple_elems;
+  }
+
+  bool is_unit () const { return tuple_elems.size () == 0; }
+
+  ExprType get_expression_type () const override final
+  {
+    return ExprType::Tuple;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TupleExpr *clone_expr_impl () const override { return new TupleExpr (*this); }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TupleExpr *clone_expr_without_block_impl () const override
+  {
+    return new TupleExpr (*this);
+  }
+};
+
+class TupleIndexExpr : public ExprWithoutBlock
+{
+  std::unique_ptr<Expr> tuple_expr;
+  TupleIndex tuple_index;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  TupleIndex get_tuple_index () const { return tuple_index; }
+
+  TupleIndexExpr (Analysis::NodeMapping mappings,
+		  std::unique_ptr<Expr> tuple_expr, TupleIndex index,
+		  AST::AttrVec outer_attribs, Location locus)
+    : ExprWithoutBlock (std::move (mappings), std::move (outer_attribs)),
+      tuple_expr (std::move (tuple_expr)), tuple_index (index), locus (locus)
+  {}
+
+  // Copy constructor requires a clone for tuple_expr
+  TupleIndexExpr (TupleIndexExpr const &other)
+    : ExprWithoutBlock (other), tuple_expr (other.tuple_expr->clone_expr ()),
+      tuple_index (other.tuple_index), locus (other.locus)
+  {}
+
+  // Overload assignment operator in order to clone
+  TupleIndexExpr &operator= (TupleIndexExpr const &other)
+  {
+    ExprWithoutBlock::operator= (other);
+    tuple_expr = other.tuple_expr->clone_expr ();
+    tuple_index = other.tuple_index;
+    locus = other.locus;
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  TupleIndexExpr (TupleIndexExpr &&other) = default;
+  TupleIndexExpr &operator= (TupleIndexExpr &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  std::unique_ptr<Expr> &get_tuple_expr ()
+  {
+    rust_assert (tuple_expr != nullptr);
+    return tuple_expr;
+  }
+
+  ExprType get_expression_type () const override final
+  {
+    return ExprType::TupleIdx;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TupleIndexExpr *clone_expr_impl () const override
+  {
+    return new TupleIndexExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TupleIndexExpr *clone_expr_without_block_impl () const override
+  {
+    return new TupleIndexExpr (*this);
+  }
+};
+
+// Base struct/tuple/union value creator HIR node (abstract)
+class StructExpr : public ExprWithoutBlock
+{
+protected:
+  PathInExpression struct_name;
+
+  // Protected constructor to allow initialising struct_name
+  StructExpr (Analysis::NodeMapping mappings, PathInExpression struct_path,
+	      AST::AttrVec outer_attribs)
+    : ExprWithoutBlock (std::move (mappings), std::move (outer_attribs)),
+      struct_name (std::move (struct_path))
+  {}
+
+public:
+  PathInExpression &get_struct_name () { return struct_name; }
+
+  std::string as_string () const override;
+
+  ExprType get_expression_type () const override final
+  {
+    return ExprType::Struct;
+  }
+};
+
+// Actual HIR node of the struct creator (with no fields). Not abstract!
+class StructExprStruct : public StructExpr
+{
+  AST::AttrVec inner_attrs;
+
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  AST::AttrVec get_inner_attrs () const { return inner_attrs; }
+
+  // Constructor has to call protected constructor of base class
+  StructExprStruct (Analysis::NodeMapping mappings,
+		    PathInExpression struct_path, AST::AttrVec inner_attribs,
+		    AST::AttrVec outer_attribs, Location locus)
+    : StructExpr (std::move (mappings), std::move (struct_path),
+		  std::move (outer_attribs)),
+      inner_attrs (std::move (inner_attribs)), locus (locus)
+  {}
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  StructExprStruct *clone_expr_impl () const override
+  {
+    return new StructExprStruct (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  StructExprStruct *clone_expr_without_block_impl () const override
+  {
+    return new StructExprStruct (*this);
+  }
+};
+
+/* HIR node representing expression used to fill a struct's fields from another
+ * struct */
+struct StructBase
+{
+public:
+  std::unique_ptr<Expr> base_struct;
+
+  // TODO: should this store location data?
+  StructBase (std::unique_ptr<Expr> base_struct_ptr)
+    : base_struct (std::move (base_struct_ptr))
+  {}
+
+  // Copy constructor requires clone
+  StructBase (StructBase const &other)
+  {
+    /* HACK: gets around base_struct pointer being null (e.g. if no struct base
+     * exists) */
+    if (other.base_struct != nullptr)
+      other.base_struct->clone_expr ();
+  }
+
+  // Destructor
+  ~StructBase () = default;
+
+  // Overload assignment operator to clone base_struct
+  StructBase &operator= (StructBase const &other)
+  {
+    base_struct = other.base_struct->clone_expr ();
+
+    return *this;
+  }
+
+  // move constructors
+  StructBase (StructBase &&other) = default;
+  StructBase &operator= (StructBase &&other) = default;
+
+  // Returns a null expr-ed StructBase - error state
+  static StructBase error () { return StructBase (nullptr); }
+
+  // Returns whether StructBase is in error state
+  bool is_invalid () const { return base_struct == nullptr; }
+
+  std::string as_string () const;
+
+  Expr *get_base () { return base_struct.get (); }
+};
+
+/* Base HIR node for a single struct expression field (in struct instance
+ * creation) - abstract */
+class StructExprField
+{
+public:
+  enum StructExprFieldKind
+  {
+    IDENTIFIER_VALUE,
+    IDENTIFIER,
+    INDEX_VALUE,
+  };
+
+  virtual ~StructExprField () {}
+
+  // Unique pointer custom clone function
+  std::unique_ptr<StructExprField> clone_struct_expr_field () const
+  {
+    return std::unique_ptr<StructExprField> (clone_struct_expr_field_impl ());
+  }
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (HIRFullVisitor &vis) = 0;
+  virtual void accept_vis (HIRExpressionVisitor &vis) = 0;
+
+  Analysis::NodeMapping &get_mappings () { return mappings; }
+
+  Location get_locus () { return locus; }
+
+  virtual StructExprFieldKind get_kind () const = 0;
+
+protected:
+  // pure virtual clone implementation
+  virtual StructExprField *clone_struct_expr_field_impl () const = 0;
+
+  StructExprField (Analysis::NodeMapping mapping, Location locus)
+    : mappings (mapping), locus (locus)
+  {}
+
+  Analysis::NodeMapping mappings;
+  Location locus;
+};
+
+// Identifier-only variant of StructExprField HIR node
+class StructExprFieldIdentifier : public StructExprField
+{
+private:
+  Identifier field_name;
+
+  // TODO: should this store location data?
+public:
+  StructExprFieldIdentifier (Analysis::NodeMapping mapping,
+			     Identifier field_identifier, Location locus)
+    : StructExprField (mapping, locus),
+      field_name (std::move (field_identifier))
+  {}
+
+  std::string as_string () const override { return field_name; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  Identifier get_field_name () const { return field_name; }
+
+  StructExprFieldKind get_kind () const override
+  {
+    return StructExprFieldKind::IDENTIFIER;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  StructExprFieldIdentifier *clone_struct_expr_field_impl () const override
+  {
+    return new StructExprFieldIdentifier (*this);
+  }
+};
+
+/* Base HIR node for a single struct expression field with an assigned value -
+ * abstract */
+class StructExprFieldWithVal : public StructExprField
+{
+  std::unique_ptr<Expr> value;
+
+protected:
+  StructExprFieldWithVal (Analysis::NodeMapping mapping,
+			  std::unique_ptr<Expr> field_value, Location locus)
+    : StructExprField (mapping, locus), value (std::move (field_value))
+  {}
+
+  // Copy constructor requires clone
+  StructExprFieldWithVal (StructExprFieldWithVal const &other)
+    : StructExprField (other.mappings, other.locus),
+      value (other.value->clone_expr ())
+  {}
+
+  // Overload assignment operator to clone unique_ptr
+  StructExprFieldWithVal &operator= (StructExprFieldWithVal const &other)
+  {
+    value = other.value->clone_expr ();
+    mappings = other.mappings;
+    locus = other.locus;
+
+    return *this;
+  }
+
+  // move constructors
+  StructExprFieldWithVal (StructExprFieldWithVal &&other) = default;
+  StructExprFieldWithVal &operator= (StructExprFieldWithVal &&other) = default;
+
+public:
+  std::string as_string () const override;
+
+  Expr *get_value () { return value.get (); }
+};
+
+// Identifier and value variant of StructExprField HIR node
+class StructExprFieldIdentifierValue : public StructExprFieldWithVal
+{
+public:
+  Identifier field_name;
+
+  // TODO: should this store location data?
+
+  StructExprFieldIdentifierValue (Analysis::NodeMapping mapping,
+				  Identifier field_identifier,
+				  std::unique_ptr<Expr> field_value,
+				  Location locus)
+    : StructExprFieldWithVal (mapping, std::move (field_value), locus),
+      field_name (std::move (field_identifier))
+  {}
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  StructExprFieldKind get_kind () const override
+  {
+    return StructExprFieldKind::IDENTIFIER_VALUE;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  StructExprFieldIdentifierValue *clone_struct_expr_field_impl () const override
+  {
+    return new StructExprFieldIdentifierValue (*this);
+  }
+};
+
+// Tuple index and value variant of StructExprField HIR node
+class StructExprFieldIndexValue : public StructExprFieldWithVal
+{
+public:
+  TupleIndex index;
+
+  // TODO: should this store location data?
+
+  StructExprFieldIndexValue (Analysis::NodeMapping mapping,
+			     TupleIndex tuple_index,
+			     std::unique_ptr<Expr> field_value, Location locus)
+    : StructExprFieldWithVal (mapping, std::move (field_value), locus),
+      index (tuple_index)
+  {}
+
+  std::string as_string () const override;
+
+  TupleIndex get_tuple_index () const { return index; };
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  StructExprFieldKind get_kind () const override
+  {
+    return StructExprFieldKind::INDEX_VALUE;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  StructExprFieldIndexValue *clone_struct_expr_field_impl () const override
+  {
+    return new StructExprFieldIndexValue (*this);
+  }
+};
+
+// HIR node of a struct creator with fields
+class StructExprStructFields : public StructExprStruct
+{
+public:
+  // std::vector<StructExprField> fields;
+  std::vector<std::unique_ptr<StructExprField> > fields;
+
+  // bool has_struct_base;
+  // FIXME make unique_ptr
+  StructBase *struct_base;
+
+  // For unions there is just one field, the index
+  // is set when type checking
+  int union_index = -1;
+
+  std::string as_string () const override;
+
+  bool has_struct_base () const { return struct_base != nullptr; }
+
+  // Constructor for StructExprStructFields when no struct base is used
+  StructExprStructFields (
+    Analysis::NodeMapping mappings, PathInExpression struct_path,
+    std::vector<std::unique_ptr<StructExprField> > expr_fields, Location locus,
+    StructBase *base_struct, AST::AttrVec inner_attribs = AST::AttrVec (),
+    AST::AttrVec outer_attribs = AST::AttrVec ())
+    : StructExprStruct (std::move (mappings), std::move (struct_path),
+			std::move (inner_attribs), std::move (outer_attribs),
+			locus),
+      fields (std::move (expr_fields)), struct_base (base_struct)
+  {}
+
+  // copy constructor with vector clone
+  StructExprStructFields (StructExprStructFields const &other)
+    : StructExprStruct (other), struct_base (other.struct_base),
+      union_index (other.union_index)
+  {
+    fields.reserve (other.fields.size ());
+    for (const auto &e : other.fields)
+      fields.push_back (e->clone_struct_expr_field ());
+  }
+
+  // overloaded assignment operator with vector clone
+  StructExprStructFields &operator= (StructExprStructFields const &other)
+  {
+    StructExprStruct::operator= (other);
+    struct_base = other.struct_base;
+    union_index = other.union_index;
+
+    fields.reserve (other.fields.size ());
+    for (const auto &e : other.fields)
+      fields.push_back (e->clone_struct_expr_field ());
+
+    return *this;
+  }
+
+  // move constructors
+  StructExprStructFields (StructExprStructFields &&other) = default;
+  StructExprStructFields &operator= (StructExprStructFields &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  std::vector<std::unique_ptr<StructExprField> > &get_fields ()
+  {
+    return fields;
+  };
+
+  const std::vector<std::unique_ptr<StructExprField> > &get_fields () const
+  {
+    return fields;
+  };
+
+  void set_fields_as_owner (
+    std::vector<std::unique_ptr<StructExprField> > new_fields)
+  {
+    fields = std::move (new_fields);
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  StructExprStructFields *clone_expr_impl () const override
+  {
+    return new StructExprStructFields (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  StructExprStructFields *clone_expr_without_block_impl () const override
+  {
+    return new StructExprStructFields (*this);
+  }
+};
+
+// HIR node of the functional update struct creator
+class StructExprStructBase : public StructExprStruct
+{
+  StructBase struct_base;
+
+public:
+  std::string as_string () const override;
+
+  /*inline StructBase get_struct_base() const {
+      return struct_base;
+  }*/
+
+  StructExprStructBase (Analysis::NodeMapping mappings,
+			PathInExpression struct_path, StructBase base_struct,
+			AST::AttrVec inner_attribs, AST::AttrVec outer_attribs,
+			Location locus)
+    : StructExprStruct (std::move (mappings), std::move (struct_path),
+			std::move (inner_attribs), std::move (outer_attribs),
+			locus),
+      struct_base (std::move (base_struct))
+  {}
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  StructBase *get_struct_base () { return &struct_base; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  StructExprStructBase *clone_expr_impl () const override
+  {
+    return new StructExprStructBase (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  StructExprStructBase *clone_expr_without_block_impl () const override
+  {
+    return new StructExprStructBase (*this);
+  }
+};
+
+// Function call expression HIR node
+class CallExpr : public ExprWithoutBlock
+{
+  std::unique_ptr<Expr> function;
+  std::vector<std::unique_ptr<Expr> > params;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  CallExpr (Analysis::NodeMapping mappings, std::unique_ptr<Expr> function_expr,
+	    std::vector<std::unique_ptr<Expr> > function_params,
+	    AST::AttrVec outer_attribs, Location locus)
+    : ExprWithoutBlock (std::move (mappings), std::move (outer_attribs)),
+      function (std::move (function_expr)),
+      params (std::move (function_params)), locus (locus)
+  {}
+
+  // copy constructor requires clone
+  CallExpr (CallExpr const &other)
+    : ExprWithoutBlock (other), function (other.function->clone_expr ()),
+      locus (other.locus)
+  /*, params(other.params),*/ {
+    params.reserve (other.params.size ());
+    for (const auto &e : other.params)
+      params.push_back (e->clone_expr ());
+  }
+
+  // Overload assignment operator to clone
+  CallExpr &operator= (CallExpr const &other)
+  {
+    ExprWithoutBlock::operator= (other);
+    function = other.function->clone_expr ();
+    locus = other.locus;
+    // params = other.params;
+    // outer_attrs = other.outer_attrs;
+
+    params.reserve (other.params.size ());
+    for (const auto &e : other.params)
+      params.push_back (e->clone_expr ());
+
+    return *this;
+  }
+
+  // move constructors
+  CallExpr (CallExpr &&other) = default;
+  CallExpr &operator= (CallExpr &&other) = default;
+
+  // Returns whether function call has parameters.
+  bool has_params () const { return !params.empty (); }
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  Expr *get_fnexpr () { return function.get (); }
+
+  size_t num_params () const { return params.size (); }
+
+  std::vector<std::unique_ptr<Expr> > &get_arguments () { return params; }
+
+  const std::vector<std::unique_ptr<Expr> > &get_arguments () const
+  {
+    return params;
+  }
+
+  ExprType get_expression_type () const override final
+  {
+    return ExprType::Call;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  CallExpr *clone_expr_impl () const override { return new CallExpr (*this); }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  CallExpr *clone_expr_without_block_impl () const override
+  {
+    return new CallExpr (*this);
+  }
+};
+
+// Method call expression HIR node
+class MethodCallExpr : public ExprWithoutBlock
+{
+  std::unique_ptr<Expr> receiver;
+  PathExprSegment method_name;
+  std::vector<std::unique_ptr<Expr> > params;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  MethodCallExpr (Analysis::NodeMapping mappings,
+		  std::unique_ptr<Expr> call_receiver,
+		  PathExprSegment method_path,
+		  std::vector<std::unique_ptr<Expr> > method_params,
+		  AST::AttrVec outer_attribs, Location locus)
+    : ExprWithoutBlock (std::move (mappings), std::move (outer_attribs)),
+      receiver (std::move (call_receiver)),
+      method_name (std::move (method_path)), params (std::move (method_params)),
+      locus (locus)
+  {}
+
+  // copy constructor required due to cloning
+  MethodCallExpr (MethodCallExpr const &other)
+    : ExprWithoutBlock (other), receiver (other.receiver->clone_expr ()),
+      method_name (other.method_name), locus (other.locus)
+  /*, params(other.params),*/ {
+    params.reserve (other.params.size ());
+    for (const auto &e : other.params)
+      params.push_back (e->clone_expr ());
+  }
+
+  // Overload assignment operator to clone receiver object
+  MethodCallExpr &operator= (MethodCallExpr const &other)
+  {
+    ExprWithoutBlock::operator= (other);
+    receiver = other.receiver->clone_expr ();
+    method_name = other.method_name;
+    locus = other.locus;
+    // params = other.params;
+    // outer_attrs = other.outer_attrs;
+
+    params.reserve (other.params.size ());
+    for (const auto &e : other.params)
+      params.push_back (e->clone_expr ());
+
+    return *this;
+  }
+
+  // move constructors
+  MethodCallExpr (MethodCallExpr &&other) = default;
+  MethodCallExpr &operator= (MethodCallExpr &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  std::unique_ptr<Expr> &get_receiver () { return receiver; }
+
+  PathExprSegment get_method_name () const { return method_name; };
+
+  size_t num_params () const { return params.size (); }
+
+  std::vector<std::unique_ptr<Expr> > &get_arguments () { return params; }
+
+  const std::vector<std::unique_ptr<Expr> > &get_arguments () const
+  {
+    return params;
+  }
+
+  ExprType get_expression_type () const override final
+  {
+    return ExprType::MethodCall;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  MethodCallExpr *clone_expr_impl () const override
+  {
+    return new MethodCallExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  MethodCallExpr *clone_expr_without_block_impl () const override
+  {
+    return new MethodCallExpr (*this);
+  }
+};
+
+// aka FieldExpression
+// Struct or union field access expression HIR node
+class FieldAccessExpr : public ExprWithoutBlock
+{
+  std::unique_ptr<Expr> receiver;
+  Identifier field;
+
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  FieldAccessExpr (Analysis::NodeMapping mappings,
+		   std::unique_ptr<Expr> field_access_receiver,
+		   Identifier field_name, AST::AttrVec outer_attribs,
+		   Location locus)
+    : ExprWithoutBlock (std::move (mappings), std::move (outer_attribs)),
+      receiver (std::move (field_access_receiver)),
+      field (std::move (field_name)), locus (locus)
+  {}
+
+  // Copy constructor required due to unique_ptr cloning
+  FieldAccessExpr (FieldAccessExpr const &other)
+    : ExprWithoutBlock (other), receiver (other.receiver->clone_expr ()),
+      field (other.field), locus (other.locus)
+  {}
+
+  // Overload assignment operator to clone unique_ptr
+  FieldAccessExpr &operator= (FieldAccessExpr const &other)
+  {
+    ExprWithoutBlock::operator= (other);
+    receiver = other.receiver->clone_expr ();
+    field = other.field;
+    locus = other.locus;
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  FieldAccessExpr (FieldAccessExpr &&other) = default;
+  FieldAccessExpr &operator= (FieldAccessExpr &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  std::unique_ptr<Expr> &get_receiver_expr ()
+  {
+    rust_assert (receiver != nullptr);
+    return receiver;
+  }
+
+  Identifier get_field_name () const { return field; }
+
+  ExprType get_expression_type () const override final
+  {
+    return ExprType::FieldAccess;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  FieldAccessExpr *clone_expr_impl () const override
+  {
+    return new FieldAccessExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  FieldAccessExpr *clone_expr_without_block_impl () const override
+  {
+    return new FieldAccessExpr (*this);
+  }
+};
+
+// Closure parameter data structure
+struct ClosureParam
+{
+private:
+  std::unique_ptr<Pattern> pattern;
+
+  // bool has_type_given;
+  std::unique_ptr<Type> type;
+
+  // TODO: should this store location data?
+
+public:
+  // Returns whether the type of the parameter has been given.
+  bool has_type_given () const { return type != nullptr; }
+
+  // Constructor for closure parameter
+  ClosureParam (std::unique_ptr<Pattern> param_pattern,
+		std::unique_ptr<Type> param_type = nullptr)
+    : pattern (std::move (param_pattern)), type (std::move (param_type))
+  {}
+
+  // Copy constructor required due to cloning as a result of unique_ptrs
+  ClosureParam (ClosureParam const &other)
+    : pattern (other.pattern->clone_pattern ())
+  {
+    // guard to protect from null pointer dereference
+    if (other.type != nullptr)
+      type = other.type->clone_type ();
+  }
+
+  ~ClosureParam () = default;
+
+  // Assignment operator must be overloaded to clone as well
+  ClosureParam &operator= (ClosureParam const &other)
+  {
+    pattern = other.pattern->clone_pattern ();
+    type = other.type->clone_type ();
+
+    return *this;
+  }
+
+  // move constructors
+  ClosureParam (ClosureParam &&other) = default;
+  ClosureParam &operator= (ClosureParam &&other) = default;
+
+  // Returns whether closure parameter is in an error state.
+  bool is_error () const { return pattern == nullptr; }
+
+  // Creates an error state closure parameter.
+  static ClosureParam create_error () { return ClosureParam (nullptr); }
+
+  std::string as_string () const;
+};
+
+// Base closure definition expression HIR node - abstract
+class ClosureExpr : public ExprWithoutBlock
+{
+  bool has_move;
+  std::vector<ClosureParam> params;
+  Location locus;
+
+protected:
+  ClosureExpr (Analysis::NodeMapping mappings,
+	       std::vector<ClosureParam> closure_params, bool has_move,
+	       AST::AttrVec outer_attribs, Location locus)
+    : ExprWithoutBlock (std::move (mappings), std::move (outer_attribs)),
+      has_move (has_move), params (std::move (closure_params)), locus (locus)
+  {}
+
+public:
+  std::string as_string () const override;
+
+  Location get_locus () const override final { return locus; }
+
+  ExprType get_expression_type () const override final
+  {
+    return ExprType::Closure;
+  }
+};
+
+// Represents a non-type-specified closure expression HIR node
+class ClosureExprInner : public ClosureExpr
+{
+  std::unique_ptr<Expr> closure_inner;
+
+public:
+  std::string as_string () const override;
+
+  // Constructor for a ClosureExprInner
+  ClosureExprInner (Analysis::NodeMapping mappings,
+		    std::unique_ptr<Expr> closure_inner_expr,
+		    std::vector<ClosureParam> closure_params, Location locus,
+		    bool is_move = false,
+		    AST::AttrVec outer_attribs = AST::AttrVec ())
+    : ClosureExpr (std::move (mappings), std::move (closure_params), is_move,
+		   std::move (outer_attribs), locus),
+      closure_inner (std::move (closure_inner_expr))
+  {}
+
+  // Copy constructor must be defined to allow copying via cloning of unique_ptr
+  ClosureExprInner (ClosureExprInner const &other)
+    : ClosureExpr (other), closure_inner (other.closure_inner->clone_expr ())
+  {}
+
+  // Overload assignment operator to clone closure_inner
+  ClosureExprInner &operator= (ClosureExprInner const &other)
+  {
+    ClosureExpr::operator= (other);
+    closure_inner = other.closure_inner->clone_expr ();
+    // params = other.params;
+    // has_move = other.has_move;
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  ClosureExprInner (ClosureExprInner &&other) = default;
+  ClosureExprInner &operator= (ClosureExprInner &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ClosureExprInner *clone_expr_impl () const override
+  {
+    return new ClosureExprInner (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ClosureExprInner *clone_expr_without_block_impl () const override
+  {
+    return new ClosureExprInner (*this);
+  }
+};
+
+// A block HIR node
+class BlockExpr : public ExprWithBlock
+{
+public:
+  AST::AttrVec inner_attrs;
+  std::vector<std::unique_ptr<Stmt> > statements;
+  std::unique_ptr<Expr> expr;
+  bool tail_reachable;
+  Location start_locus;
+  Location end_locus;
+
+  std::string as_string () const override;
+
+  // Returns whether the block contains statements.
+  bool has_statements () const { return !statements.empty (); }
+
+  // Returns whether the block contains an expression
+  bool has_expr () const { return expr != nullptr; }
+
+  bool is_tail_reachable () const { return tail_reachable; }
+
+  BlockExpr (Analysis::NodeMapping mappings,
+	     std::vector<std::unique_ptr<Stmt> > block_statements,
+	     std::unique_ptr<Expr> block_expr, bool tail_reachable,
+	     AST::AttrVec inner_attribs, AST::AttrVec outer_attribs,
+	     Location start_locus, Location end_locus)
+    : ExprWithBlock (std::move (mappings), std::move (outer_attribs)),
+      inner_attrs (std::move (inner_attribs)),
+      statements (std::move (block_statements)), expr (std::move (block_expr)),
+      tail_reachable (tail_reachable), start_locus (start_locus),
+      end_locus (end_locus)
+  {}
+
+  // Copy constructor with clone
+  BlockExpr (BlockExpr const &other)
+    : ExprWithBlock (other), /*statements(other.statements),*/
+      inner_attrs (other.inner_attrs), start_locus (other.start_locus),
+      end_locus (other.end_locus)
+  {
+    // guard to protect from null pointer dereference
+    if (other.expr != nullptr)
+      expr = other.expr->clone_expr ();
+
+    statements.reserve (other.statements.size ());
+    for (const auto &e : other.statements)
+      statements.push_back (e->clone_stmt ());
+  }
+
+  // Overloaded assignment operator to clone pointer
+  BlockExpr &operator= (BlockExpr const &other)
+  {
+    ExprWithBlock::operator= (other);
+    // statements = other.statements;
+    expr = other.expr->clone_expr ();
+    inner_attrs = other.inner_attrs;
+    start_locus = other.end_locus;
+    end_locus = other.end_locus;
+    // outer_attrs = other.outer_attrs;
+
+    statements.reserve (other.statements.size ());
+    for (const auto &e : other.statements)
+      statements.push_back (e->clone_stmt ());
+
+    return *this;
+  }
+
+  // move constructors
+  BlockExpr (BlockExpr &&other) = default;
+  BlockExpr &operator= (BlockExpr &&other) = default;
+
+  // Unique pointer custom clone function
+  std::unique_ptr<BlockExpr> clone_block_expr () const
+  {
+    return std::unique_ptr<BlockExpr> (clone_block_expr_impl ());
+  }
+
+  Location get_locus () const override final { return start_locus; }
+
+  Location get_start_locus () const { return start_locus; }
+
+  Location get_end_locus () const { return end_locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  bool is_final_stmt (Stmt *stmt) { return statements.back ().get () == stmt; }
+
+  std::unique_ptr<Expr> &get_final_expr () { return expr; }
+
+  std::vector<std::unique_ptr<Stmt> > &get_statements () { return statements; }
+
+  ExprType get_expression_type () const final override
+  {
+    return ExprType::Block;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  BlockExpr *clone_expr_impl () const override
+  {
+    return clone_block_expr_impl ();
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  BlockExpr *clone_expr_with_block_impl () const override
+  {
+    return clone_block_expr_impl ();
+  }
+
+  /* This is the base method as not an abstract class - not virtual but could be
+   * in future if required. */
+  /*virtual*/ BlockExpr *clone_block_expr_impl () const
+  {
+    return new BlockExpr (*this);
+  }
+};
+
+// Represents a type-specified closure expression HIR node
+class ClosureExprInnerTyped : public ClosureExpr
+{
+  std::unique_ptr<Type> return_type;
+  std::unique_ptr<BlockExpr>
+    expr; // only used because may be polymorphic in future
+
+public:
+  std::string as_string () const override;
+
+  // Constructor potentially with a move
+  ClosureExprInnerTyped (Analysis::NodeMapping mappings,
+			 std::unique_ptr<Type> closure_return_type,
+			 std::unique_ptr<BlockExpr> closure_expr,
+			 std::vector<ClosureParam> closure_params,
+			 Location locus, bool is_move = false,
+			 AST::AttrVec outer_attribs = AST::AttrVec ())
+    : ClosureExpr (std::move (mappings), std::move (closure_params), is_move,
+		   std::move (outer_attribs), locus),
+      return_type (std::move (closure_return_type)),
+      expr (std::move (closure_expr))
+  {}
+
+  // Copy constructor requires cloning
+  ClosureExprInnerTyped (ClosureExprInnerTyped const &other)
+    : ClosureExpr (other), return_type (other.return_type->clone_type ()),
+      expr (other.expr->clone_block_expr ())
+  {}
+
+  // Overload assignment operator to clone unique_ptrs
+  ClosureExprInnerTyped &operator= (ClosureExprInnerTyped const &other)
+  {
+    ClosureExpr::operator= (other);
+    return_type = other.return_type->clone_type ();
+    expr = other.expr->clone_block_expr ();
+    // params = other.params;
+    // has_move = other.has_move;
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  ClosureExprInnerTyped (ClosureExprInnerTyped &&other) = default;
+  ClosureExprInnerTyped &operator= (ClosureExprInnerTyped &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ClosureExprInnerTyped *clone_expr_impl () const override
+  {
+    return new ClosureExprInnerTyped (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ClosureExprInnerTyped *clone_expr_without_block_impl () const override
+  {
+    return new ClosureExprInnerTyped (*this);
+  }
+};
+
+// HIR node representing continue expression within loops
+class ContinueExpr : public ExprWithoutBlock
+{
+  Lifetime label;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  // Returns true if the continue expr has a label.
+  bool has_label () const { return !label.is_error (); }
+
+  // Constructor for a ContinueExpr with a label.
+  ContinueExpr (Analysis::NodeMapping mappings, Location locus, Lifetime label,
+		AST::AttrVec outer_attribs = AST::AttrVec ())
+    : ExprWithoutBlock (std::move (mappings), std::move (outer_attribs)),
+      label (std::move (label)), locus (locus)
+  {}
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  Lifetime &get_label () { return label; }
+
+  ExprType get_expression_type () const final override
+  {
+    return ExprType::Continue;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ContinueExpr *clone_expr_impl () const override
+  {
+    return new ContinueExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ContinueExpr *clone_expr_without_block_impl () const override
+  {
+    return new ContinueExpr (*this);
+  }
+};
+
+// HIR node representing break expression within loops
+class BreakExpr : public ExprWithoutBlock
+{
+  // bool has_label;
+  Lifetime label;
+
+  // bool has_break_expr;
+  std::unique_ptr<Expr> break_expr;
+
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  // Returns whether the break expression has a label or not.
+  bool has_label () const { return !label.is_error (); }
+
+  /* Returns whether the break expression has an expression used in the break or
+   * not. */
+  bool has_break_expr () const { return break_expr != nullptr; }
+
+  // Constructor for a break expression
+  BreakExpr (Analysis::NodeMapping mappings, Location locus,
+	     Lifetime break_label,
+	     std::unique_ptr<Expr> expr_in_break = nullptr,
+	     AST::AttrVec outer_attribs = AST::AttrVec ())
+    : ExprWithoutBlock (std::move (mappings), std::move (outer_attribs)),
+      label (std::move (break_label)), break_expr (std::move (expr_in_break)),
+      locus (locus)
+  {}
+
+  // Copy constructor defined to use clone for unique pointer
+  BreakExpr (BreakExpr const &other)
+    : ExprWithoutBlock (other), label (other.label), locus (other.locus)
+  {
+    // guard to protect from null pointer dereference
+    if (other.break_expr != nullptr)
+      break_expr = other.break_expr->clone_expr ();
+  }
+
+  // Overload assignment operator to clone unique pointer
+  BreakExpr &operator= (BreakExpr const &other)
+  {
+    ExprWithoutBlock::operator= (other);
+    label = other.label;
+    break_expr = other.break_expr->clone_expr ();
+    locus = other.locus;
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  BreakExpr (BreakExpr &&other) = default;
+  BreakExpr &operator= (BreakExpr &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  Lifetime &get_label () { return label; }
+
+  std::unique_ptr<Expr> &get_expr () { return break_expr; }
+
+  ExprType get_expression_type () const override final
+  {
+    return ExprType::Break;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  BreakExpr *clone_expr_impl () const override { return new BreakExpr (*this); }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  BreakExpr *clone_expr_without_block_impl () const override
+  {
+    return new BreakExpr (*this);
+  }
+};
+
+// Base range expression HIR node object - abstract
+class RangeExpr : public ExprWithoutBlock
+{
+  Location locus;
+
+protected:
+  // outer attributes not allowed before range expressions
+  RangeExpr (Analysis::NodeMapping mappings, Location locus)
+    : ExprWithoutBlock (std::move (mappings), AST::AttrVec ()), locus (locus)
+  {}
+
+public:
+  Location get_locus () const override final { return locus; }
+
+  ExprType get_expression_type () const override final
+  {
+    return ExprType::Range;
+  }
+};
+
+// Range from (inclusive) and to (exclusive) expression HIR node object
+// aka RangeExpr; constructs a std::ops::Range object
+class RangeFromToExpr : public RangeExpr
+{
+  std::unique_ptr<Expr> from;
+  std::unique_ptr<Expr> to;
+
+public:
+  std::string as_string () const override;
+
+  RangeFromToExpr (Analysis::NodeMapping mappings,
+		   std::unique_ptr<Expr> range_from,
+		   std::unique_ptr<Expr> range_to, Location locus)
+    : RangeExpr (std::move (mappings), locus), from (std::move (range_from)),
+      to (std::move (range_to))
+  {}
+
+  // Copy constructor with cloning
+  RangeFromToExpr (RangeFromToExpr const &other)
+    : RangeExpr (other), from (other.from->clone_expr ()),
+      to (other.to->clone_expr ())
+  {}
+
+  // Overload assignment operator to clone unique pointers
+  RangeFromToExpr &operator= (RangeFromToExpr const &other)
+  {
+    RangeExpr::operator= (other);
+    from = other.from->clone_expr ();
+    to = other.to->clone_expr ();
+
+    return *this;
+  }
+
+  // move constructors
+  RangeFromToExpr (RangeFromToExpr &&other) = default;
+  RangeFromToExpr &operator= (RangeFromToExpr &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  std::unique_ptr<Expr> &get_from_expr () { return from; }
+  std::unique_ptr<Expr> &get_to_expr () { return to; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangeFromToExpr *clone_expr_impl () const override
+  {
+    return new RangeFromToExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangeFromToExpr *clone_expr_without_block_impl () const override
+  {
+    return new RangeFromToExpr (*this);
+  }
+};
+
+// Range from (inclusive) expression HIR node object
+// constructs a std::ops::RangeFrom object
+class RangeFromExpr : public RangeExpr
+{
+  std::unique_ptr<Expr> from;
+
+public:
+  std::string as_string () const override;
+
+  RangeFromExpr (Analysis::NodeMapping mappings,
+		 std::unique_ptr<Expr> range_from, Location locus)
+    : RangeExpr (std::move (mappings), locus), from (std::move (range_from))
+  {}
+
+  // Copy constructor with clone
+  RangeFromExpr (RangeFromExpr const &other)
+    : RangeExpr (other), from (other.from->clone_expr ())
+  {}
+
+  // Overload assignment operator to clone unique_ptr
+  RangeFromExpr &operator= (RangeFromExpr const &other)
+  {
+    RangeExpr::operator= (other);
+    from = other.from->clone_expr ();
+
+    return *this;
+  }
+
+  // move constructors
+  RangeFromExpr (RangeFromExpr &&other) = default;
+  RangeFromExpr &operator= (RangeFromExpr &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  std::unique_ptr<Expr> &get_from_expr () { return from; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangeFromExpr *clone_expr_impl () const override
+  {
+    return new RangeFromExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangeFromExpr *clone_expr_without_block_impl () const override
+  {
+    return new RangeFromExpr (*this);
+  }
+};
+
+// Range to (exclusive) expression HIR node object
+// constructs a std::ops::RangeTo object
+class RangeToExpr : public RangeExpr
+{
+  std::unique_ptr<Expr> to;
+
+public:
+  std::string as_string () const override;
+
+  // outer attributes not allowed
+  RangeToExpr (Analysis::NodeMapping mappings, std::unique_ptr<Expr> range_to,
+	       Location locus)
+    : RangeExpr (std::move (mappings), locus), to (std::move (range_to))
+  {}
+
+  // Copy constructor with clone
+  RangeToExpr (RangeToExpr const &other)
+    : RangeExpr (other), to (other.to->clone_expr ())
+  {}
+
+  // Overload assignment operator to clone unique_ptr
+  RangeToExpr &operator= (RangeToExpr const &other)
+  {
+    RangeExpr::operator= (other);
+    to = other.to->clone_expr ();
+
+    return *this;
+  }
+
+  // move constructors
+  RangeToExpr (RangeToExpr &&other) = default;
+  RangeToExpr &operator= (RangeToExpr &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  std::unique_ptr<Expr> &get_to_expr () { return to; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangeToExpr *clone_expr_impl () const override
+  {
+    return new RangeToExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangeToExpr *clone_expr_without_block_impl () const override
+  {
+    return new RangeToExpr (*this);
+  }
+};
+
+// Full range expression HIR node object
+// constructs a std::ops::RangeFull object
+class RangeFullExpr : public RangeExpr
+{
+public:
+  std::string as_string () const override;
+
+  RangeFullExpr (Analysis::NodeMapping mappings, Location locus)
+    : RangeExpr (std::move (mappings), locus)
+  {}
+  // outer attributes not allowed
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangeFullExpr *clone_expr_impl () const override
+  {
+    return new RangeFullExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangeFullExpr *clone_expr_without_block_impl () const override
+  {
+    return new RangeFullExpr (*this);
+  }
+};
+
+// Range from (inclusive) and to (inclusive) expression HIR node object
+// aka RangeInclusiveExpr; constructs a std::ops::RangeInclusive object
+class RangeFromToInclExpr : public RangeExpr
+{
+  std::unique_ptr<Expr> from;
+  std::unique_ptr<Expr> to;
+
+public:
+  std::string as_string () const override;
+
+  RangeFromToInclExpr (Analysis::NodeMapping mappings,
+		       std::unique_ptr<Expr> range_from,
+		       std::unique_ptr<Expr> range_to, Location locus)
+    : RangeExpr (std::move (mappings), locus), from (std::move (range_from)),
+      to (std::move (range_to))
+  {}
+  // outer attributes not allowed
+
+  // Copy constructor with clone
+  RangeFromToInclExpr (RangeFromToInclExpr const &other)
+    : RangeExpr (other), from (other.from->clone_expr ()),
+      to (other.to->clone_expr ())
+  {}
+
+  // Overload assignment operator to use clone
+  RangeFromToInclExpr &operator= (RangeFromToInclExpr const &other)
+  {
+    RangeExpr::operator= (other);
+    from = other.from->clone_expr ();
+    to = other.to->clone_expr ();
+
+    return *this;
+  }
+
+  // move constructors
+  RangeFromToInclExpr (RangeFromToInclExpr &&other) = default;
+  RangeFromToInclExpr &operator= (RangeFromToInclExpr &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  std::unique_ptr<Expr> &get_from_expr () { return from; }
+  std::unique_ptr<Expr> &get_to_expr () { return to; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangeFromToInclExpr *clone_expr_impl () const override
+  {
+    return new RangeFromToInclExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangeFromToInclExpr *clone_expr_without_block_impl () const override
+  {
+    return new RangeFromToInclExpr (*this);
+  }
+};
+
+// Range to (inclusive) expression HIR node object
+// aka RangeToInclusiveExpr; constructs a std::ops::RangeToInclusive object
+class RangeToInclExpr : public RangeExpr
+{
+  std::unique_ptr<Expr> to;
+
+public:
+  std::string as_string () const override;
+
+  RangeToInclExpr (Analysis::NodeMapping mappings,
+		   std::unique_ptr<Expr> range_to, Location locus)
+    : RangeExpr (std::move (mappings), locus), to (std::move (range_to))
+  {}
+  // outer attributes not allowed
+
+  // Copy constructor with clone
+  RangeToInclExpr (RangeToInclExpr const &other)
+    : RangeExpr (other), to (other.to->clone_expr ())
+  {}
+
+  // Overload assignment operator to clone pointer
+  RangeToInclExpr &operator= (RangeToInclExpr const &other)
+  {
+    RangeExpr::operator= (other);
+    to = other.to->clone_expr ();
+
+    return *this;
+  }
+
+  // move constructors
+  RangeToInclExpr (RangeToInclExpr &&other) = default;
+  RangeToInclExpr &operator= (RangeToInclExpr &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  std::unique_ptr<Expr> &get_to_expr () { return to; };
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangeToInclExpr *clone_expr_impl () const override
+  {
+    return new RangeToInclExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangeToInclExpr *clone_expr_without_block_impl () const override
+  {
+    return new RangeToInclExpr (*this);
+  }
+};
+
+// Return expression HIR node representation
+class ReturnExpr : public ExprWithoutBlock
+{
+public:
+  std::unique_ptr<Expr> return_expr;
+
+  Location locus;
+
+  std::string as_string () const override;
+
+  /* Returns whether the object has an expression returned (i.e. not void return
+   * type). */
+  bool has_return_expr () const { return return_expr != nullptr; }
+
+  // Constructor for ReturnExpr.
+  ReturnExpr (Analysis::NodeMapping mappings, Location locus,
+	      std::unique_ptr<Expr> returned_expr = nullptr,
+	      AST::AttrVec outer_attribs = AST::AttrVec ())
+    : ExprWithoutBlock (std::move (mappings), std::move (outer_attribs)),
+      return_expr (std::move (returned_expr)), locus (locus)
+  {}
+
+  // Copy constructor with clone
+  ReturnExpr (ReturnExpr const &other)
+    : ExprWithoutBlock (other), locus (other.locus)
+  {
+    // guard to protect from null pointer dereference
+    if (other.return_expr != nullptr)
+      return_expr = other.return_expr->clone_expr ();
+  }
+
+  // Overloaded assignment operator to clone return_expr pointer
+  ReturnExpr &operator= (ReturnExpr const &other)
+  {
+    ExprWithoutBlock::operator= (other);
+    return_expr = other.return_expr->clone_expr ();
+    locus = other.locus;
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  ReturnExpr (ReturnExpr &&other) = default;
+  ReturnExpr &operator= (ReturnExpr &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  Expr *get_expr () { return return_expr.get (); }
+
+  ExprType get_expression_type () const override final
+  {
+    return ExprType::Return;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ReturnExpr *clone_expr_impl () const override
+  {
+    return new ReturnExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ReturnExpr *clone_expr_without_block_impl () const override
+  {
+    return new ReturnExpr (*this);
+  }
+};
+
+// An unsafe block HIR node
+class UnsafeBlockExpr : public ExprWithBlock
+{
+  // Or just have it extend BlockExpr
+  std::unique_ptr<BlockExpr> expr;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  UnsafeBlockExpr (Analysis::NodeMapping mappings,
+		   std::unique_ptr<BlockExpr> block_expr,
+		   AST::AttrVec outer_attribs, Location locus)
+    : ExprWithBlock (std::move (mappings), std::move (outer_attribs)),
+      expr (std::move (block_expr)), locus (locus)
+  {}
+
+  // Copy constructor with clone
+  UnsafeBlockExpr (UnsafeBlockExpr const &other)
+    : ExprWithBlock (other), expr (other.expr->clone_block_expr ()),
+      locus (other.locus)
+  {}
+
+  // Overloaded assignment operator to clone
+  UnsafeBlockExpr &operator= (UnsafeBlockExpr const &other)
+  {
+    ExprWithBlock::operator= (other);
+    expr = other.expr->clone_block_expr ();
+    locus = other.locus;
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  UnsafeBlockExpr (UnsafeBlockExpr &&other) = default;
+  UnsafeBlockExpr &operator= (UnsafeBlockExpr &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  std::unique_ptr<BlockExpr> &get_block_expr () { return expr; }
+
+  ExprType get_expression_type () const override final
+  {
+    return ExprType::UnsafeBlock;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  UnsafeBlockExpr *clone_expr_impl () const override
+  {
+    return new UnsafeBlockExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  UnsafeBlockExpr *clone_expr_with_block_impl () const override
+  {
+    return new UnsafeBlockExpr (*this);
+  }
+};
+
+// Loop label expression HIR node used with break and continue expressions
+// TODO: inline?
+class LoopLabel /*: public Node*/
+{
+  Lifetime label; // or type LIFETIME_OR_LABEL
+
+  Location locus;
+
+  Analysis::NodeMapping mappings;
+
+public:
+  std::string as_string () const;
+
+  LoopLabel (Analysis::NodeMapping mapping, Lifetime loop_label, Location locus)
+    : label (std::move (loop_label)), locus (locus), mappings (mapping)
+  {}
+
+  // Returns whether the LoopLabel is in an error state.
+  bool is_error () const { return label.is_error (); }
+
+  Location get_locus () const { return locus; }
+
+  Analysis::NodeMapping &get_mappings () { return mappings; }
+
+  Lifetime &get_lifetime () { return label; }
+};
+
+// Base loop expression HIR node - aka LoopExpr
+class BaseLoopExpr : public ExprWithBlock
+{
+protected:
+  LoopLabel loop_label;
+  std::unique_ptr<BlockExpr> loop_block;
+
+private:
+  Location locus;
+
+protected:
+  // Constructor for BaseLoopExpr
+  BaseLoopExpr (Analysis::NodeMapping mappings,
+		std::unique_ptr<BlockExpr> loop_block, Location locus,
+		LoopLabel loop_label,
+		AST::AttrVec outer_attribs = AST::AttrVec ())
+    : ExprWithBlock (std::move (mappings), std::move (outer_attribs)),
+      loop_label (std::move (loop_label)), loop_block (std::move (loop_block)),
+      locus (locus)
+  {}
+
+  // Copy constructor for BaseLoopExpr with clone
+  BaseLoopExpr (BaseLoopExpr const &other)
+    : ExprWithBlock (other), loop_label (other.loop_label),
+      loop_block (other.loop_block->clone_block_expr ()), locus (other.locus)
+  {}
+
+  // Overloaded assignment operator to clone
+  BaseLoopExpr &operator= (BaseLoopExpr const &other)
+  {
+    ExprWithBlock::operator= (other);
+    loop_block = other.loop_block->clone_block_expr ();
+    loop_label = other.loop_label;
+    locus = other.locus;
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  BaseLoopExpr (BaseLoopExpr &&other) = default;
+  BaseLoopExpr &operator= (BaseLoopExpr &&other) = default;
+
+  ExprType get_expression_type () const final override
+  {
+    return ExprType::BaseLoop;
+  }
+
+public:
+  bool has_loop_label () const { return !loop_label.is_error (); }
+
+  Location get_locus () const override final { return locus; }
+
+  std::unique_ptr<HIR::BlockExpr> &get_loop_block () { return loop_block; };
+
+  LoopLabel &get_loop_label () { return loop_label; }
+};
+
+// 'Loop' expression (i.e. the infinite loop) HIR node
+class LoopExpr : public BaseLoopExpr
+{
+public:
+  std::string as_string () const override;
+
+  // Constructor for LoopExpr
+  LoopExpr (Analysis::NodeMapping mappings,
+	    std::unique_ptr<BlockExpr> loop_block, Location locus,
+	    LoopLabel loop_label, AST::AttrVec outer_attribs = AST::AttrVec ())
+    : BaseLoopExpr (std::move (mappings), std::move (loop_block), locus,
+		    std::move (loop_label), std::move (outer_attribs))
+  {}
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  LoopExpr *clone_expr_impl () const override { return new LoopExpr (*this); }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  LoopExpr *clone_expr_with_block_impl () const override
+  {
+    return new LoopExpr (*this);
+  }
+};
+
+// While loop expression HIR node (predicate loop)
+class WhileLoopExpr : public BaseLoopExpr
+{
+  std::unique_ptr<Expr> condition;
+
+public:
+  std::string as_string () const override;
+
+  // Constructor for while loop with loop label
+  WhileLoopExpr (Analysis::NodeMapping mappings,
+		 std::unique_ptr<Expr> loop_condition,
+		 std::unique_ptr<BlockExpr> loop_block, Location locus,
+		 LoopLabel loop_label,
+		 AST::AttrVec outer_attribs = AST::AttrVec ())
+    : BaseLoopExpr (std::move (mappings), std::move (loop_block), locus,
+		    std::move (loop_label), std::move (outer_attribs)),
+      condition (std::move (loop_condition))
+  {}
+
+  // Copy constructor with clone
+  WhileLoopExpr (WhileLoopExpr const &other)
+    : BaseLoopExpr (other), condition (other.condition->clone_expr ())
+  {}
+
+  // Overloaded assignment operator to clone
+  WhileLoopExpr &operator= (WhileLoopExpr const &other)
+  {
+    BaseLoopExpr::operator= (other);
+    condition = other.condition->clone_expr ();
+    // loop_block = other.loop_block->clone_block_expr();
+    // loop_label = other.loop_label;
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  WhileLoopExpr (WhileLoopExpr &&other) = default;
+  WhileLoopExpr &operator= (WhileLoopExpr &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  std::unique_ptr<Expr> &get_predicate_expr () { return condition; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  WhileLoopExpr *clone_expr_impl () const override
+  {
+    return new WhileLoopExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  WhileLoopExpr *clone_expr_with_block_impl () const override
+  {
+    return new WhileLoopExpr (*this);
+  }
+};
+
+// While let loop expression HIR node (predicate pattern loop)
+class WhileLetLoopExpr : public BaseLoopExpr
+{
+  // MatchArmPatterns patterns;
+  std::vector<std::unique_ptr<Pattern> > match_arm_patterns; // inlined
+  std::unique_ptr<Expr> condition;
+
+public:
+  std::string as_string () const override;
+
+  // Constructor with a loop label
+  WhileLetLoopExpr (Analysis::NodeMapping mappings,
+		    std::vector<std::unique_ptr<Pattern> > match_arm_patterns,
+		    std::unique_ptr<Expr> condition,
+		    std::unique_ptr<BlockExpr> loop_block, Location locus,
+		    LoopLabel loop_label,
+		    AST::AttrVec outer_attribs = AST::AttrVec ())
+    : BaseLoopExpr (std::move (mappings), std::move (loop_block), locus,
+		    std::move (loop_label), std::move (outer_attribs)),
+      match_arm_patterns (std::move (match_arm_patterns)),
+      condition (std::move (condition))
+  {}
+
+  // Copy constructor with clone
+  WhileLetLoopExpr (WhileLetLoopExpr const &other)
+    : BaseLoopExpr (other),
+      /*match_arm_patterns(other.match_arm_patterns),*/ condition (
+	other.condition->clone_expr ())
+  {
+    match_arm_patterns.reserve (other.match_arm_patterns.size ());
+    for (const auto &e : other.match_arm_patterns)
+      match_arm_patterns.push_back (e->clone_pattern ());
+  }
+
+  // Overloaded assignment operator to clone pointers
+  WhileLetLoopExpr &operator= (WhileLetLoopExpr const &other)
+  {
+    BaseLoopExpr::operator= (other);
+    // match_arm_patterns = other.match_arm_patterns;
+    condition = other.condition->clone_expr ();
+    // loop_block = other.loop_block->clone_block_expr();
+    // loop_label = other.loop_label;
+    // outer_attrs = other.outer_attrs;
+
+    match_arm_patterns.reserve (other.match_arm_patterns.size ());
+    for (const auto &e : other.match_arm_patterns)
+      match_arm_patterns.push_back (e->clone_pattern ());
+
+    return *this;
+  }
+
+  // move constructors
+  WhileLetLoopExpr (WhileLetLoopExpr &&other) = default;
+  WhileLetLoopExpr &operator= (WhileLetLoopExpr &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  std::unique_ptr<Expr> &get_cond () { return condition; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  WhileLetLoopExpr *clone_expr_impl () const override
+  {
+    return new WhileLetLoopExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  WhileLetLoopExpr *clone_expr_with_block_impl () const override
+  {
+    return new WhileLetLoopExpr (*this);
+  }
+};
+
+// For loop expression HIR node (iterator loop)
+class ForLoopExpr : public BaseLoopExpr
+{
+  std::unique_ptr<Pattern> pattern;
+  std::unique_ptr<Expr> iterator_expr;
+
+public:
+  std::string as_string () const override;
+
+  // Constructor with loop label
+  ForLoopExpr (Analysis::NodeMapping mappings,
+	       std::unique_ptr<Pattern> loop_pattern,
+	       std::unique_ptr<Expr> iterator_expr,
+	       std::unique_ptr<BlockExpr> loop_body, Location locus,
+	       LoopLabel loop_label,
+	       AST::AttrVec outer_attribs = AST::AttrVec ())
+    : BaseLoopExpr (std::move (mappings), std::move (loop_body), locus,
+		    std::move (loop_label), std::move (outer_attribs)),
+      pattern (std::move (loop_pattern)),
+      iterator_expr (std::move (iterator_expr))
+  {}
+
+  // Copy constructor with clone
+  ForLoopExpr (ForLoopExpr const &other)
+    : BaseLoopExpr (other), pattern (other.pattern->clone_pattern ()),
+      iterator_expr (other.iterator_expr->clone_expr ())
+  {}
+
+  // Overloaded assignment operator to clone
+  ForLoopExpr &operator= (ForLoopExpr const &other)
+  {
+    BaseLoopExpr::operator= (other);
+    pattern = other.pattern->clone_pattern ();
+    iterator_expr = other.iterator_expr->clone_expr ();
+    /*loop_block = other.loop_block->clone_block_expr();
+    loop_label = other.loop_label;
+    outer_attrs = other.outer_attrs;*/
+
+    return *this;
+  }
+
+  // move constructors
+  ForLoopExpr (ForLoopExpr &&other) = default;
+  ForLoopExpr &operator= (ForLoopExpr &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  std::unique_ptr<Expr> &get_iterator_expr () { return iterator_expr; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ForLoopExpr *clone_expr_impl () const override
+  {
+    return new ForLoopExpr (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ForLoopExpr *clone_expr_with_block_impl () const override
+  {
+    return new ForLoopExpr (*this);
+  }
+};
+
+// forward decl for IfExpr
+class IfLetExpr;
+
+// Base if expression with no "else" or "if let" HIR node
+class IfExpr : public ExprWithBlock
+{
+  std::unique_ptr<Expr> condition;
+  std::unique_ptr<BlockExpr> if_block;
+
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  IfExpr (Analysis::NodeMapping mappings, std::unique_ptr<Expr> condition,
+	  std::unique_ptr<BlockExpr> if_block, Location locus)
+    : ExprWithBlock (std::move (mappings), AST::AttrVec ()),
+      condition (std::move (condition)), if_block (std::move (if_block)),
+      locus (locus)
+  {}
+  // outer attributes are never allowed on IfExprs
+
+  // Copy constructor with clone
+  IfExpr (IfExpr const &other)
+    : ExprWithBlock (other), condition (other.condition->clone_expr ()),
+      if_block (other.if_block->clone_block_expr ()), locus (other.locus)
+  {}
+
+  // Overloaded assignment operator to clone expressions
+  IfExpr &operator= (IfExpr const &other)
+  {
+    ExprWithBlock::operator= (other);
+    condition = other.condition->clone_expr ();
+    if_block = other.if_block->clone_block_expr ();
+    locus = other.locus;
+
+    return *this;
+  }
+
+  // move constructors
+  IfExpr (IfExpr &&other) = default;
+  IfExpr &operator= (IfExpr &&other) = default;
+
+  // Unique pointer custom clone function
+  std::unique_ptr<IfExpr> clone_if_expr () const
+  {
+    return std::unique_ptr<IfExpr> (clone_if_expr_impl ());
+  }
+
+  /* Note that multiple "else if"s are handled via nested HIRs rather than a
+   * vector of else ifs - i.e. not like a switch statement. TODO - is this a
+   * better approach? or does it not parse correctly and have downsides? */
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  void vis_if_condition (HIRFullVisitor &vis) { condition->accept_vis (vis); }
+  void vis_if_block (HIRFullVisitor &vis) { if_block->accept_vis (vis); }
+
+  Expr *get_if_condition () { return condition.get (); }
+  BlockExpr *get_if_block () { return if_block.get (); }
+
+  ExprType get_expression_type () const final override { return ExprType::If; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfExpr *clone_expr_impl () const override { return new IfExpr (*this); }
+
+  // Base clone function but still concrete as concrete base class
+  virtual IfExpr *clone_if_expr_impl () const { return new IfExpr (*this); }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfExpr *clone_expr_with_block_impl () const override
+  {
+    return new IfExpr (*this);
+  }
+};
+
+// If expression with an ending "else" expression HIR node (trailing)
+class IfExprConseqElse : public IfExpr
+{
+  std::unique_ptr<BlockExpr> else_block;
+
+public:
+  std::string as_string () const override;
+
+  IfExprConseqElse (Analysis::NodeMapping mappings,
+		    std::unique_ptr<Expr> condition,
+		    std::unique_ptr<BlockExpr> if_block,
+		    std::unique_ptr<BlockExpr> else_block, Location locus)
+    : IfExpr (std::move (mappings), std::move (condition), std::move (if_block),
+	      locus),
+      else_block (std::move (else_block))
+  {}
+  // again, outer attributes not allowed
+
+  // Copy constructor with clone
+  IfExprConseqElse (IfExprConseqElse const &other)
+    : IfExpr (other), else_block (other.else_block->clone_block_expr ())
+  {}
+
+  // Overloaded assignment operator with cloning
+  IfExprConseqElse &operator= (IfExprConseqElse const &other)
+  {
+    IfExpr::operator= (other);
+    // condition = other.condition->clone_expr();
+    // if_block = other.if_block->clone_block_expr();
+    else_block = other.else_block->clone_block_expr ();
+
+    return *this;
+  }
+
+  // move constructors
+  IfExprConseqElse (IfExprConseqElse &&other) = default;
+  IfExprConseqElse &operator= (IfExprConseqElse &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  void vis_else_block (HIRFullVisitor &vis) { else_block->accept_vis (vis); }
+
+  BlockExpr *get_else_block () { return else_block.get (); }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfExprConseqElse *clone_expr_impl () const override
+  {
+    return new IfExprConseqElse (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfExprConseqElse *clone_expr_with_block_impl () const override
+  {
+    return new IfExprConseqElse (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfExprConseqElse *clone_if_expr_impl () const override
+  {
+    return new IfExprConseqElse (*this);
+  }
+};
+
+// If expression with an ending "else if" expression HIR node
+class IfExprConseqIf : public IfExpr
+{
+  std::unique_ptr<IfExpr> conseq_if_expr;
+
+public:
+  std::string as_string () const override;
+
+  IfExprConseqIf (Analysis::NodeMapping mappings,
+		  std::unique_ptr<Expr> condition,
+		  std::unique_ptr<BlockExpr> if_block,
+		  std::unique_ptr<IfExpr> conseq_if_expr, Location locus)
+    : IfExpr (std::move (mappings), std::move (condition), std::move (if_block),
+	      locus),
+      conseq_if_expr (std::move (conseq_if_expr))
+  {}
+  // outer attributes not allowed
+
+  // Copy constructor with clone
+  IfExprConseqIf (IfExprConseqIf const &other)
+    : IfExpr (other), conseq_if_expr (other.conseq_if_expr->clone_if_expr ())
+  {}
+
+  // Overloaded assignment operator to use clone
+  IfExprConseqIf &operator= (IfExprConseqIf const &other)
+  {
+    IfExpr::operator= (other);
+    // condition = other.condition->clone_expr();
+    // if_block = other.if_block->clone_block_expr();
+    conseq_if_expr = other.conseq_if_expr->clone_if_expr ();
+
+    return *this;
+  }
+
+  // move constructors
+  IfExprConseqIf (IfExprConseqIf &&other) = default;
+  IfExprConseqIf &operator= (IfExprConseqIf &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  void vis_conseq_if_expr (HIRFullVisitor &vis)
+  {
+    conseq_if_expr->accept_vis (vis);
+  }
+
+  IfExpr *get_conseq_if_expr () { return conseq_if_expr.get (); }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfExprConseqIf *clone_expr_impl () const override
+  {
+    return new IfExprConseqIf (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfExprConseqIf *clone_expr_with_block_impl () const override
+  {
+    return new IfExprConseqIf (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfExprConseqIf *clone_if_expr_impl () const override
+  {
+    return new IfExprConseqIf (*this);
+  }
+};
+
+// Basic "if let" expression HIR node with no else
+class IfLetExpr : public ExprWithBlock
+{
+  // MatchArmPatterns patterns;
+  std::vector<std::unique_ptr<Pattern> > match_arm_patterns; // inlined
+  std::unique_ptr<Expr> value;
+  std::unique_ptr<BlockExpr> if_block;
+
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  IfLetExpr (Analysis::NodeMapping mappings,
+	     std::vector<std::unique_ptr<Pattern> > match_arm_patterns,
+	     std::unique_ptr<Expr> value, std::unique_ptr<BlockExpr> if_block,
+	     Location locus)
+    : ExprWithBlock (std::move (mappings), AST::AttrVec ()),
+      match_arm_patterns (std::move (match_arm_patterns)),
+      value (std::move (value)), if_block (std::move (if_block)), locus (locus)
+  {}
+  // outer attributes not allowed on if let exprs either
+
+  // copy constructor with clone
+  IfLetExpr (IfLetExpr const &other)
+    : ExprWithBlock (other),
+      /*match_arm_patterns(other.match_arm_patterns),*/ value (
+	other.value->clone_expr ()),
+      if_block (other.if_block->clone_block_expr ()), locus (other.locus)
+  {
+    match_arm_patterns.reserve (other.match_arm_patterns.size ());
+    for (const auto &e : other.match_arm_patterns)
+      match_arm_patterns.push_back (e->clone_pattern ());
+  }
+
+  // overload assignment operator to clone
+  IfLetExpr &operator= (IfLetExpr const &other)
+  {
+    ExprWithBlock::operator= (other);
+    // match_arm_patterns = other.match_arm_patterns;
+    value = other.value->clone_expr ();
+    if_block = other.if_block->clone_block_expr ();
+    locus = other.locus;
+
+    match_arm_patterns.reserve (other.match_arm_patterns.size ());
+    for (const auto &e : other.match_arm_patterns)
+      match_arm_patterns.push_back (e->clone_pattern ());
+
+    return *this;
+  }
+
+  // move constructors
+  IfLetExpr (IfLetExpr &&other) = default;
+  IfLetExpr &operator= (IfLetExpr &&other) = default;
+
+  // Unique pointer custom clone function
+  std::unique_ptr<IfLetExpr> clone_if_let_expr () const
+  {
+    return std::unique_ptr<IfLetExpr> (clone_if_let_expr_impl ());
+  }
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  std::unique_ptr<Expr> &get_scrutinee_expr ()
+  {
+    rust_assert (value != nullptr);
+    return value;
+  }
+
+  std::vector<std::unique_ptr<Pattern> > &get_patterns ()
+  {
+    return match_arm_patterns;
+  }
+
+  BlockExpr *get_if_block () { return if_block.get (); }
+
+  ExprType get_expression_type () const final override
+  {
+    return ExprType::IfLet;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfLetExpr *clone_expr_impl () const override { return new IfLetExpr (*this); }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfLetExpr *clone_expr_with_block_impl () const override
+  {
+    return new IfLetExpr (*this);
+  }
+
+  // Base clone function but still concrete as concrete base class
+  virtual IfLetExpr *clone_if_let_expr_impl () const
+  {
+    return new IfLetExpr (*this);
+  }
+};
+
+// If expression with an ending "else if let" expression HIR node
+class IfExprConseqIfLet : public IfExpr
+{
+  std::unique_ptr<IfLetExpr> if_let_expr;
+
+public:
+  std::string as_string () const override;
+
+  IfExprConseqIfLet (Analysis::NodeMapping mappings,
+		     std::unique_ptr<Expr> condition,
+		     std::unique_ptr<BlockExpr> if_block,
+		     std::unique_ptr<IfLetExpr> conseq_if_let_expr,
+		     Location locus)
+    : IfExpr (std::move (mappings), std::move (condition), std::move (if_block),
+	      locus),
+      if_let_expr (std::move (conseq_if_let_expr))
+  {}
+  // outer attributes not allowed
+
+  // Copy constructor with clone
+  IfExprConseqIfLet (IfExprConseqIfLet const &other)
+    : IfExpr (other), if_let_expr (other.if_let_expr->clone_if_let_expr ())
+  {}
+
+  // Overloaded assignment operator to use clone
+  IfExprConseqIfLet &operator= (IfExprConseqIfLet const &other)
+  {
+    IfExpr::operator= (other);
+    // condition = other.condition->clone_expr();
+    // if_block = other.if_block->clone_block_expr();
+    if_let_expr = other.if_let_expr->clone_if_let_expr ();
+
+    return *this;
+  }
+
+  // move constructors
+  IfExprConseqIfLet (IfExprConseqIfLet &&other) = default;
+  IfExprConseqIfLet &operator= (IfExprConseqIfLet &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfExprConseqIfLet *clone_expr_impl () const override
+  {
+    return new IfExprConseqIfLet (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfExprConseqIfLet *clone_expr_with_block_impl () const override
+  {
+    return new IfExprConseqIfLet (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfExprConseqIfLet *clone_if_expr_impl () const override
+  {
+    return new IfExprConseqIfLet (*this);
+  }
+};
+
+/* HIR node representing "if let" expression with an "else" expression at the
+ * end */
+class IfLetExprConseqElse : public IfLetExpr
+{
+  std::unique_ptr<BlockExpr> else_block;
+
+public:
+  std::string as_string () const override;
+
+  IfLetExprConseqElse (
+    Analysis::NodeMapping mappings,
+    std::vector<std::unique_ptr<Pattern> > match_arm_patterns,
+    std::unique_ptr<Expr> value, std::unique_ptr<BlockExpr> if_block,
+    std::unique_ptr<BlockExpr> else_block, Location locus)
+    : IfLetExpr (std::move (mappings), std::move (match_arm_patterns),
+		 std::move (value), std::move (if_block), locus),
+      else_block (std::move (else_block))
+  {}
+  // outer attributes not allowed
+
+  // copy constructor with clone
+  IfLetExprConseqElse (IfLetExprConseqElse const &other)
+    : IfLetExpr (other), else_block (other.else_block->clone_block_expr ())
+  {}
+
+  // overload assignment operator to clone
+  IfLetExprConseqElse &operator= (IfLetExprConseqElse const &other)
+  {
+    IfLetExpr::operator= (other);
+    // match_arm_patterns = other.match_arm_patterns;
+    // value = other.value->clone_expr();
+    // if_block = other.if_block->clone_block_expr();
+    else_block = other.else_block->clone_block_expr ();
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  IfLetExprConseqElse (IfLetExprConseqElse &&other) = default;
+  IfLetExprConseqElse &operator= (IfLetExprConseqElse &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfLetExprConseqElse *clone_expr_impl () const override
+  {
+    return new IfLetExprConseqElse (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfLetExprConseqElse *clone_expr_with_block_impl () const override
+  {
+    return new IfLetExprConseqElse (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfLetExprConseqElse *clone_if_let_expr_impl () const override
+  {
+    return new IfLetExprConseqElse (*this);
+  }
+};
+
+/* HIR node representing "if let" expression with an "else if" expression at the
+ * end */
+class IfLetExprConseqIf : public IfLetExpr
+{
+  std::unique_ptr<IfExpr> if_expr;
+
+public:
+  std::string as_string () const override;
+
+  IfLetExprConseqIf (Analysis::NodeMapping mappings,
+		     std::vector<std::unique_ptr<Pattern> > match_arm_patterns,
+		     std::unique_ptr<Expr> value,
+		     std::unique_ptr<BlockExpr> if_block,
+		     std::unique_ptr<IfExpr> if_expr, Location locus)
+    : IfLetExpr (std::move (mappings), std::move (match_arm_patterns),
+		 std::move (value), std::move (if_block), locus),
+      if_expr (std::move (if_expr))
+  {}
+  // again, outer attributes not allowed
+
+  // copy constructor with clone
+  IfLetExprConseqIf (IfLetExprConseqIf const &other)
+    : IfLetExpr (other), if_expr (other.if_expr->clone_if_expr ())
+  {}
+
+  // overload assignment operator to clone
+  IfLetExprConseqIf &operator= (IfLetExprConseqIf const &other)
+  {
+    IfLetExpr::operator= (other);
+    // match_arm_patterns = other.match_arm_patterns;
+    // value = other.value->clone_expr();
+    // if_block = other.if_block->clone_block_expr();
+    if_expr = other.if_expr->clone_if_expr ();
+
+    return *this;
+  }
+
+  // move constructors
+  IfLetExprConseqIf (IfLetExprConseqIf &&other) = default;
+  IfLetExprConseqIf &operator= (IfLetExprConseqIf &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfLetExprConseqIf *clone_expr_impl () const override
+  {
+    return new IfLetExprConseqIf (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfLetExprConseqIf *clone_expr_with_block_impl () const override
+  {
+    return new IfLetExprConseqIf (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfLetExprConseqIf *clone_if_let_expr_impl () const override
+  {
+    return new IfLetExprConseqIf (*this);
+  }
+};
+
+/* HIR node representing "if let" expression with an "else if let" expression at
+ * the end */
+class IfLetExprConseqIfLet : public IfLetExpr
+{
+  std::unique_ptr<IfLetExpr> if_let_expr;
+
+public:
+  std::string as_string () const override;
+
+  IfLetExprConseqIfLet (
+    Analysis::NodeMapping mappings,
+    std::vector<std::unique_ptr<Pattern> > match_arm_patterns,
+    std::unique_ptr<Expr> value, std::unique_ptr<BlockExpr> if_block,
+    std::unique_ptr<IfLetExpr> if_let_expr, Location locus)
+    : IfLetExpr (std::move (mappings), std::move (match_arm_patterns),
+		 std::move (value), std::move (if_block), locus),
+      if_let_expr (std::move (if_let_expr))
+  {}
+  // outer attributes not allowed
+
+  // copy constructor with clone
+  IfLetExprConseqIfLet (IfLetExprConseqIfLet const &other)
+    : IfLetExpr (other), if_let_expr (other.if_let_expr->clone_if_let_expr ())
+  {}
+
+  // overload assignment operator to clone
+  IfLetExprConseqIfLet &operator= (IfLetExprConseqIfLet const &other)
+  {
+    IfLetExpr::operator= (other);
+    // match_arm_patterns = other.match_arm_patterns;
+    // value = other.value->clone_expr();
+    // if_block = other.if_block->clone_block_expr();
+    if_let_expr = other.if_let_expr->clone_if_let_expr ();
+
+    return *this;
+  }
+
+  // move constructors
+  IfLetExprConseqIfLet (IfLetExprConseqIfLet &&other) = default;
+  IfLetExprConseqIfLet &operator= (IfLetExprConseqIfLet &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfLetExprConseqIfLet *clone_expr_impl () const override
+  {
+    return new IfLetExprConseqIfLet (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfLetExprConseqIfLet *clone_expr_with_block_impl () const override
+  {
+    return new IfLetExprConseqIfLet (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IfLetExprConseqIfLet *clone_if_let_expr_impl () const override
+  {
+    return new IfLetExprConseqIfLet (*this);
+  }
+};
+
+// Match arm expression
+struct MatchArm
+{
+private:
+  AST::AttrVec outer_attrs;
+  std::vector<std::unique_ptr<Pattern> > match_arm_patterns;
+  std::unique_ptr<Expr> guard_expr;
+  Location locus;
+
+public:
+  // Returns whether the MatchArm has a match arm guard expression
+  bool has_match_arm_guard () const { return guard_expr != nullptr; }
+
+  // Constructor for match arm with a guard expression
+  MatchArm (std::vector<std::unique_ptr<Pattern> > match_arm_patterns,
+	    Location locus, std::unique_ptr<Expr> guard_expr = nullptr,
+	    AST::AttrVec outer_attrs = AST::AttrVec ())
+    : outer_attrs (std::move (outer_attrs)),
+      match_arm_patterns (std::move (match_arm_patterns)),
+      guard_expr (std::move (guard_expr)), locus (locus)
+  {}
+
+  // Copy constructor with clone
+  MatchArm (MatchArm const &other) : outer_attrs (other.outer_attrs)
+  {
+    // guard to protect from null pointer dereference
+    if (other.guard_expr != nullptr)
+      guard_expr = other.guard_expr->clone_expr ();
+
+    match_arm_patterns.reserve (other.match_arm_patterns.size ());
+    for (const auto &e : other.match_arm_patterns)
+      match_arm_patterns.push_back (e->clone_pattern ());
+
+    locus = other.locus;
+  }
+
+  ~MatchArm () = default;
+
+  // Overload assignment operator to clone
+  MatchArm &operator= (MatchArm const &other)
+  {
+    outer_attrs = other.outer_attrs;
+
+    if (other.guard_expr != nullptr)
+      guard_expr = other.guard_expr->clone_expr ();
+
+    match_arm_patterns.clear ();
+    match_arm_patterns.reserve (other.match_arm_patterns.size ());
+    for (const auto &e : other.match_arm_patterns)
+      match_arm_patterns.push_back (e->clone_pattern ());
+
+    return *this;
+  }
+
+  // move constructors
+  MatchArm (MatchArm &&other) = default;
+  MatchArm &operator= (MatchArm &&other) = default;
+
+  // Returns whether match arm is in an error state.
+  bool is_error () const { return match_arm_patterns.empty (); }
+
+  // Creates a match arm in an error state.
+  static MatchArm create_error ()
+  {
+    Location locus = Location ();
+    return MatchArm (std::vector<std::unique_ptr<Pattern> > (), locus);
+  }
+
+  std::string as_string () const;
+
+  std::vector<std::unique_ptr<Pattern> > &get_patterns ()
+  {
+    return match_arm_patterns;
+  }
+
+  std::unique_ptr<Expr> &get_guard_expr ()
+  {
+    rust_assert (has_match_arm_guard ());
+    return guard_expr;
+  }
+
+  Location get_locus () const { return locus; }
+};
+
+/* A "match case" - a correlated match arm and resulting expression. Not
+ * abstract. */
+struct MatchCase
+{
+private:
+  Analysis::NodeMapping mappings;
+  MatchArm arm;
+  std::unique_ptr<Expr> expr;
+
+public:
+  MatchCase (Analysis::NodeMapping mappings, MatchArm arm,
+	     std::unique_ptr<Expr> expr)
+    : mappings (mappings), arm (std::move (arm)), expr (std::move (expr))
+  {}
+
+  MatchCase (const MatchCase &other)
+    : mappings (other.mappings), arm (other.arm),
+      expr (other.expr->clone_expr ())
+  {}
+
+  MatchCase &operator= (const MatchCase &other)
+  {
+    mappings = other.mappings;
+    arm = other.arm;
+    expr = other.expr->clone_expr ();
+
+    return *this;
+  }
+
+  MatchCase (MatchCase &&other) = default;
+  MatchCase &operator= (MatchCase &&other) = default;
+
+  ~MatchCase () = default;
+
+  std::string as_string () const;
+
+  Analysis::NodeMapping get_mappings () const { return mappings; }
+
+  MatchArm &get_arm () { return arm; }
+  std::unique_ptr<Expr> &get_expr () { return expr; }
+};
+
+// Match expression HIR node
+class MatchExpr : public ExprWithBlock
+{
+  std::unique_ptr<Expr> branch_value;
+  AST::AttrVec inner_attrs;
+  std::vector<MatchCase> match_arms;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  bool has_match_arms () const { return !match_arms.empty (); }
+
+  MatchExpr (Analysis::NodeMapping mappings, std::unique_ptr<Expr> branch_value,
+	     std::vector<MatchCase> match_arms, AST::AttrVec inner_attrs,
+	     AST::AttrVec outer_attrs, Location locus)
+    : ExprWithBlock (std::move (mappings), std::move (outer_attrs)),
+      branch_value (std::move (branch_value)),
+      inner_attrs (std::move (inner_attrs)),
+      match_arms (std::move (match_arms)), locus (locus)
+  {}
+
+  // Copy constructor requires clone due to unique_ptr
+  MatchExpr (MatchExpr const &other)
+    : ExprWithBlock (other), branch_value (other.branch_value->clone_expr ()),
+      inner_attrs (other.inner_attrs), match_arms (other.match_arms),
+      locus (other.locus)
+  {
+    /*match_arms.reserve (other.match_arms.size ());
+    for (const auto &e : other.match_arms)
+      match_arms.push_back (e->clone_match_case ());*/
+  }
+
+  // Overloaded assignment operator to clone due to unique_ptr
+  MatchExpr &operator= (MatchExpr const &other)
+  {
+    ExprWithBlock::operator= (other);
+    branch_value = other.branch_value->clone_expr ();
+    inner_attrs = other.inner_attrs;
+    match_arms = other.match_arms;
+    // outer_attrs = other.outer_attrs;
+    locus = other.locus;
+
+    /*match_arms.reserve (other.match_arms.size ());
+    for (const auto &e : other.match_arms)
+      match_arms.push_back (e->clone_match_case ());*/
+
+    return *this;
+  }
+
+  // move constructors
+  MatchExpr (MatchExpr &&other) = default;
+  MatchExpr &operator= (MatchExpr &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  std::unique_ptr<Expr> &get_scrutinee_expr ()
+  {
+    rust_assert (branch_value != nullptr);
+    return branch_value;
+  }
+
+  const std::vector<MatchCase> &get_match_cases () const { return match_arms; }
+  std::vector<MatchCase> &get_match_cases () { return match_arms; }
+
+  ExprType get_expression_type () const final override
+  {
+    return ExprType::Match;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  MatchExpr *clone_expr_impl () const override { return new MatchExpr (*this); }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  MatchExpr *clone_expr_with_block_impl () const override
+  {
+    return new MatchExpr (*this);
+  }
+};
+
+// Await expression HIR node (pseudo-member variable access)
+class AwaitExpr : public ExprWithoutBlock
+{
+  std::unique_ptr<Expr> awaited_expr;
+  Location locus;
+
+public:
+  // TODO: ensure outer attributes are actually allowed
+  AwaitExpr (Analysis::NodeMapping mappings, std::unique_ptr<Expr> awaited_expr,
+	     AST::AttrVec outer_attrs, Location locus)
+    : ExprWithoutBlock (std::move (mappings), std::move (outer_attrs)),
+      awaited_expr (std::move (awaited_expr)), locus (locus)
+  {}
+
+  // copy constructor with clone
+  AwaitExpr (AwaitExpr const &other)
+    : ExprWithoutBlock (other),
+      awaited_expr (other.awaited_expr->clone_expr ()), locus (other.locus)
+  {}
+
+  // overloaded assignment operator with clone
+  AwaitExpr &operator= (AwaitExpr const &other)
+  {
+    ExprWithoutBlock::operator= (other);
+    awaited_expr = other.awaited_expr->clone_expr ();
+    locus = other.locus;
+
+    return *this;
+  }
+
+  // move constructors
+  AwaitExpr (AwaitExpr &&other) = default;
+  AwaitExpr &operator= (AwaitExpr &&other) = default;
+
+  std::string as_string () const override;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  ExprType get_expression_type () const final override
+  {
+    return ExprType::Await;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  AwaitExpr *clone_expr_without_block_impl () const override
+  {
+    return new AwaitExpr (*this);
+  }
+};
+
+// Async block expression HIR node (block expr that evaluates to a future)
+class AsyncBlockExpr : public ExprWithBlock
+{
+  bool has_move;
+  std::unique_ptr<BlockExpr> block_expr;
+  Location locus;
+
+public:
+  AsyncBlockExpr (Analysis::NodeMapping mappings,
+		  std::unique_ptr<BlockExpr> block_expr, bool has_move,
+		  AST::AttrVec outer_attrs, Location locus)
+    : ExprWithBlock (std::move (mappings), std::move (outer_attrs)),
+      has_move (has_move), block_expr (std::move (block_expr)), locus (locus)
+  {}
+
+  // copy constructor with clone
+  AsyncBlockExpr (AsyncBlockExpr const &other)
+    : ExprWithBlock (other), has_move (other.has_move),
+      block_expr (other.block_expr->clone_block_expr ()), locus (other.locus)
+  {}
+
+  // overloaded assignment operator to clone
+  AsyncBlockExpr &operator= (AsyncBlockExpr const &other)
+  {
+    ExprWithBlock::operator= (other);
+    has_move = other.has_move;
+    block_expr = other.block_expr->clone_block_expr ();
+    locus = other.locus;
+
+    return *this;
+  }
+
+  // move constructors
+  AsyncBlockExpr (AsyncBlockExpr &&other) = default;
+  AsyncBlockExpr &operator= (AsyncBlockExpr &&other) = default;
+
+  std::string as_string () const override;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  ExprType get_expression_type () const final override
+  {
+    return ExprType::AsyncBlock;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  AsyncBlockExpr *clone_expr_with_block_impl () const override
+  {
+    return new AsyncBlockExpr (*this);
+  }
+};
+
+// this is a utility helper class for type-checking and code-generation
+class OperatorExprMeta
+{
+public:
+  OperatorExprMeta (HIR::CompoundAssignmentExpr &expr)
+    : node_mappings (expr.get_mappings ()),
+      lvalue_mappings (expr.get_expr ()->get_mappings ()),
+      locus (expr.get_locus ())
+  {}
+
+  OperatorExprMeta (HIR::ArithmeticOrLogicalExpr &expr)
+    : node_mappings (expr.get_mappings ()),
+      lvalue_mappings (expr.get_expr ()->get_mappings ()),
+      locus (expr.get_locus ())
+  {}
+
+  OperatorExprMeta (HIR::NegationExpr &expr)
+    : node_mappings (expr.get_mappings ()),
+      lvalue_mappings (expr.get_expr ()->get_mappings ()),
+      locus (expr.get_locus ())
+  {}
+
+  OperatorExprMeta (HIR::DereferenceExpr &expr)
+    : node_mappings (expr.get_mappings ()),
+      lvalue_mappings (expr.get_expr ()->get_mappings ()),
+      locus (expr.get_locus ())
+  {}
+
+  OperatorExprMeta (HIR::ArrayIndexExpr &expr)
+    : node_mappings (expr.get_mappings ()),
+      lvalue_mappings (expr.get_array_expr ()->get_mappings ()),
+      locus (expr.get_locus ())
+  {}
+
+  const Analysis::NodeMapping &get_mappings () const { return node_mappings; }
+
+  const Analysis::NodeMapping &get_lvalue_mappings () const
+  {
+    return lvalue_mappings;
+  }
+
+  Location get_locus () const { return locus; }
+
+private:
+  const Analysis::NodeMapping node_mappings;
+  const Analysis::NodeMapping lvalue_mappings;
+  Location locus;
+};
+
+} // namespace HIR
+} // namespace Rust
+
+#endif
diff --git a/gcc/rust/hir/tree/rust-hir-full-decls.h b/gcc/rust/hir/tree/rust-hir-full-decls.h
new file mode 100644
index 00000000000..2798ba9fd84
--- /dev/null
+++ b/gcc/rust/hir/tree/rust-hir-full-decls.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_HIR_FULL_DECLS_H
+#define RUST_HIR_FULL_DECLS_H
+
+namespace Rust {
+namespace HIR {
+
+struct Literal;
+class Stmt;
+class Item;
+class Expr;
+class ExprWithoutBlock;
+class Pattern;
+class Type;
+class TypeNoBounds;
+class TypeParamBound;
+class Lifetime;
+class GenericParam;
+class LifetimeParam;
+
+class TraitItem;
+class ImplItem;
+struct Crate;
+class PathExpr;
+
+// rust-path.h
+class PathIdentSegment;
+struct GenericArgsBinding;
+struct GenericArgs;
+class PathExprSegment;
+class PathPattern;
+class PathInExpression;
+class TypePathSegment;
+class TypePathSegmentGeneric;
+struct TypePathFunction;
+class TypePathSegmentFunction;
+class TypePath;
+struct QualifiedPathType;
+class QualifiedPathInExpression;
+class QualifiedPathInType;
+
+// rust-expr.h
+class ExprWithBlock;
+class LiteralExpr;
+class AttrInputLiteral;
+class OperatorExpr;
+class BorrowExpr;
+class DereferenceExpr;
+class ErrorPropagationExpr;
+class NegationExpr;
+class ArithmeticOrLogicalExpr;
+class ComparisonExpr;
+class LazyBooleanExpr;
+class TypeCastExpr;
+class AssignmentExpr;
+class CompoundAssignmentExpr;
+class GroupedExpr;
+class ArrayElems;
+class ArrayElemsValues;
+class ArrayElemsCopied;
+class ArrayExpr;
+class ArrayIndexExpr;
+class TupleExpr;
+class TupleIndexExpr;
+class StructExpr;
+class StructExprStruct;
+struct StructBase;
+class StructExprField;
+class StructExprFieldIdentifier;
+class StructExprFieldWithVal;
+class StructExprFieldIdentifierValue;
+class StructExprFieldIndexValue;
+class StructExprStructFields;
+class StructExprStructBase;
+class CallExpr;
+class MethodCallExpr;
+class FieldAccessExpr;
+struct ClosureParam;
+class ClosureExpr;
+class ClosureExprInner;
+class BlockExpr;
+class ClosureExprInnerTyped;
+class ContinueExpr;
+class BreakExpr;
+class RangeExpr;
+class RangeFromToExpr;
+class RangeFromExpr;
+class RangeToExpr;
+class RangeFullExpr;
+class RangeFromToInclExpr;
+class RangeToInclExpr;
+class ReturnExpr;
+class UnsafeBlockExpr;
+class LoopLabel;
+class BaseLoopExpr;
+class LoopExpr;
+class WhileLoopExpr;
+class WhileLetLoopExpr;
+class ForLoopExpr;
+class IfExpr;
+class IfExprConseqElse;
+class IfExprConseqIf;
+class IfLetExpr;
+class IfExprConseqIfLet;
+class IfLetExprConseqElse;
+class IfLetExprConseqIf;
+class IfLetExprConseqIfLet;
+struct MatchArm;
+// class MatchCase;
+// class MatchCaseBlockExpr;
+// class MatchCaseExpr;
+struct MatchCase;
+class MatchExpr;
+class AwaitExpr;
+class AsyncBlockExpr;
+
+// rust-stmt.h
+class EmptyStmt;
+class LetStmt;
+class ExprStmt;
+class ExprStmtWithoutBlock;
+class ExprStmtWithBlock;
+
+// rust-item.h
+class TypeParam;
+class ConstGenericParam;
+class WhereClauseItem;
+class LifetimeWhereClauseItem;
+class TypeBoundWhereClauseItem;
+struct WhereClause;
+struct SelfParam;
+struct FunctionQualifiers;
+struct FunctionParam;
+struct Visibility;
+class VisItem;
+class Module;
+class ExternCrate;
+class UseTree;
+class UseTreeGlob;
+class UseTreeList;
+class UseTreeRebind;
+class UseDeclaration;
+class Function;
+class TypeAlias;
+class Struct;
+struct StructField;
+class StructStruct;
+struct TupleField;
+class TupleStruct;
+class EnumItem;
+class EnumItemTuple;
+class EnumItemStruct;
+class EnumItemDiscriminant;
+class Enum;
+class Union;
+class ConstantItem;
+class StaticItem;
+struct TraitFunctionDecl;
+class TraitItemFunc;
+class TraitItemConst;
+class TraitItemType;
+class Trait;
+class ImplBlock;
+class ExternalItem;
+class ExternalStaticItem;
+struct NamedFunctionParam;
+class ExternalFunctionItem;
+class ExternBlock;
+
+// rust-pattern.h
+class LiteralPattern;
+class IdentifierPattern;
+class WildcardPattern;
+class RangePatternBound;
+class RangePatternBoundLiteral;
+class RangePatternBoundPath;
+class RangePatternBoundQualPath;
+class RangePattern;
+class ReferencePattern;
+struct StructPatternEtc;
+class StructPatternField;
+class StructPatternFieldTuplePat;
+class StructPatternFieldIdentPat;
+class StructPatternFieldIdent;
+class StructPattern;
+class TupleStructItems;
+class TupleStructItemsNoRange;
+class TupleStructItemsRange;
+class TupleStructPattern;
+class TuplePatternItems;
+class TuplePatternItemsMultiple;
+class TuplePatternItemsRanged;
+class TuplePattern;
+class GroupedPattern;
+class SlicePattern;
+
+// rust-type.h
+class TraitBound;
+class ImplTraitType;
+class TraitObjectType;
+class ParenthesisedType;
+class ImplTraitTypeOneBound;
+class TupleType;
+class NeverType;
+class RawPointerType;
+class ReferenceType;
+class ArrayType;
+class SliceType;
+class InferredType;
+struct MaybeNamedParam;
+class BareFunctionType;
+} // namespace HIR
+} // namespace Rust
+
+#endif
diff --git a/gcc/rust/hir/tree/rust-hir-full-test.cc b/gcc/rust/hir/tree/rust-hir-full-test.cc
new file mode 100644
index 00000000000..4e255320e2d
--- /dev/null
+++ b/gcc/rust/hir/tree/rust-hir-full-test.cc
@@ -0,0 +1,5292 @@
+// 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/>.
+
+#include "rust-ast-full.h"
+#include "rust-hir-full.h"
+#include "rust-hir-visitor.h"
+#include "rust-diagnostics.h"
+
+/* Compilation unit used for various HIR-related functions that would make
+ * the headers too long if they were defined inline and don't receive any
+ * benefits from being defined inline because they are virtual. Also used
+ * for various other stuff. */
+
+namespace Rust {
+namespace HIR {
+
+enum indent_mode
+{
+  enter,
+  out,
+  stay
+};
+
+std::string
+indent_spaces (enum indent_mode mode)
+{
+  static int indent = 0;
+  std::string str = "";
+  if (out == mode)
+    indent--;
+  for (int i = 0; i < indent; i++)
+    str += " ";
+  if (enter == mode)
+    indent++;
+
+  return str;
+}
+
+// Gets a string in a certain delim type.
+std::string
+get_string_in_delims (std::string str_input, AST::DelimType delim_type)
+{
+  switch (delim_type)
+    {
+    case AST::DelimType::PARENS:
+      return "(" + str_input + ")";
+    case AST::DelimType::SQUARE:
+      return "[" + str_input + "]";
+    case AST::DelimType::CURLY:
+      return "{" + str_input + "}";
+    default:
+      return "ERROR-MARK-STRING (delims)";
+    }
+  gcc_unreachable ();
+}
+
+std::string
+Crate::as_string () const
+{
+  std::string str ("HIR::Crate: ");
+
+  // inner attributes
+  str += "\n inner attributes: ";
+  if (inner_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "inner attribute" syntax -
+       * just the body */
+      for (const auto &attr : inner_attrs)
+	{
+	  str += "\n  " + attr.as_string ();
+	}
+    }
+
+  // items
+  str += "\n items: ";
+  if (items.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &item : items)
+	{
+	  // DEBUG: null pointer check
+	  if (item == nullptr)
+	    {
+	      rust_debug ("something really terrible has gone wrong - "
+			  "null pointer item in crate.");
+	      return "nullptr_POINTER_MARK";
+	    }
+
+	  str += "\n  " + item->as_string ();
+	}
+    }
+
+  return str + "\n::" + get_mappings ().as_string () + "\n";
+}
+
+std::string
+Visibility::as_string () const
+{
+  switch (vis_type)
+    {
+    case PRIVATE:
+      return std::string ("private");
+    case PUBLIC:
+      return std::string ("pub");
+    case RESTRICTED:
+      return std::string ("pub(in ") + path.get_mappings ().as_string ()
+	     + std::string (")");
+    default:
+      gcc_unreachable ();
+    }
+}
+
+// Creates a string that reflects the visibility stored.
+std::string
+VisItem::as_string () const
+{
+  // FIXME: can't do formatting on string to make identation occur.
+  std::string str = Item::as_string ();
+
+  if (has_visibility ())
+    {
+      str = visibility.as_string () + " ";
+    }
+
+  return str;
+}
+
+// Creates a string that reflects the outer attributes stored.
+std::string
+Item::as_string () const
+{
+  std::string str;
+
+  if (!outer_attrs.empty ())
+    {
+      for (const auto &attr : outer_attrs)
+	{
+	  str += attr.as_string () + "\n";
+	}
+    }
+
+  return str;
+}
+
+std::string
+Module::as_string () const
+{
+  // get module string for "[vis] mod [name]"
+  std::string str = VisItem::as_string () + "mod " + module_name;
+
+  // inner attributes
+  str += "\n inner attributes: ";
+  if (inner_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "inner attribute" syntax -
+       * just the body */
+      for (const auto &attr : inner_attrs)
+	{
+	  str += "\n  " + attr.as_string ();
+	}
+    }
+
+  // items
+  str += "\n items: ";
+  if (items.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &item : items)
+	{
+	  // DEBUG: null pointer check
+	  if (item == nullptr)
+	    {
+	      rust_debug ("something really terrible has gone wrong - "
+			  "null pointer item in crate.");
+	      return "nullptr_POINTER_MARK";
+	    }
+
+	  str += "\n  " + item->as_string ();
+	}
+    }
+
+  return str + "\n";
+}
+
+std::string
+StaticItem::as_string () const
+{
+  std::string str = VisItem::as_string ();
+
+  str += indent_spaces (stay) + "static";
+
+  if (is_mut ())
+    {
+      str += " mut";
+    }
+
+  str += name;
+
+  // DEBUG: null pointer check
+  if (type == nullptr)
+    {
+      rust_debug ("something really terrible has gone wrong - null "
+		  "pointer type in static item.");
+      return "nullptr_POINTER_MARK";
+    }
+  str += "\n" + indent_spaces (stay) + "Type: " + type->as_string ();
+
+  // DEBUG: null pointer check
+  if (expr == nullptr)
+    {
+      rust_debug ("something really terrible has gone wrong - null "
+		  "pointer expr in static item.");
+      return "nullptr_POINTER_MARK";
+    }
+  str += "\n" + indent_spaces (stay) + "Expression: " + expr->as_string ();
+
+  return str + "\n";
+}
+
+std::string
+ExternCrate::as_string () const
+{
+  std::string str = VisItem::as_string ();
+
+  str += "extern crate " + referenced_crate;
+
+  if (has_as_clause ())
+    {
+      str += " as " + as_clause_name;
+    }
+
+  return str;
+}
+
+std::string
+TupleStruct::as_string () const
+{
+  std::string str = VisItem::as_string ();
+
+  str += "struct " + struct_name;
+
+  // generic params
+  str += "\n Generic params: ";
+  if (generic_params.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : generic_params)
+	{
+	  // DEBUG: null pointer check
+	  if (param == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"generic param in enum.");
+	      return "nullptr_POINTER_MARK";
+	    }
+
+	  str += "\n  " + param->as_string ();
+	}
+    }
+
+  // tuple fields
+  str += "\n Tuple fields: ";
+  if (fields.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &field : fields)
+	{
+	  str += "\n  " + field.as_string ();
+	}
+    }
+
+  str += "\n Where clause: ";
+  if (has_where_clause ())
+    {
+      str += where_clause.as_string ();
+    }
+  else
+    {
+      str += "none";
+    }
+
+  return str;
+}
+
+std::string
+ConstantItem::as_string () const
+{
+  std::string str = VisItem::as_string ();
+
+  str += "const " + identifier;
+
+  // DEBUG: null pointer check
+  if (type == nullptr)
+    {
+      rust_debug ("something really terrible has gone wrong - null "
+		  "pointer type in const item.");
+      return "nullptr_POINTER_MARK";
+    }
+  str += "\n  Type: " + type->as_string ();
+
+  // DEBUG: null pointer check
+  if (const_expr == nullptr)
+    {
+      rust_debug ("something really terrible has gone wrong - null "
+		  "pointer expr in const item.");
+      return "nullptr_POINTER_MARK";
+    }
+  str += "\n  Expression: " + const_expr->as_string ();
+
+  return str + "\n";
+}
+
+std::string
+ImplBlock::as_string () const
+{
+  std::string str = VisItem::as_string ();
+
+  str += "impl ";
+
+  // generic params
+  str += "\n Generic params: ";
+  if (generic_params.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : generic_params)
+	{
+	  // DEBUG: null pointer check
+	  if (param == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"generic param in impl.");
+	      return "nullptr_POINTER_MARK";
+	    }
+
+	  str += "\n  " + param->as_string ();
+	}
+    }
+
+  str += "\n Type: " + impl_type->as_string ();
+
+  str += "\n Where clause: ";
+  if (has_where_clause ())
+    {
+      str += where_clause.as_string ();
+    }
+  else
+    {
+      str += "none";
+    }
+
+  // inner attributes
+  str += "\n inner attributes: ";
+  if (inner_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "inner attribute" syntax -
+       * just the body */
+      for (const auto &attr : inner_attrs)
+	{
+	  str += "\n  " + attr.as_string ();
+	}
+    }
+
+  str += "\n impl items: ";
+  if (!has_impl_items ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &item : impl_items)
+	{
+	  str += "\n  " + item->as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+StructStruct::as_string () const
+{
+  std::string str = VisItem::as_string ();
+
+  str += "struct " + struct_name;
+
+  // generic params
+  str += "\n Generic params: ";
+  if (generic_params.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : generic_params)
+	{
+	  // DEBUG: null pointer check
+	  if (param == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"generic param in enum.");
+	      return "nullptr_POINTER_MARK";
+	    }
+
+	  str += "\n  " + param->as_string ();
+	}
+    }
+
+  str += "\n Where clause: ";
+  if (has_where_clause ())
+    {
+      str += where_clause.as_string ();
+    }
+  else
+    {
+      str += "none";
+    }
+
+  // struct fields
+  str += "\n Struct fields: ";
+  if (is_unit)
+    {
+      str += "none (unit)";
+    }
+  else if (fields.empty ())
+    {
+      str += "none (non-unit)";
+    }
+  else
+    {
+      for (const auto &field : fields)
+	{
+	  str += "\n  - " + field.as_string ();
+	}
+      str += "\n";
+    }
+
+  return str + "::" + get_mappings ().as_string () + "\n";
+}
+
+std::string
+UseDeclaration::as_string () const
+{
+  std::string str = VisItem::as_string ();
+
+  // DEBUG: null pointer check
+  if (use_tree == nullptr)
+    {
+      rust_debug (
+	"something really terrible has gone wrong - null pointer use tree in "
+	"use declaration.");
+      return "nullptr_POINTER_MARK";
+    }
+
+  str += "use " + use_tree->as_string ();
+
+  return str;
+}
+
+std::string
+UseTreeGlob::as_string () const
+{
+  switch (glob_type)
+    {
+    case NO_PATH:
+      return "*";
+    case GLOBAL:
+      return "::*";
+      case PATH_PREFIXED: {
+	std::string path_str = path.as_string ();
+	return path_str + "::*";
+      }
+    default:
+      // some kind of error
+      return "ERROR-PATH";
+    }
+  gcc_unreachable ();
+}
+
+std::string
+UseTreeList::as_string () const
+{
+  std::string path_str;
+  switch (path_type)
+    {
+    case NO_PATH:
+      path_str = "{";
+      break;
+    case GLOBAL:
+      path_str = "::{";
+      break;
+      case PATH_PREFIXED: {
+	path_str = path.as_string () + "::{";
+	break;
+      }
+    default:
+      // some kind of error
+      return "ERROR-PATH-LIST";
+    }
+
+  if (has_trees ())
+    {
+      auto i = trees.begin ();
+      auto e = trees.end ();
+
+      // DEBUG: null pointer check
+      if (*i == nullptr)
+	{
+	  rust_debug ("something really terrible has gone wrong - null pointer "
+		      "tree in use tree list.");
+	  return "nullptr_POINTER_MARK";
+	}
+
+      for (; i != e; i++)
+	{
+	  path_str += (*i)->as_string ();
+	  if (e != i + 1)
+	    path_str += ", ";
+	}
+    }
+  else
+    {
+      path_str += "none";
+    }
+
+  return path_str + "}";
+}
+
+std::string
+UseTreeRebind::as_string () const
+{
+  std::string path_str = path.as_string ();
+
+  switch (bind_type)
+    {
+    case NONE:
+      // nothing to add, just path
+      break;
+    case IDENTIFIER:
+      path_str += " as " + identifier;
+      break;
+    case WILDCARD:
+      path_str += " as _";
+      break;
+    default:
+      // error
+      return "ERROR-PATH-REBIND";
+    }
+
+  return path_str;
+}
+
+std::string
+Enum::as_string () const
+{
+  std::string str = VisItem::as_string ();
+  str += enum_name;
+
+  // generic params
+  str += "\n Generic params: ";
+  if (generic_params.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : generic_params)
+	{
+	  // DEBUG: null pointer check
+	  if (param == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"generic param in enum.");
+	      return "nullptr_POINTER_MARK";
+	    }
+
+	  str += "\n  " + param->as_string ();
+	}
+    }
+
+  str += "\n Where clause: ";
+  if (has_where_clause ())
+    {
+      str += where_clause.as_string ();
+    }
+  else
+    {
+      str += "none";
+    }
+
+  // items
+  str += "\n Items: ";
+  if (items.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &item : items)
+	{
+	  // DEBUG: null pointer check
+	  if (item == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"enum item in enum.");
+	      return "nullptr_POINTER_MARK";
+	    }
+
+	  str += "\n  " + item->as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+Trait::as_string () const
+{
+  std::string str = VisItem::as_string ();
+
+  if (unsafety == Unsafety::Unsafe)
+    {
+      str += "unsafe ";
+    }
+
+  str += "trait " + name;
+
+  // generic params
+  str += "\n Generic params: ";
+  if (generic_params.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : generic_params)
+	{
+	  // DEBUG: null pointer check
+	  if (param == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"generic param in trait.");
+	      return "nullptr_POINTER_MARK";
+	    }
+
+	  str += "\n  " + param->as_string ();
+	}
+    }
+
+  str += "\n Type param bounds: ";
+  if (!has_type_param_bounds ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &bound : type_param_bounds)
+	{
+	  // DEBUG: null pointer check
+	  if (bound == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"type param bound in trait.");
+	      return "nullptr_POINTER_MARK";
+	    }
+
+	  str += "\n  " + bound->as_string ();
+	}
+    }
+
+  str += "\n Where clause: ";
+  if (!has_where_clause ())
+    {
+      str += "none";
+    }
+  else
+    {
+      str += where_clause.as_string ();
+    }
+
+  str += "\n Trait items: ";
+  if (!has_trait_items ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &item : trait_items)
+	{
+	  // DEBUG: null pointer check
+	  if (item == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"trait item in trait.");
+	      return "nullptr_POINTER_MARK";
+	    }
+
+	  str += "\n  " + item->as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+Union::as_string () const
+{
+  std::string str = VisItem::as_string ();
+
+  str += "union " + union_name;
+
+  // generic params
+  str += "\n Generic params: ";
+  if (generic_params.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : generic_params)
+	{
+	  // DEBUG: null pointer check
+	  if (param == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"generic param in union.");
+	      return "nullptr_POINTER_MARK";
+	    }
+
+	  str += "\n  " + param->as_string ();
+	}
+    }
+
+  str += "\n Where clause: ";
+  if (has_where_clause ())
+    {
+      str += where_clause.as_string ();
+    }
+  else
+    {
+      str += "none";
+    }
+
+  // struct fields
+  str += "\n Struct fields (variants): ";
+  if (variants.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &field : variants)
+	{
+	  str += "\n  " + field.as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+Function::as_string () const
+{
+  std::string str = VisItem::as_string () + "\n";
+  std::string qstr = qualifiers.as_string ();
+  if ("" != qstr)
+    str += qstr + " ";
+
+  if (has_function_return_type ())
+    {
+      // DEBUG: null pointer check
+      if (return_type == nullptr)
+	{
+	  rust_debug (
+	    "something really terrible has gone wrong - null pointer return "
+	    "type in function.");
+	  return "nullptr_POINTER_MARK";
+	}
+
+      str += return_type->as_string () + " ";
+    }
+  else
+    {
+      str += "void ";
+    }
+
+  str += function_name;
+
+  if (has_generics ())
+    {
+      str += "<";
+
+      auto i = generic_params.begin ();
+      auto e = generic_params.end ();
+
+      // DEBUG: null pointer check
+      if (i == e)
+	{
+	  rust_debug ("something really terrible has gone wrong - null pointer "
+		      "generic param in function item.");
+	  return "nullptr_POINTER_MARK";
+	}
+
+      for (; i != e; i++)
+	{
+	  str += (*i)->as_string ();
+	  if (e != i + 1)
+	    str += ", ";
+	}
+      str += ">";
+    }
+
+  if (has_function_params ())
+    {
+      auto i = function_params.begin ();
+      auto e = function_params.end ();
+      str += "(";
+      for (; i != e; i++)
+	{
+	  str += (*i).as_string ();
+	  if (e != i + 1)
+	    str += ", ";
+	}
+      str += ")";
+    }
+  else
+    {
+      str += "()";
+    }
+
+  if (has_where_clause ())
+    {
+      str += " where " + where_clause.as_string ();
+    }
+
+  str += "\n";
+
+  // DEBUG: null pointer check
+  if (function_body == nullptr)
+    {
+      rust_debug (
+	"something really terrible has gone wrong - null pointer function "
+	"body in function.");
+      return "nullptr_POINTER_MARK";
+    }
+  return str + function_body->as_string () + "::" + get_mappings ().as_string ()
+	 + "\n";
+}
+
+std::string
+WhereClause::as_string () const
+{
+  // just print where clause items, don't mention "where" or "where clause"
+  std::string str;
+
+  if (where_clause_items.empty ())
+    {
+      str = "none";
+    }
+  else
+    {
+      for (const auto &item : where_clause_items)
+	{
+	  str += "\n  " + item->as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+BlockExpr::as_string () const
+{
+  std::string istr = indent_spaces (enter);
+  std::string str = istr + "BlockExpr:\n" + istr;
+  // get outer attributes
+  str += "{\n" + indent_spaces (stay) + Expr::as_string ();
+
+  // inner attributes
+  str += "\n" + indent_spaces (stay) + "inner attributes: ";
+  if (inner_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "inner attribute" syntax -
+       * just the body */
+      for (const auto &attr : inner_attrs)
+	{
+	  str += "\n" + indent_spaces (stay) + attr.as_string ();
+	}
+    }
+
+  // statements
+  str += "\n" + indent_spaces (stay) + "statements: ";
+  if (statements.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &stmt : statements)
+	{
+	  // DEBUG: null pointer check
+	  if (stmt == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"stmt in block expr.");
+	      return "nullptr_POINTER_MARK";
+	    }
+
+	  str += "\n" + indent_spaces (stay) + stmt->as_string ();
+	}
+    }
+
+  // final expression
+  str += "\n" + indent_spaces (stay) + "final expression: ";
+  if (expr == nullptr)
+    {
+      str += "none";
+    }
+  else
+    {
+      str += "\n" + expr->as_string ();
+    }
+
+  str += "\n" + indent_spaces (out) + "}";
+  return str;
+}
+
+std::string
+TypeAlias::as_string () const
+{
+  std::string str = VisItem::as_string ();
+
+  str += " " + new_type_name;
+
+  // generic params
+  str += "\n Generic params: ";
+  if (!has_generics ())
+    {
+      str += "none";
+    }
+  else
+    {
+      auto i = generic_params.begin ();
+      auto e = generic_params.end ();
+
+      for (; i != e; i++)
+	{
+	  str += (*i)->as_string ();
+	  if (e != i + 1)
+	    str += ", ";
+	}
+    }
+
+  str += "\n Where clause: ";
+  if (!has_where_clause ())
+    {
+      str += "none";
+    }
+  else
+    {
+      str += where_clause.as_string ();
+    }
+
+  str += "\n Type: " + existing_type->as_string ();
+
+  return str;
+}
+
+std::string
+ExternBlock::as_string () const
+{
+  std::string str = VisItem::as_string ();
+
+  str += "extern ";
+  str += "\"" + get_string_from_abi (abi) + "\" ";
+
+  // inner attributes
+  str += "\n inner attributes: ";
+  if (inner_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "inner attribute" syntax -
+       * just the body */
+      for (const auto &attr : inner_attrs)
+	{
+	  str += "\n  " + attr.as_string ();
+	}
+    }
+
+  str += "\n external items: ";
+  if (!has_extern_items ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &item : extern_items)
+	{
+	  str += "\n  " + item->as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+PathInExpression::as_string () const
+{
+  std::string str;
+
+  if (has_opening_scope_resolution)
+    {
+      str = "::";
+    }
+
+  return str + PathPattern::as_string () + "::" + get_mappings ().as_string ();
+}
+
+std::string
+ExprStmtWithBlock::as_string () const
+{
+  std::string str = indent_spaces (enter) + "ExprStmtWithBlock: \n";
+
+  if (expr == nullptr)
+    {
+      str += "none (this should not happen and is an error)";
+    }
+  else
+    {
+      indent_spaces (enter);
+      str += expr->as_string ();
+      indent_spaces (out);
+    }
+
+  indent_spaces (out);
+  return str;
+}
+
+std::string
+ClosureParam::as_string () const
+{
+  std::string str (pattern->as_string ());
+
+  if (has_type_given ())
+    {
+      str += " : " + type->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+ClosureExpr::as_string () const
+{
+  std::string str ("ClosureExpr:\n Has move: ");
+  if (has_move)
+    {
+      str += "true";
+    }
+  else
+    {
+      str += "false";
+    }
+
+  str += "\n Params: ";
+  if (params.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : params)
+	{
+	  str += "\n  " + param.as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+ClosureExprInnerTyped::as_string () const
+{
+  std::string str = ClosureExpr::as_string ();
+
+  str += "\n Return type: " + return_type->as_string ();
+
+  str += "\n Body: " + expr->as_string ();
+
+  return str;
+}
+
+std::string
+PathPattern::as_string () const
+{
+  std::string str;
+
+  for (const auto &segment : segments)
+    {
+      str += segment.as_string () + "::";
+    }
+
+  // basically a hack - remove last two characters of string (remove final ::)
+  str.erase (str.length () - 2);
+
+  return str;
+}
+
+std::string
+QualifiedPathType::as_string () const
+{
+  std::string str ("<");
+  str += type->as_string ();
+
+  if (has_as_clause ())
+    {
+      str += " as " + trait->as_string ();
+    }
+
+  return str + ">";
+}
+
+std::string
+QualifiedPathInExpression::as_string () const
+{
+  return path_type.as_string () + "::" + PathPattern::as_string ();
+}
+
+std::string
+BorrowExpr::as_string () const
+{
+  std::string str ("&");
+
+  if (double_borrow)
+    {
+      str += "&";
+    }
+
+  if (is_mut ())
+    {
+      str += "mut ";
+    }
+
+  str += main_or_left_expr->as_string ();
+
+  return str;
+}
+
+std::string
+ReturnExpr::as_string () const
+{
+  std::string str ("return ");
+
+  if (has_return_expr ())
+    {
+      str += return_expr->as_string ();
+    }
+
+  return str + "::" + get_mappings ().as_string ();
+}
+
+std::string
+GroupedExpr::as_string () const
+{
+  std::string str ("Grouped expr:");
+
+  // inner attributes
+  str += "\n inner attributes: ";
+  if (inner_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "inner attribute" syntax -
+       * just the body */
+      for (const auto &attr : inner_attrs)
+	{
+	  str += "\n  " + attr.as_string ();
+	}
+    }
+
+  str += "\n Expr in parens: " + expr_in_parens->as_string ();
+
+  return str;
+}
+
+std::string
+RangeToExpr::as_string () const
+{
+  return ".." + to->as_string ();
+}
+
+std::string
+ContinueExpr::as_string () const
+{
+  std::string str ("continue ");
+
+  if (has_label ())
+    {
+      str += label.as_string ();
+    }
+
+  return str;
+}
+
+std::string
+NegationExpr::as_string () const
+{
+  std::string str;
+
+  switch (expr_type)
+    {
+    case NegationOperator::NEGATE:
+      str = "-";
+      break;
+    case NegationOperator::NOT:
+      str = "!";
+      break;
+    default:
+      return "ERROR_MARK_STRING - negation expr";
+    }
+
+  str += main_or_left_expr->as_string ();
+
+  return str;
+}
+
+std::string
+RangeFromExpr::as_string () const
+{
+  return from->as_string () + "..";
+}
+
+std::string
+RangeFullExpr::as_string () const
+{
+  return "..";
+}
+
+std::string
+ArrayIndexExpr::as_string () const
+{
+  return array_expr->as_string () + "[" + index_expr->as_string () + "]";
+}
+
+std::string
+AssignmentExpr::as_string () const
+{
+  return main_or_left_expr->as_string () + " = " + right_expr->as_string ()
+	 + "::" + get_mappings ().as_string ();
+}
+
+std::string
+CompoundAssignmentExpr::as_string () const
+{
+  std::string operator_str;
+  operator_str.reserve (1);
+
+  // get operator string
+  switch (expr_type)
+    {
+    case ArithmeticOrLogicalOperator::ADD:
+      operator_str = "+";
+      break;
+    case ArithmeticOrLogicalOperator::SUBTRACT:
+      operator_str = "-";
+      break;
+    case ArithmeticOrLogicalOperator::MULTIPLY:
+      operator_str = "*";
+      break;
+    case ArithmeticOrLogicalOperator::DIVIDE:
+      operator_str = "/";
+      break;
+    case ArithmeticOrLogicalOperator::MODULUS:
+      operator_str = "%";
+      break;
+    case ArithmeticOrLogicalOperator::BITWISE_AND:
+      operator_str = "&";
+      break;
+    case ArithmeticOrLogicalOperator::BITWISE_OR:
+      operator_str = "|";
+      break;
+    case ArithmeticOrLogicalOperator::BITWISE_XOR:
+      operator_str = "^";
+      break;
+    case ArithmeticOrLogicalOperator::LEFT_SHIFT:
+      operator_str = "<<";
+      break;
+    case ArithmeticOrLogicalOperator::RIGHT_SHIFT:
+      operator_str = ">>";
+      break;
+    default:
+      gcc_unreachable ();
+      break;
+    }
+
+  operator_str += "=";
+
+  std::string str ("CompoundAssignmentExpr: ");
+  if (main_or_left_expr == nullptr || right_expr == nullptr)
+    {
+      str += "error. this is probably a parsing failure.";
+    }
+  else
+    {
+      str += "\n left: " + main_or_left_expr->as_string ();
+      str += "\n right: " + right_expr->as_string ();
+      str += "\n operator: " + operator_str;
+    }
+
+  return str;
+}
+
+std::string
+AsyncBlockExpr::as_string () const
+{
+  std::string str = "AsyncBlockExpr: ";
+
+  // get outer attributes
+  str += "\n " + Expr::as_string ();
+
+  str += "\n Has move: ";
+  str += has_move ? "true" : "false";
+
+  return str + "\n" + block_expr->as_string ();
+}
+
+std::string
+ComparisonExpr::as_string () const
+{
+  std::string str (main_or_left_expr->as_string ());
+
+  switch (expr_type)
+    {
+    case ComparisonOperator::EQUAL:
+      str += " == ";
+      break;
+    case ComparisonOperator::NOT_EQUAL:
+      str += " != ";
+      break;
+    case ComparisonOperator::GREATER_THAN:
+      str += " > ";
+      break;
+    case ComparisonOperator::LESS_THAN:
+      str += " < ";
+      break;
+    case ComparisonOperator::GREATER_OR_EQUAL:
+      str += " >= ";
+      break;
+    case ComparisonOperator::LESS_OR_EQUAL:
+      str += " <= ";
+      break;
+    default:
+      return "ERROR_MARK_STRING - comparison expr";
+    }
+
+  str += right_expr->as_string ();
+
+  return str;
+}
+
+std::string
+MethodCallExpr::as_string () const
+{
+  std::string str ("MethodCallExpr: \n Object (receiver) expr: ");
+
+  str += receiver->as_string ();
+
+  str += "\n Method path segment: \n";
+
+  str += method_name.as_string ();
+
+  str += "\n Call params:";
+  if (params.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : params)
+	{
+	  if (param == nullptr)
+	    {
+	      return "ERROR_MARK_STRING - method call expr param is null";
+	    }
+
+	  str += "\n  " + param->as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+TupleIndexExpr::as_string () const
+{
+  return tuple_expr->as_string () + "." + std::to_string (tuple_index);
+}
+
+std::string
+DereferenceExpr::as_string () const
+{
+  return "*" + main_or_left_expr->as_string ();
+}
+
+std::string
+FieldAccessExpr::as_string () const
+{
+  return receiver->as_string () + "." + field;
+}
+
+std::string
+LazyBooleanExpr::as_string () const
+{
+  std::string str (main_or_left_expr->as_string ());
+
+  switch (expr_type)
+    {
+    case LazyBooleanOperator::LOGICAL_OR:
+      str += " || ";
+      break;
+    case LazyBooleanOperator::LOGICAL_AND:
+      str += " && ";
+      break;
+    default:
+      return "ERROR_MARK_STRING - lazy boolean expr out of bounds";
+    }
+
+  str += right_expr->as_string ();
+
+  return str;
+}
+
+std::string
+RangeFromToExpr::as_string () const
+{
+  return from->as_string () + ".." + to->as_string ();
+}
+
+std::string
+RangeToInclExpr::as_string () const
+{
+  return "..=" + to->as_string ();
+}
+
+std::string
+UnsafeBlockExpr::as_string () const
+{
+  std::string istr = indent_spaces (enter);
+  std::string str = istr + "UnsafeBlockExpr:";
+  str += istr + "{";
+
+  // get outer attributes
+  str += "\n" + indent_spaces (stay) + Expr::as_string ();
+
+  return str + "\n" + indent_spaces (out) + "}\n" + expr->as_string ();
+}
+
+std::string
+ClosureExprInner::as_string () const
+{
+  std::string str = ClosureExpr::as_string ();
+
+  str += "\n Expression: " + closure_inner->as_string ();
+
+  return str;
+}
+
+std::string
+IfExpr::as_string () const
+{
+  std::string str ("IfExpr: ");
+
+  str += "\n Condition expr: " + condition->as_string ();
+
+  str += "\n If block expr: " + if_block->as_string ();
+
+  return str;
+}
+
+std::string
+IfExprConseqElse::as_string () const
+{
+  std::string str = IfExpr::as_string ();
+
+  str += "\n Else block expr: " + else_block->as_string ();
+
+  return str;
+}
+
+std::string
+IfExprConseqIf::as_string () const
+{
+  std::string str = IfExpr::as_string ();
+
+  str += "\n Else if expr: \n  " + conseq_if_expr->as_string ();
+
+  return str;
+}
+
+std::string
+IfExprConseqIfLet::as_string () const
+{
+  std::string str = IfExpr::as_string ();
+
+  str += "\n Else if let expr: \n  " + if_let_expr->as_string ();
+
+  return str;
+}
+
+std::string
+IfLetExpr::as_string () const
+{
+  std::string str ("IfLetExpr: ");
+
+  str += "\n Condition match arm patterns: ";
+  if (match_arm_patterns.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &pattern : match_arm_patterns)
+	{
+	  str += "\n  " + pattern->as_string ();
+	}
+    }
+
+  str += "\n Scrutinee expr: " + value->as_string ();
+
+  str += "\n If let block expr: " + if_block->as_string ();
+
+  return str;
+}
+
+std::string
+IfLetExprConseqElse::as_string () const
+{
+  std::string str = IfLetExpr::as_string ();
+
+  str += "\n Else block expr: " + else_block->as_string ();
+
+  return str;
+}
+
+std::string
+IfLetExprConseqIf::as_string () const
+{
+  std::string str = IfLetExpr::as_string ();
+
+  str += "\n Else if expr: \n  " + if_expr->as_string ();
+
+  return str;
+}
+
+std::string
+IfLetExprConseqIfLet::as_string () const
+{
+  std::string str = IfLetExpr::as_string ();
+
+  str += "\n Else if let expr: \n  " + if_let_expr->as_string ();
+
+  return str;
+}
+
+std::string
+RangeFromToInclExpr::as_string () const
+{
+  return from->as_string () + "..=" + to->as_string ();
+}
+
+std::string
+ErrorPropagationExpr::as_string () const
+{
+  return main_or_left_expr->as_string () + "?";
+}
+
+std::string
+ArithmeticOrLogicalExpr::as_string () const
+{
+  std::string operator_str;
+  operator_str.reserve (1);
+
+  // get operator string
+  switch (expr_type)
+    {
+    case ArithmeticOrLogicalOperator::ADD:
+      operator_str = "+";
+      break;
+    case ArithmeticOrLogicalOperator::SUBTRACT:
+      operator_str = "-";
+      break;
+    case ArithmeticOrLogicalOperator::MULTIPLY:
+      operator_str = "*";
+      break;
+    case ArithmeticOrLogicalOperator::DIVIDE:
+      operator_str = "/";
+      break;
+    case ArithmeticOrLogicalOperator::MODULUS:
+      operator_str = "%";
+      break;
+    case ArithmeticOrLogicalOperator::BITWISE_AND:
+      operator_str = "&";
+      break;
+    case ArithmeticOrLogicalOperator::BITWISE_OR:
+      operator_str = "|";
+      break;
+    case ArithmeticOrLogicalOperator::BITWISE_XOR:
+      operator_str = "^";
+      break;
+    case ArithmeticOrLogicalOperator::LEFT_SHIFT:
+      operator_str = "<<";
+      break;
+    case ArithmeticOrLogicalOperator::RIGHT_SHIFT:
+      operator_str = ">>";
+      break;
+    default:
+      gcc_unreachable ();
+      break;
+    }
+
+  std::string str = main_or_left_expr->as_string () + " ";
+  str += operator_str + " ";
+  str += right_expr->as_string ();
+
+  return "( " + str + " (" + get_mappings ().as_string () + "))";
+}
+
+std::string
+CallExpr::as_string () const
+{
+  std::string str = function->as_string () + "(";
+  if (!has_params ())
+    str += "none";
+  else
+    {
+      for (const auto &param : params)
+	{
+	  if (param == nullptr)
+	    {
+	      return "ERROR_MARK_STRING - call expr param is null";
+	    }
+
+	  str += param->as_string () + ",";
+	}
+    }
+  return str + ")" + "::" + get_mappings ().as_string ();
+}
+
+std::string
+WhileLoopExpr::as_string () const
+{
+  std::string str ("WhileLoopExpr: ");
+
+  str += "\n Label: ";
+  if (!has_loop_label ())
+    {
+      str += "none";
+    }
+  else
+    {
+      str += loop_label.as_string ();
+    }
+
+  str += "\n Conditional expr: " + condition->as_string ();
+
+  str += "\n Loop block: " + loop_block->as_string ();
+
+  return str;
+}
+
+std::string
+WhileLetLoopExpr::as_string () const
+{
+  std::string str ("WhileLetLoopExpr: ");
+
+  str += "\n Label: ";
+  if (!has_loop_label ())
+    {
+      str += "none";
+    }
+  else
+    {
+      str += loop_label.as_string ();
+    }
+
+  str += "\n Match arm patterns: ";
+  if (match_arm_patterns.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &pattern : match_arm_patterns)
+	{
+	  str += "\n  " + pattern->as_string ();
+	}
+    }
+
+  str += "\n Scrutinee expr: " + condition->as_string ();
+
+  str += "\n Loop block: " + loop_block->as_string ();
+
+  return str;
+}
+
+std::string
+LoopExpr::as_string () const
+{
+  std::string str ("LoopExpr: (infinite loop)");
+
+  str += "\n Label: ";
+  if (!has_loop_label ())
+    {
+      str += "none";
+    }
+  else
+    {
+      str += loop_label.as_string ();
+    }
+
+  str += "\n Loop block: " + loop_block->as_string ();
+
+  return str;
+}
+
+std::string
+ArrayExpr::as_string () const
+{
+  std::string str ("ArrayExpr:");
+
+  // inner attributes
+  str += "\n inner attributes: ";
+  if (inner_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "inner attribute" syntax -
+       * just the body */
+      for (const auto &attr : inner_attrs)
+	{
+	  str += "\n  " + attr.as_string ();
+	}
+    }
+
+  str += "\n Array elems: ";
+  if (!has_array_elems ())
+    {
+      str += "none";
+    }
+  else
+    {
+      str += internal_elements->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+AwaitExpr::as_string () const
+{
+  return awaited_expr->as_string () + ".await";
+}
+
+std::string
+BreakExpr::as_string () const
+{
+  std::string str ("break ");
+
+  if (has_label ())
+    {
+      str += label.as_string () + " ";
+    }
+
+  if (has_break_expr ())
+    {
+      str += break_expr->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+LoopLabel::as_string () const
+{
+  return label.as_string () + ": (label) ";
+}
+
+std::string
+MatchArm::as_string () const
+{
+  // outer attributes
+  std::string str = "Outer attributes: ";
+  if (outer_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "outer attribute" syntax -
+       * just the body */
+      for (const auto &attr : outer_attrs)
+	{
+	  str += "\n " + attr.as_string ();
+	}
+    }
+
+  str += "\nPatterns: ";
+  if (match_arm_patterns.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &pattern : match_arm_patterns)
+	{
+	  str += "\n " + pattern->as_string ();
+	}
+    }
+
+  str += "\nGuard expr: ";
+  if (!has_match_arm_guard ())
+    {
+      str += "none";
+    }
+  else
+    {
+      str += guard_expr->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+MatchCase::as_string () const
+{
+  std::string str ("MatchCase: (match arm) ");
+
+  str += "\n Match arm matcher: \n" + arm.as_string ();
+  str += "\n Expr: " + expr->as_string ();
+
+  return str;
+}
+
+/*std::string
+MatchCaseBlockExpr::as_string () const
+{
+  std::string str = MatchCase::as_string ();
+
+  str += "\n Block expr: " + block_expr->as_string ();
+
+  return str;
+}
+
+std::string
+MatchCaseExpr::as_string () const
+{
+  std::string str = MatchCase::as_string ();
+
+  str += "\n Expr: " + expr->as_string ();
+
+  return str;
+}*/
+
+std::string
+MatchExpr::as_string () const
+{
+  std::string str ("MatchExpr:");
+
+  str += "\n Scrutinee expr: " + branch_value->as_string ();
+
+  // inner attributes
+  str += "\n inner attributes: ";
+  if (inner_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "inner attribute" syntax -
+       * just the body */
+      for (const auto &attr : inner_attrs)
+	{
+	  str += "\n  " + attr.as_string ();
+	}
+    }
+
+  // match arms
+  str += "\n Match arms: ";
+  if (match_arms.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &arm : match_arms)
+	str += "\n  " + arm.as_string ();
+    }
+
+  return str;
+}
+
+std::string
+TupleExpr::as_string () const
+{
+  std::string str ("TupleExpr:");
+
+  // inner attributes
+  str += "\n inner attributes: ";
+  if (inner_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "inner attribute" syntax -
+       * just the body */
+      for (const auto &attr : inner_attrs)
+	{
+	  str += "\n  " + attr.as_string ();
+	}
+    }
+
+  str += "\n Tuple elements: ";
+  if (tuple_elems.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &elem : tuple_elems)
+	{
+	  str += "\n  " + elem->as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+ExprStmtWithoutBlock::as_string () const
+{
+  std::string str ("ExprStmtWithoutBlock:\n");
+  indent_spaces (enter);
+  str += indent_spaces (stay);
+
+  if (expr == nullptr)
+    {
+      str += "none (this shouldn't happen and is probably an error)";
+    }
+  else
+    {
+      str += expr->as_string ();
+    }
+  indent_spaces (out);
+
+  return str;
+}
+
+std::string
+FunctionParam::as_string () const
+{
+  return param_name->as_string () + " : " + type->as_string ();
+}
+
+std::string
+FunctionQualifiers::as_string () const
+{
+  std::string str;
+
+  switch (const_status)
+    {
+    case NONE:
+      // do nothing
+      break;
+    case CONST_FN:
+      str += "const ";
+      break;
+    case ASYNC_FN:
+      str += "async ";
+      break;
+    default:
+      return "ERROR_MARK_STRING: async-const status failure";
+    }
+
+  if (unsafety == Unsafety::Unsafe)
+    {
+      str += "unsafe ";
+    }
+
+  if (has_extern)
+    {
+      str += "extern";
+      str += " \"" + get_string_from_abi (abi) + "\"";
+    }
+
+  return str;
+}
+
+std::string
+TraitBound::as_string () const
+{
+  std::string str ("TraitBound:");
+
+  str += "\n Has opening question mark: ";
+  if (opening_question_mark)
+    {
+      str += "true";
+    }
+  else
+    {
+      str += "false";
+    }
+
+  str += "\n For lifetimes: ";
+  if (!has_for_lifetimes ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &lifetime : for_lifetimes)
+	{
+	  str += "\n  " + lifetime.as_string ();
+	}
+    }
+
+  str += "\n Type path: " + type_path.as_string ();
+
+  return str;
+}
+
+std::string
+LifetimeParam::as_string () const
+{
+  std::string str ("LifetimeParam: ");
+
+  str += "\n Outer attribute: ";
+  if (!has_outer_attribute ())
+    {
+      str += "none";
+    }
+  else
+    {
+      str += outer_attr.as_string ();
+    }
+
+  str += "\n Lifetime: " + lifetime.as_string ();
+
+  str += "\n Lifetime bounds: ";
+  if (!has_lifetime_bounds ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &bound : lifetime_bounds)
+	{
+	  str += "\n  " + bound.as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+QualifiedPathInType::as_string () const
+{
+  std::string str = path_type.as_string ();
+
+  for (const auto &segment : segments)
+    {
+      str += "::" + segment->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+Lifetime::as_string () const
+{
+  if (is_error ())
+    {
+      return "error lifetime";
+    }
+
+  switch (lifetime_type)
+    {
+    case AST::Lifetime::LifetimeType::NAMED:
+      return "'" + lifetime_name;
+    case AST::Lifetime::LifetimeType::STATIC:
+      return "'static";
+    case AST::Lifetime::LifetimeType::WILDCARD:
+      return "'_";
+    default:
+      return "ERROR-MARK-STRING: lifetime type failure";
+    }
+}
+
+std::string
+TypePath::as_string () const
+{
+  std::string str;
+
+  if (has_opening_scope_resolution)
+    {
+      str = "::";
+    }
+
+  for (const auto &segment : segments)
+    {
+      str += segment->as_string () + "::";
+    }
+
+  // kinda hack - remove last 2 '::' characters
+  str.erase (str.length () - 2);
+
+  return str;
+}
+
+std::string
+TypeParam::as_string () const
+{
+  std::string str ("TypeParam: ");
+
+  str += "\n Outer attribute: ";
+  if (!has_outer_attribute ())
+    {
+      str += "none";
+    }
+  else
+    {
+      str += outer_attr.as_string ();
+    }
+
+  str += "\n Identifier: " + type_representation;
+
+  str += "\n Type param bounds: ";
+  if (!has_type_param_bounds ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &bound : type_param_bounds)
+	{
+	  str += "\n  " + bound->as_string ();
+	}
+    }
+
+  str += "\n Type: ";
+  if (!has_type ())
+    {
+      str += "none";
+    }
+  else
+    {
+      str += type->as_string ();
+    }
+
+  return str;
+}
+
+AST::SimplePath
+PathPattern::convert_to_simple_path (bool with_opening_scope_resolution) const
+{
+  if (!has_segments ())
+    {
+      return AST::SimplePath::create_empty ();
+    }
+
+  // create vector of reserved size (to minimise reallocations)
+  std::vector<AST::SimplePathSegment> simple_segments;
+  simple_segments.reserve (segments.size ());
+
+  for (const auto &segment : segments)
+    {
+      // return empty path if doesn't meet simple path segment requirements
+      if (segment.has_generic_args () || segment.as_string () == "Self")
+	{
+	  return AST::SimplePath::create_empty ();
+	}
+
+      // create segment and add to vector
+      std::string segment_str = segment.as_string ();
+      simple_segments.push_back (
+	AST::SimplePathSegment (std::move (segment_str), segment.get_locus ()));
+    }
+
+  // kind of a HACK to get locus depending on opening scope resolution
+  Location locus = Linemap::unknown_location ();
+  if (with_opening_scope_resolution)
+    {
+      locus = simple_segments[0].get_locus () - 2; // minus 2 chars for ::
+    }
+  else
+    {
+      locus = simple_segments[0].get_locus ();
+    }
+
+  return AST::SimplePath (std::move (simple_segments),
+			  with_opening_scope_resolution, locus);
+}
+
+AST::SimplePath
+TypePath::as_simple_path () const
+{
+  if (segments.empty ())
+    {
+      return AST::SimplePath::create_empty ();
+    }
+
+  // create vector of reserved size (to minimise reallocations)
+  std::vector<AST::SimplePathSegment> simple_segments;
+  simple_segments.reserve (segments.size ());
+
+  for (const auto &segment : segments)
+    {
+      // return empty path if doesn't meet simple path segment requirements
+      if (segment == nullptr || segment->is_error ()
+	  || !segment->is_ident_only () || segment->as_string () == "Self")
+	{
+	  return AST::SimplePath::create_empty ();
+	}
+
+      // create segment and add to vector
+      std::string segment_str = segment->as_string ();
+      simple_segments.push_back (
+	AST::SimplePathSegment (std::move (segment_str),
+				segment->get_locus ()));
+    }
+
+  return AST::SimplePath (std::move (simple_segments),
+			  has_opening_scope_resolution, locus);
+}
+
+std::string
+PathExprSegment::as_string () const
+{
+  std::string ident_str = segment_name.as_string ();
+  if (has_generic_args ())
+    {
+      ident_str += "::<" + generic_args.as_string () + ">";
+    }
+
+  return ident_str;
+}
+
+std::string
+GenericArgs::as_string () const
+{
+  std::string args;
+
+  // lifetime args
+  if (!lifetime_args.empty ())
+    {
+      auto i = lifetime_args.begin ();
+      auto e = lifetime_args.end ();
+
+      for (; i != e; i++)
+	{
+	  args += (*i).as_string ();
+	  if (e != i + 1)
+	    args += ", ";
+	}
+    }
+
+  // type args
+  if (!type_args.empty ())
+    {
+      auto i = type_args.begin ();
+      auto e = type_args.end ();
+
+      for (; i != e; i++)
+	{
+	  args += (*i)->as_string ();
+	  if (e != i + 1)
+	    args += ", ";
+	}
+    }
+
+  // binding args
+  if (!binding_args.empty ())
+    {
+      auto i = binding_args.begin ();
+      auto e = binding_args.end ();
+
+      for (; i != e; i++)
+	{
+	  args += (*i).as_string ();
+	  if (e != i + 1)
+	    args += ", ";
+	}
+    }
+
+  return args;
+}
+
+std::string
+GenericArgsBinding::as_string () const
+{
+  return identifier + " = " + type->as_string ();
+}
+
+std::string
+ForLoopExpr::as_string () const
+{
+  std::string str ("ForLoopExpr: ");
+
+  str += "\n Label: ";
+  if (!has_loop_label ())
+    {
+      str += "none";
+    }
+  else
+    {
+      str += loop_label.as_string ();
+    }
+
+  str += "\n Pattern: " + pattern->as_string ();
+
+  str += "\n Iterator expr: " + iterator_expr->as_string ();
+
+  str += "\n Loop block: " + loop_block->as_string ();
+
+  return str;
+}
+
+std::string
+RangePattern::as_string () const
+{
+  if (has_ellipsis_syntax)
+    {
+      return lower->as_string () + "..." + upper->as_string ();
+    }
+  else
+    {
+      return lower->as_string () + "..=" + upper->as_string ();
+    }
+}
+
+std::string
+RangePatternBoundLiteral::as_string () const
+{
+  std::string str;
+
+  if (has_minus)
+    {
+      str += "-";
+    }
+
+  str += literal.as_string ();
+
+  return str;
+}
+
+std::string
+SlicePattern::as_string () const
+{
+  std::string str ("SlicePattern: ");
+
+  for (const auto &pattern : items)
+    {
+      str += "\n " + pattern->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+TuplePatternItemsMultiple::as_string () const
+{
+  std::string str;
+
+  for (const auto &pattern : patterns)
+    {
+      str += "\n " + pattern->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+TuplePatternItemsRanged::as_string () const
+{
+  std::string str;
+
+  str += "\n Lower patterns: ";
+  if (lower_patterns.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &lower : lower_patterns)
+	{
+	  str += "\n  " + lower->as_string ();
+	}
+    }
+
+  str += "\n Upper patterns: ";
+  if (upper_patterns.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &upper : upper_patterns)
+	{
+	  str += "\n  " + upper->as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+TuplePattern::as_string () const
+{
+  return "TuplePattern: " + items->as_string ();
+}
+
+std::string
+StructPatternField::as_string () const
+{
+  // outer attributes
+  std::string str ("Outer attributes: ");
+  if (outer_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "outer attribute" syntax -
+       * just the body */
+      for (const auto &attr : outer_attrs)
+	{
+	  str += "\n  " + attr.as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+StructPatternFieldIdent::as_string () const
+{
+  std::string str = StructPatternField::as_string ();
+
+  str += "\n";
+
+  if (has_ref)
+    {
+      str += "ref ";
+    }
+
+  if (is_mut ())
+    {
+      str += "mut ";
+    }
+
+  str += ident;
+
+  return str;
+}
+
+std::string
+StructPatternFieldTuplePat::as_string () const
+{
+  std::string str = StructPatternField::as_string ();
+
+  str += "\n";
+
+  str += std::to_string (index) + " : " + tuple_pattern->as_string ();
+
+  return str;
+}
+
+std::string
+StructPatternFieldIdentPat::as_string () const
+{
+  std::string str = StructPatternField::as_string ();
+
+  str += "\n";
+
+  str += ident + " : " + ident_pattern->as_string ();
+
+  return str;
+}
+
+std::string
+StructPatternElements::as_string () const
+{
+  std::string str ("\n  Fields: ");
+
+  if (!has_struct_pattern_fields ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &field : fields)
+	{
+	  str += "\n   " + field->as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+StructPattern::as_string () const
+{
+  std::string str ("StructPattern: \n Path: ");
+
+  str += path.as_string ();
+
+  str += "\n Struct pattern elems: ";
+  if (!has_struct_pattern_elems ())
+    {
+      str += "none";
+    }
+  else
+    {
+      str += elems.as_string ();
+    }
+
+  return str;
+}
+
+std::string
+LiteralPattern::as_string () const
+{
+  return lit.as_string ();
+}
+
+std::string
+ReferencePattern::as_string () const
+{
+  std::string str ("&");
+
+  if (has_two_amps)
+    {
+      str += "&";
+    }
+
+  if (is_mut ())
+    {
+      str += "mut ";
+    }
+
+  str += pattern->as_string ();
+
+  return str;
+}
+
+std::string
+IdentifierPattern::as_string () const
+{
+  std::string str;
+
+  if (is_ref)
+    {
+      str += "ref ";
+    }
+
+  if (is_mut ())
+    {
+      str += "mut ";
+    }
+
+  str += variable_ident;
+
+  if (has_pattern_to_bind ())
+    {
+      str += " @ " + to_bind->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+TupleStructItemsNoRange::as_string () const
+{
+  std::string str;
+
+  for (const auto &pattern : patterns)
+    {
+      str += "\n  " + pattern->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+TupleStructItemsRange::as_string () const
+{
+  std::string str ("\n  Lower patterns: ");
+
+  if (lower_patterns.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &lower : lower_patterns)
+	{
+	  str += "\n   " + lower->as_string ();
+	}
+    }
+
+  str += "\n  Upper patterns: ";
+  if (upper_patterns.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &upper : upper_patterns)
+	{
+	  str += "\n   " + upper->as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+TupleStructPattern::as_string () const
+{
+  std::string str ("TupleStructPattern: \n Path: ");
+
+  str += path.as_string ();
+
+  str += "\n Tuple struct items: " + items->as_string ();
+
+  return str;
+}
+
+std::string
+LetStmt::as_string () const
+{
+  // outer attributes
+  std::string str = "Outer attributes: ";
+  if (outer_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "outer attribute" syntax -
+       * just the body */
+      indent_spaces (enter);
+      for (const auto &attr : outer_attrs)
+	{
+	  str += "\n" + indent_spaces (stay) + attr.as_string ();
+	}
+      indent_spaces (out);
+    }
+
+  str += "\n" + indent_spaces (stay) + "let " + variables_pattern->as_string ();
+
+  if (has_type ())
+    {
+      str += " : " + type->as_string ();
+    }
+
+  if (has_init_expr ())
+    {
+      str += " = " + init_expr->as_string ();
+    }
+
+  return str;
+}
+
+// Used to get outer attributes for expressions.
+std::string
+Expr::as_string () const
+{
+  // outer attributes
+  std::string str = "outer attributes: ";
+  if (outer_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "outer attribute" syntax -
+       * just the body */
+      for (const auto &attr : outer_attrs)
+	{
+	  str += "\n  " + attr.as_string ();
+	}
+    }
+
+  return str;
+}
+
+// hopefully definition here will prevent circular dependency issue
+TraitBound *
+TypePath::to_trait_bound (bool in_parens) const
+{
+  // create clone FIXME is this required? or is copy constructor automatically
+  // called?
+  TypePath copy (*this);
+  return new TraitBound (mappings, std::move (copy), copy.get_locus (),
+			 in_parens);
+}
+
+std::string
+InferredType::as_string () const
+{
+  return "_ (inferred) " + get_mappings ().as_string ();
+}
+
+std::string
+TypeCastExpr::as_string () const
+{
+  return main_or_left_expr->as_string () + " as "
+	 + type_to_convert_to->as_string ();
+}
+
+std::string
+ImplTraitType::as_string () const
+{
+  std::string str ("ImplTraitType: \n TypeParamBounds: ");
+
+  if (type_param_bounds.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &bound : type_param_bounds)
+	{
+	  str += "\n  " + bound->as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+ReferenceType::as_string () const
+{
+  std::string str ("&");
+
+  if (has_lifetime ())
+    {
+      str += lifetime.as_string () + " ";
+    }
+
+  if (is_mut ())
+    {
+      str += "mut ";
+    }
+
+  str += type->as_string ();
+
+  return str;
+}
+
+std::string
+RawPointerType::as_string () const
+{
+  return std::string ("*") + (is_mut () ? "mut " : "const ")
+	 + type->as_string ();
+}
+
+std::string
+TraitObjectType::as_string () const
+{
+  std::string str ("TraitObjectType: \n Has dyn dispatch: ");
+
+  if (has_dyn)
+    {
+      str += "true";
+    }
+  else
+    {
+      str += "false";
+    }
+
+  str += "\n TypeParamBounds: ";
+  if (type_param_bounds.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &bound : type_param_bounds)
+	{
+	  str += "\n  " + bound->as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+BareFunctionType::as_string () const
+{
+  std::string str ("BareFunctionType: \n For lifetimes: ");
+
+  if (!has_for_lifetimes ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &for_lifetime : for_lifetimes)
+	{
+	  str += "\n  " + for_lifetime.as_string ();
+	}
+    }
+
+  str += "\n Qualifiers: " + function_qualifiers.as_string ();
+
+  str += "\n Params: ";
+  if (params.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : params)
+	{
+	  str += "\n  " + param.as_string ();
+	}
+    }
+
+  str += "\n Is variadic: ";
+  if (is_variadic)
+    {
+      str += "true";
+    }
+  else
+    {
+      str += "false";
+    }
+
+  str += "\n Return type: ";
+  if (!has_return_type ())
+    {
+      str += "none (void)";
+    }
+  else
+    {
+      str += return_type->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+ImplTraitTypeOneBound::as_string () const
+{
+  std::string str ("ImplTraitTypeOneBound: \n TraitBound: ");
+
+  return str + trait_bound.as_string ();
+}
+
+std::string
+TypePathSegmentGeneric::as_string () const
+{
+  return TypePathSegment::as_string () + "<" + generic_args.as_string () + ">";
+}
+
+std::string
+TypePathFunction::as_string () const
+{
+  std::string str ("(");
+
+  if (has_inputs ())
+    {
+      auto i = inputs.begin ();
+      auto e = inputs.end ();
+
+      for (; i != e; i++)
+	{
+	  str += (*i)->as_string ();
+	  if (e != i + 1)
+	    str += ", ";
+	}
+    }
+
+  str += ")";
+
+  if (has_return_type ())
+    {
+      str += " -> " + return_type->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+TypePathSegmentFunction::as_string () const
+{
+  return TypePathSegment::as_string () + function_path.as_string ();
+}
+
+std::string
+ArrayType::as_string () const
+{
+  return "[" + elem_type->as_string () + "; " + size->as_string () + "]";
+}
+
+std::string
+SliceType::as_string () const
+{
+  return "[" + elem_type->as_string () + "]";
+}
+
+std::string
+TupleType::as_string () const
+{
+  std::string str ("(");
+
+  if (!is_unit_type ())
+    {
+      auto i = elems.begin ();
+      auto e = elems.end ();
+
+      for (; i != e; i++)
+	{
+	  str += (*i)->as_string ();
+	  if (e != i + 1)
+	    str += ", ";
+	}
+    }
+
+  str += ")";
+
+  return str;
+}
+
+std::string
+StructExpr::as_string () const
+{
+  std::string str = ExprWithoutBlock::as_string ();
+  indent_spaces (enter);
+  str += "\n" + indent_spaces (stay) + "StructExpr:";
+  indent_spaces (enter);
+  str += "\n" + indent_spaces (stay) + "PathInExpr:\n";
+  str += indent_spaces (stay) + struct_name.as_string ();
+  indent_spaces (out);
+  indent_spaces (out);
+  return str;
+}
+
+std::string
+StructExprStruct::as_string () const
+{
+  std::string str ("StructExprStruct (or subclass): ");
+
+  str += "\n Path: " + struct_name.as_string ();
+
+  // inner attributes
+  str += "\n inner attributes: ";
+  if (inner_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "inner attribute" syntax -
+       * just the body */
+      for (const auto &attr : inner_attrs)
+	{
+	  str += "\n  " + attr.as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+StructBase::as_string () const
+{
+  if (base_struct != nullptr)
+    {
+      return base_struct->as_string ();
+    }
+  else
+    {
+      return "ERROR_MARK_STRING - invalid struct base had as string applied";
+    }
+}
+
+std::string
+StructExprFieldWithVal::as_string () const
+{
+  // used to get value string
+  return value->as_string ();
+}
+
+std::string
+StructExprFieldIdentifierValue::as_string () const
+{
+  return field_name + " : " + StructExprFieldWithVal::as_string ();
+}
+
+std::string
+StructExprFieldIndexValue::as_string () const
+{
+  return std::to_string (index) + " : " + StructExprFieldWithVal::as_string ();
+}
+
+std::string
+StructExprStructFields::as_string () const
+{
+  std::string str = StructExprStruct::as_string ();
+
+  str += "\n Fields: ";
+  if (fields.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &field : fields)
+	{
+	  str += "\n  " + field->as_string ();
+	}
+    }
+
+  str += "\n Struct base: ";
+  if (!has_struct_base ())
+    {
+      str += "none";
+    }
+  else
+    {
+      str += struct_base->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+EnumItem::as_string () const
+{
+  std::string str = Item::as_string ();
+  str += variant_name;
+  str += " ";
+  switch (get_enum_item_kind ())
+    {
+    case Named:
+      str += "[Named variant]";
+      break;
+    case Tuple:
+      str += "[Tuple variant]";
+      break;
+    case Struct:
+      str += "[Struct variant]";
+      break;
+    case Discriminant:
+      str += "[Discriminant variant]";
+      break;
+    }
+
+  return str;
+}
+
+std::string
+EnumItemTuple::as_string () const
+{
+  std::string str = EnumItem::as_string ();
+
+  // add tuple opening parens
+  str += "(";
+
+  // tuple fields
+  if (has_tuple_fields ())
+    {
+      auto i = tuple_fields.begin ();
+      auto e = tuple_fields.end ();
+
+      for (; i != e; i++)
+	{
+	  str += (*i).as_string ();
+	  if (e != i + 1)
+	    str += ", ";
+	}
+    }
+
+  // add tuple closing parens
+  str += ")";
+
+  return str;
+}
+
+std::string
+TupleField::as_string () const
+{
+  // outer attributes
+  std::string str = "outer attributes: ";
+  if (outer_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "outer attribute" syntax -
+       * just the body */
+      for (const auto &attr : outer_attrs)
+	{
+	  str += "\n  " + attr.as_string ();
+	}
+    }
+
+  if (has_visibility ())
+    {
+      str += "\n" + visibility.as_string ();
+    }
+
+  str += " " + field_type->as_string ();
+
+  return str;
+}
+
+std::string
+EnumItemStruct::as_string () const
+{
+  std::string str = EnumItem::as_string ();
+
+  // add struct opening parens
+  str += "{";
+
+  // tuple fields
+  if (has_struct_fields ())
+    {
+      auto i = struct_fields.begin ();
+      auto e = struct_fields.end ();
+
+      for (; i != e; i++)
+	{
+	  str += (*i).as_string ();
+	  if (e != i + 1)
+	    str += ", ";
+	}
+    }
+
+  // add struct closing parens
+  str += "}";
+
+  return str;
+}
+
+std::string
+StructField::as_string () const
+{
+  // outer attributes
+  std::string str = "outer attributes: ";
+  if (outer_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "outer attribute" syntax -
+       * just the body */
+      for (const auto &attr : outer_attrs)
+	{
+	  str += "\n  " + attr.as_string ();
+	}
+    }
+
+  if (has_visibility ())
+    {
+      str += "\n" + visibility.as_string ();
+    }
+
+  str += " " + field_name + " : " + field_type->as_string ();
+
+  return str;
+}
+
+std::string
+EnumItemDiscriminant::as_string () const
+{
+  std::string str = EnumItem::as_string ();
+
+  // add equal and expression
+  str += " = " + expression->as_string ();
+
+  return str;
+}
+
+std::string
+ExternalItem::as_string () const
+{
+  // outer attributes
+  std::string str = "outer attributes: ";
+  if (outer_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "outer attribute" syntax -
+       * just the body */
+      for (const auto &attr : outer_attrs)
+	{
+	  str += "\n  " + attr.as_string ();
+	}
+    }
+
+  // start visibility on new line and with a space
+  str += "\n" + visibility.as_string () + " ";
+
+  return str;
+}
+
+std::string
+ExternalStaticItem::as_string () const
+{
+  std::string str = ExternalItem::as_string ();
+
+  str += "static ";
+
+  if (is_mut ())
+    {
+      str += "mut ";
+    }
+
+  // add name
+  str += get_item_name ();
+
+  // add type on new line
+  str += "\n Type: " + item_type->as_string ();
+
+  return str;
+}
+
+std::string
+ExternalFunctionItem::as_string () const
+{
+  std::string str = ExternalItem::as_string ();
+
+  str += "fn ";
+
+  // add name
+  str += get_item_name ();
+
+  // generic params
+  str += "\n Generic params: ";
+  if (generic_params.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : generic_params)
+	{
+	  // DEBUG: null pointer check
+	  if (param == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"generic param in external function item.");
+	      return "nullptr_POINTER_MARK";
+	    }
+
+	  str += "\n  " + param->as_string ();
+	}
+    }
+
+  // function params
+  str += "\n Function params: ";
+  if (function_params.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : function_params)
+	{
+	  str += "\n  " + param.as_string ();
+	}
+      if (has_variadics)
+	{
+	  str += "\n  .. (variadic)";
+	}
+    }
+
+  // add type on new line)
+  str += "\n (return) Type: "
+	 + (has_return_type () ? return_type->as_string () : "()");
+
+  // where clause
+  str += "\n Where clause: ";
+  if (has_where_clause ())
+    {
+      str += where_clause.as_string ();
+    }
+  else
+    {
+      str += "none";
+    }
+
+  return str;
+}
+
+std::string
+NamedFunctionParam::as_string () const
+{
+  std::string str = name;
+
+  str += "\n Type: " + param_type->as_string ();
+
+  return str;
+}
+
+/*std::string TraitItem::as_string() const {
+    // outer attributes
+    std::string str = "outer attributes: ";
+    if (outer_attrs.empty()) {
+	str += "none";
+    } else {
+	// note that this does not print them with "outer attribute" syntax -
+just the body for (const auto& attr : outer_attrs) { str += "\n  " +
+attr.as_string();
+	}
+    }
+
+    return str;
+}*/
+
+std::string
+TraitItemFunc::as_string () const
+{
+  std::string str = "outer attributes: ";
+  if (outer_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "outer attribute" syntax -
+       * just the body */
+      for (const auto &attr : outer_attrs)
+	{
+	  str += "\n  " + attr.as_string ();
+	}
+    }
+
+  str += "\n" + decl.as_string ();
+
+  str += "\n Definition (block expr): ";
+  if (has_definition ())
+    {
+      str += block_expr->as_string ();
+    }
+  else
+    {
+      str += "none";
+    }
+
+  return str;
+}
+
+std::string
+TraitFunctionDecl::as_string () const
+{
+  std::string str = qualifiers.as_string () + "fn " + function_name;
+
+  // generic params
+  str += "\n Generic params: ";
+  if (generic_params.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &param : generic_params)
+	{
+	  // DEBUG: null pointer check
+	  if (param == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"generic param in trait function decl.");
+	      return "nullptr_POINTER_MARK";
+	    }
+
+	  str += "\n  " + param->as_string ();
+	}
+    }
+
+  str += "\n Function params: ";
+  if (is_method ())
+    {
+      str += self.as_string () + (has_params () ? ", " : "");
+    }
+
+  if (has_params ())
+    {
+      for (const auto &param : function_params)
+	{
+	  str += "\n  " + param.as_string ();
+	}
+    }
+  else if (!is_method ())
+    {
+      str += "none";
+    }
+
+  str += "\n Return type: ";
+  if (has_return_type ())
+    {
+      str += return_type->as_string ();
+    }
+  else
+    {
+      str += "none (void)";
+    }
+
+  str += "\n Where clause: ";
+  if (has_where_clause ())
+    {
+      str += where_clause.as_string ();
+    }
+  else
+    {
+      str += "none";
+    }
+
+  return str;
+}
+
+std::string
+TraitItemConst::as_string () const
+{
+  std::string str = "outer attributes: ";
+  if (outer_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "outer attribute" syntax -
+       * just the body */
+      for (const auto &attr : outer_attrs)
+	{
+	  str += "\n  " + attr.as_string ();
+	}
+    }
+
+  str += "\nconst " + name + " : " + type->as_string ();
+
+  if (has_expression ())
+    {
+      str += " = " + expr->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+TraitItemType::as_string () const
+{
+  std::string str = "outer attributes: ";
+  if (outer_attrs.empty ())
+    {
+      str += "none";
+    }
+  else
+    {
+      /* note that this does not print them with "outer attribute" syntax -
+       * just the body */
+      for (const auto &attr : outer_attrs)
+	{
+	  str += "\n  " + attr.as_string ();
+	}
+    }
+
+  str += "\ntype " + name;
+
+  str += "\n Type param bounds: ";
+  if (!has_type_param_bounds ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &bound : type_param_bounds)
+	{
+	  // DEBUG: null pointer check
+	  if (bound == nullptr)
+	    {
+	      rust_debug (
+		"something really terrible has gone wrong - null pointer "
+		"type param bound in trait item type.");
+	      return "nullptr_POINTER_MARK";
+	    }
+
+	  str += "\n  " + bound->as_string ();
+	}
+    }
+
+  return str;
+}
+
+std::string
+SelfParam::as_string () const
+{
+  if (is_error ())
+    {
+      return "error";
+    }
+  else
+    {
+      if (has_type ())
+	{
+	  // type (i.e. not ref, no lifetime)
+	  std::string str;
+
+	  if (is_mut ())
+	    {
+	      str += "mut ";
+	    }
+
+	  str += "self : ";
+
+	  str += type->as_string ();
+
+	  return str;
+	}
+      else if (has_lifetime ())
+	{
+	  // ref and lifetime
+	  std::string str = "&" + lifetime.as_string () + " ";
+
+	  if (is_mut ())
+	    {
+	      str += "mut ";
+	    }
+
+	  str += "self";
+
+	  return str;
+	}
+      else if (is_ref ())
+	{
+	  // ref with no lifetime
+	  std::string str = "&";
+
+	  if (is_mut ())
+	    {
+	      str += " mut ";
+	    }
+
+	  str += "self";
+
+	  return str;
+	}
+      else
+	{
+	  // no ref, no type
+	  std::string str;
+
+	  if (is_mut ())
+	    {
+	      str += "mut ";
+	    }
+
+	  str += "self";
+
+	  return str;
+	}
+    }
+}
+
+std::string
+ArrayElemsCopied::as_string () const
+{
+  return elem_to_copy->as_string () + "; " + num_copies->as_string ();
+}
+
+std::string
+LifetimeWhereClauseItem::as_string () const
+{
+  std::string str ("Lifetime: ");
+
+  str += lifetime.as_string ();
+
+  str += "\nLifetime bounds: ";
+
+  for (const auto &bound : lifetime_bounds)
+    {
+      str += "\n " + bound.as_string ();
+    }
+
+  return str;
+}
+
+std::string
+TypeBoundWhereClauseItem::as_string () const
+{
+  std::string str ("For lifetimes: ");
+
+  if (!has_for_lifetimes ())
+    {
+      str += "none";
+    }
+  else
+    {
+      for (const auto &for_lifetime : for_lifetimes)
+	{
+	  str += "\n " + for_lifetime.as_string ();
+	}
+    }
+
+  str += "\nType: " + bound_type->as_string ();
+
+  str += "\nType param bounds bounds: ";
+
+  for (const auto &bound : type_param_bounds)
+    {
+      // debug null pointer check
+      if (bound == nullptr)
+	{
+	  return "nullptr_POINTER_MARK - type param bounds";
+	}
+
+      str += "\n " + bound->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+ArrayElemsValues::as_string () const
+{
+  std::string str;
+
+  for (const auto &expr : values)
+    {
+      // DEBUG: null pointer check
+      if (expr == nullptr)
+	{
+	  rust_debug ("something really terrible has gone wrong - null pointer "
+		      "expr in array elems values.");
+	  return "nullptr_POINTER_MARK";
+	}
+
+      str += "\n  " + expr->as_string ();
+    }
+
+  return str;
+}
+
+std::string
+MaybeNamedParam::as_string () const
+{
+  std::string str;
+
+  switch (param_kind)
+    {
+    case UNNAMED:
+      break;
+    case IDENTIFIER:
+      str = name + " : ";
+      break;
+    case WILDCARD:
+      str = "_ : ";
+      break;
+    default:
+      return "ERROR_MARK_STRING - maybe named param unrecognised param kind";
+    }
+
+  str += param_type->as_string ();
+
+  return str;
+}
+
+/* Override that calls the function recursively on all items contained within
+ * the module. */
+void
+Module::add_crate_name (std::vector<std::string> &names) const
+{
+  /* TODO: test whether module has been 'cfg'-ed out to determine whether to
+   * exclude it from search */
+
+  for (const auto &item : items)
+    item->add_crate_name (names);
+}
+
+/* All accept_vis method below */
+
+void
+Lifetime::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+LifetimeParam::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+PathInExpression::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+void
+PathInExpression::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TypePathSegment::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TypePathSegmentGeneric::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TypePathSegmentFunction::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TypePath::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+QualifiedPathInExpression::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+void
+QualifiedPathInExpression::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+QualifiedPathInType::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+LiteralExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+LiteralExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+BorrowExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+BorrowExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+DereferenceExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+DereferenceExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ErrorPropagationExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ErrorPropagationExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+NegationExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+NegationExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ArithmeticOrLogicalExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ArithmeticOrLogicalExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ComparisonExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ComparisonExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+LazyBooleanExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+LazyBooleanExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TypeCastExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TypeCastExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+AssignmentExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+AssignmentExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+CompoundAssignmentExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+CompoundAssignmentExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+GroupedExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+GroupedExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ArrayElemsValues::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ArrayElemsCopied::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ArrayExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ArrayIndexExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TupleExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TupleIndexExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructExprStruct::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructExprFieldIndexValue::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructExprStructFields::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructExprStructBase::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+CallExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+MethodCallExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+FieldAccessExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ClosureExprInner::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+BlockExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ClosureExprInnerTyped::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ContinueExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+BreakExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangeFromToExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangeFromExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangeToExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangeFullExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangeFromToInclExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangeToInclExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ReturnExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+UnsafeBlockExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+LoopExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+WhileLoopExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+WhileLetLoopExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ForLoopExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfExprConseqElse::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfExprConseqIf::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfExprConseqIfLet::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfLetExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfLetExprConseqElse::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfLetExprConseqIf::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfLetExprConseqIfLet::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+MatchExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+AwaitExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+AsyncBlockExpr::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TypeParam::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+LifetimeWhereClauseItem::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TypeBoundWhereClauseItem::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Module::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Module::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Module::accept_vis (HIRVisItemVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ExternCrate::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+UseTreeGlob::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+UseTreeList::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+UseTreeRebind::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+UseDeclaration::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Function::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TypeAlias::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructStruct::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TupleStruct::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+EnumItem::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+EnumItemTuple::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+EnumItemStruct::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+EnumItemDiscriminant::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Enum::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Union::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ConstantItem::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StaticItem::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TraitItemFunc::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TraitItemConst::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TraitItemType::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Trait::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ImplBlock::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ExternalStaticItem::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ExternalFunctionItem::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ExternBlock::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+LiteralPattern::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IdentifierPattern::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+WildcardPattern::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangePatternBoundLiteral::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangePatternBoundPath::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangePatternBoundQualPath::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangePattern::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ReferencePattern::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructPatternFieldTuplePat::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructPatternFieldIdentPat::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructPatternFieldIdent::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructPattern::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TupleStructItemsNoRange::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TupleStructItemsRange::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TupleStructPattern::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TuplePatternItemsMultiple::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TuplePatternItemsRanged::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TuplePattern::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+GroupedPattern::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+SlicePattern::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+EmptyStmt::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+LetStmt::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ExprStmtWithoutBlock::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ExprStmtWithBlock::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TraitBound::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ImplTraitType::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TraitObjectType::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ParenthesisedType::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ImplTraitTypeOneBound::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TupleType::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+NeverType::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RawPointerType::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ReferenceType::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ArrayType::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+SliceType::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+InferredType::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+BareFunctionType::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+NeverType::accept_vis (HIRTypeVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ParenthesisedType::accept_vis (HIRTypeVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+EmptyStmt::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+GroupedPattern::accept_vis (HIRPatternVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+WildcardPattern::accept_vis (HIRPatternVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TraitItemType::accept_vis (HIRTraitItemVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TraitItemConst::accept_vis (HIRTraitItemVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TraitItemFunc::accept_vis (HIRTraitItemVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ExternalFunctionItem::accept_vis (HIRExternalItemVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ExternalStaticItem::accept_vis (HIRExternalItemVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+EnumItemDiscriminant::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+EnumItemStruct::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+EnumItemTuple::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+EnumItem::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructExprStructFields::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructExprFieldIndexValue::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructExprFieldIdentifierValue::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructExprFieldIdentifierValue::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructExprFieldIdentifier::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructExprFieldIdentifier::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructExprStruct::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TupleType::accept_vis (HIRTypeVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+SliceType::accept_vis (HIRTypeVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ArrayType::accept_vis (HIRTypeVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ImplTraitTypeOneBound::accept_vis (HIRTypeVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+BareFunctionType::accept_vis (HIRTypeVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TraitObjectType::accept_vis (HIRTypeVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RawPointerType::accept_vis (HIRTypeVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ReferenceType::accept_vis (HIRTypeVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ImplTraitType::accept_vis (HIRTypeVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+InferredType::accept_vis (HIRTypeVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+LetStmt::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TupleStructPattern::accept_vis (HIRPatternVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IdentifierPattern::accept_vis (HIRPatternVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ReferencePattern::accept_vis (HIRPatternVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+LiteralPattern::accept_vis (HIRPatternVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructPattern::accept_vis (HIRPatternVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TuplePattern::accept_vis (HIRPatternVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+SlicePattern::accept_vis (HIRPatternVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangePattern::accept_vis (HIRPatternVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ForLoopExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TypePath::accept_vis (HIRTypeVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+QualifiedPathInType::accept_vis (HIRTypeVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ExprStmtWithoutBlock::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TupleExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+MatchExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+BreakExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+AwaitExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ArrayExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+LoopExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+WhileLetLoopExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+WhileLoopExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+CallExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangeFromToInclExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfLetExprConseqIfLet::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfLetExprConseqIf::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfLetExprConseqElse::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfLetExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfExprConseqIfLet::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfExprConseqIf::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfExprConseqElse::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IfExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ClosureExprInner::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+UnsafeBlockExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangeToInclExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangeFromToExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+FieldAccessExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TupleIndexExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+MethodCallExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+AsyncBlockExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ArrayIndexExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangeFullExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangeFromExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ContinueExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+RangeToExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ReturnExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+QualifiedPathInExpression::accept_vis (HIRPatternVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ClosureExprInnerTyped::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ExprStmtWithBlock::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+PathInExpression::accept_vis (HIRPatternVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ExternBlock::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ExternBlock::accept_vis (HIRVisItemVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TypeAlias::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TypeAlias::accept_vis (HIRVisItemVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TypeAlias::accept_vis (HIRImplVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+BlockExpr::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Function::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Function::accept_vis (HIRVisItemVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Function::accept_vis (HIRImplVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Union::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Union::accept_vis (HIRVisItemVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Trait::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Trait::accept_vis (HIRVisItemVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Enum::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+Enum::accept_vis (HIRVisItemVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+UseDeclaration::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+UseDeclaration::accept_vis (HIRVisItemVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructStruct::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StructStruct::accept_vis (HIRVisItemVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ImplBlock::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ImplBlock::accept_vis (HIRVisItemVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ConstantItem::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ConstantItem::accept_vis (HIRVisItemVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ConstantItem::accept_vis (HIRImplVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TupleStruct::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TupleStruct::accept_vis (HIRVisItemVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ExternCrate::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ExternCrate::accept_vis (HIRVisItemVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StaticItem::accept_vis (HIRStmtVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StaticItem::accept_vis (HIRVisItemVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+std::string
+ConstGenericParam::as_string () const
+{
+  auto result = "ConstGenericParam: " + name + " : " + type->as_string ();
+
+  if (default_expression)
+    result += " = " + default_expression->as_string ();
+
+  return result;
+}
+
+void
+ConstGenericParam::accept_vis (HIRFullVisitor &vis)
+{}
+
+} // namespace HIR
+} // namespace Rust
diff --git a/gcc/rust/hir/tree/rust-hir-full.h b/gcc/rust/hir/tree/rust-hir-full.h
new file mode 100644
index 00000000000..646b793654e
--- /dev/null
+++ b/gcc/rust/hir/tree/rust-hir-full.h
@@ -0,0 +1,30 @@
+// 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_HIR_FULL_H
+#define RUST_HIR_FULL_H
+
+#include "rust-hir.h"
+#include "rust-hir-expr.h"
+#include "rust-hir-item.h"
+#include "rust-hir-path.h"
+#include "rust-hir-pattern.h"
+#include "rust-hir-stmt.h"
+#include "rust-hir-type.h"
+
+#endif // RUST_HIR_FULL_H
diff --git a/gcc/rust/hir/tree/rust-hir-item.h b/gcc/rust/hir/tree/rust-hir-item.h
new file mode 100644
index 00000000000..394b04f6c7f
--- /dev/null
+++ b/gcc/rust/hir/tree/rust-hir-item.h
@@ -0,0 +1,3207 @@
+// 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_HIR_ITEM_H
+#define RUST_HIR_ITEM_H
+
+#include "rust-abi.h"
+#include "rust-ast-full-decls.h"
+#include "rust-common.h"
+#include "rust-hir.h"
+#include "rust-hir-path.h"
+
+namespace Rust {
+namespace HIR {
+// forward decls
+class BlockExpr;
+class TypePath;
+
+// A type generic parameter (as opposed to a lifetime generic parameter)
+class TypeParam : public GenericParam
+{
+  // bool has_outer_attribute;
+  // std::unique_ptr<Attribute> outer_attr;
+  AST::Attribute outer_attr;
+
+  Identifier type_representation;
+
+  // bool has_type_param_bounds;
+  // TypeParamBounds type_param_bounds;
+  std::vector<std::unique_ptr<TypeParamBound>>
+    type_param_bounds; // inlined form
+
+  // bool has_type;
+  std::unique_ptr<Type> type;
+
+  Location locus;
+
+public:
+  // Returns whether the type of the type param has been specified.
+  bool has_type () const { return type != nullptr; }
+
+  // Returns whether the type param has type param bounds.
+  bool has_type_param_bounds () const { return !type_param_bounds.empty (); }
+
+  // Returns whether the type param has an outer attribute.
+  bool has_outer_attribute () const { return !outer_attr.is_empty (); }
+
+  TypeParam (Analysis::NodeMapping mappings, Identifier type_representation,
+	     Location locus = Location (),
+	     std::vector<std::unique_ptr<TypeParamBound>> type_param_bounds
+	     = std::vector<std::unique_ptr<TypeParamBound>> (),
+	     std::unique_ptr<Type> type = nullptr,
+	     AST::Attribute outer_attr = AST::Attribute::create_empty ())
+    : GenericParam (mappings), outer_attr (std::move (outer_attr)),
+      type_representation (std::move (type_representation)),
+      type_param_bounds (std::move (type_param_bounds)),
+      type (std::move (type)), locus (locus)
+  {}
+
+  // Copy constructor uses clone
+  TypeParam (TypeParam const &other)
+    : GenericParam (other.mappings), outer_attr (other.outer_attr),
+      type_representation (other.type_representation), locus (other.locus)
+  {
+    // guard to prevent null pointer dereference
+    if (other.type != nullptr)
+      type = other.type->clone_type ();
+
+    type_param_bounds.reserve (other.type_param_bounds.size ());
+    for (const auto &e : other.type_param_bounds)
+      type_param_bounds.push_back (e->clone_type_param_bound ());
+  }
+
+  // Overloaded assignment operator to clone
+  TypeParam &operator= (TypeParam const &other)
+  {
+    type_representation = other.type_representation;
+    outer_attr = other.outer_attr;
+    locus = other.locus;
+    mappings = other.mappings;
+
+    // guard to prevent null pointer dereference
+    if (other.type != nullptr)
+      type = other.type->clone_type ();
+    else
+      type = nullptr;
+
+    type_param_bounds.reserve (other.type_param_bounds.size ());
+    for (const auto &e : other.type_param_bounds)
+      type_param_bounds.push_back (e->clone_type_param_bound ());
+
+    return *this;
+  }
+  // move constructors
+  TypeParam (TypeParam &&other) = default;
+  TypeParam &operator= (TypeParam &&other) = default;
+
+  std::string as_string () const override;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  Identifier get_type_representation () const { return type_representation; }
+
+  std::unique_ptr<Type> &get_type ()
+  {
+    rust_assert (type != nullptr);
+    return type;
+  }
+
+  Analysis::NodeMapping get_type_mappings () const
+  {
+    rust_assert (type != nullptr);
+    return type->get_mappings ();
+  }
+
+  std::vector<std::unique_ptr<TypeParamBound>> &get_type_param_bounds ()
+  {
+    return type_param_bounds;
+  }
+
+protected:
+  // Clone function implementation as (not pure) virtual method
+  TypeParam *clone_generic_param_impl () const override
+  {
+    return new TypeParam (*this);
+  }
+};
+
+/* "where" clause item base. Abstract - use LifetimeWhereClauseItem,
+ * TypeBoundWhereClauseItem */
+class WhereClauseItem
+{
+public:
+  enum ItemType
+  {
+    LIFETIME,
+    TYPE_BOUND,
+  };
+
+  virtual ~WhereClauseItem () {}
+
+  // Unique pointer custom clone function
+  std::unique_ptr<WhereClauseItem> clone_where_clause_item () const
+  {
+    return std::unique_ptr<WhereClauseItem> (clone_where_clause_item_impl ());
+  }
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (HIRFullVisitor &vis) = 0;
+
+  virtual Analysis::NodeMapping get_mappings () const = 0;
+
+  virtual ItemType get_item_type () const = 0;
+
+protected:
+  // Clone function implementation as pure virtual method
+  virtual WhereClauseItem *clone_where_clause_item_impl () const = 0;
+};
+
+// A lifetime where clause item
+class LifetimeWhereClauseItem : public WhereClauseItem
+{
+  Lifetime lifetime;
+  std::vector<Lifetime> lifetime_bounds;
+  Location locus;
+  Analysis::NodeMapping mappings;
+
+public:
+  LifetimeWhereClauseItem (Analysis::NodeMapping mappings, Lifetime lifetime,
+			   std::vector<Lifetime> lifetime_bounds,
+			   Location locus)
+    : lifetime (std::move (lifetime)),
+      lifetime_bounds (std::move (lifetime_bounds)), locus (locus),
+      mappings (std::move (mappings))
+  {}
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  Lifetime &get_lifetime () { return lifetime; }
+
+  std::vector<Lifetime> &get_lifetime_bounds () { return lifetime_bounds; }
+
+  Analysis::NodeMapping get_mappings () const override final
+  {
+    return mappings;
+  };
+
+  ItemType get_item_type () const override final
+  {
+    return WhereClauseItem::ItemType::LIFETIME;
+  }
+
+protected:
+  // Clone function implementation as (not pure) virtual method
+  LifetimeWhereClauseItem *clone_where_clause_item_impl () const override
+  {
+    return new LifetimeWhereClauseItem (*this);
+  }
+};
+
+// A type bound where clause item
+class TypeBoundWhereClauseItem : public WhereClauseItem
+{
+  std::vector<LifetimeParam> for_lifetimes;
+  std::unique_ptr<Type> bound_type;
+  std::vector<std::unique_ptr<TypeParamBound>> type_param_bounds;
+  Analysis::NodeMapping mappings;
+  Location locus;
+
+public:
+  // Returns whether the item has ForLifetimes
+  bool has_for_lifetimes () const { return !for_lifetimes.empty (); }
+
+  // Returns whether the item has type param bounds
+  bool has_type_param_bounds () const { return !type_param_bounds.empty (); }
+
+  TypeBoundWhereClauseItem (
+    Analysis::NodeMapping mappings, std::vector<LifetimeParam> for_lifetimes,
+    std::unique_ptr<Type> bound_type,
+    std::vector<std::unique_ptr<TypeParamBound>> type_param_bounds,
+    Location locus)
+    : for_lifetimes (std::move (for_lifetimes)),
+      bound_type (std::move (bound_type)),
+      type_param_bounds (std::move (type_param_bounds)),
+      mappings (std::move (mappings)), locus (locus)
+  {}
+
+  // Copy constructor requires clone
+  TypeBoundWhereClauseItem (TypeBoundWhereClauseItem const &other)
+    : for_lifetimes (other.for_lifetimes),
+      bound_type (other.bound_type->clone_type ()), mappings (other.mappings)
+  {
+    type_param_bounds.reserve (other.type_param_bounds.size ());
+    for (const auto &e : other.type_param_bounds)
+      type_param_bounds.push_back (e->clone_type_param_bound ());
+  }
+
+  // Overload assignment operator to clone
+  TypeBoundWhereClauseItem &operator= (TypeBoundWhereClauseItem const &other)
+  {
+    mappings = other.mappings;
+    for_lifetimes = other.for_lifetimes;
+    bound_type = other.bound_type->clone_type ();
+    type_param_bounds.reserve (other.type_param_bounds.size ());
+    for (const auto &e : other.type_param_bounds)
+      type_param_bounds.push_back (e->clone_type_param_bound ());
+
+    return *this;
+  }
+
+  // move constructors
+  TypeBoundWhereClauseItem (TypeBoundWhereClauseItem &&other) = default;
+  TypeBoundWhereClauseItem &operator= (TypeBoundWhereClauseItem &&other)
+    = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  std::vector<LifetimeParam> &get_for_lifetimes () { return for_lifetimes; }
+
+  std::unique_ptr<Type> &get_bound_type () { return bound_type; }
+
+  std::vector<std::unique_ptr<TypeParamBound>> &get_type_param_bounds ()
+  {
+    return type_param_bounds;
+  }
+
+  Analysis::NodeMapping get_mappings () const override final
+  {
+    return mappings;
+  };
+
+  ItemType get_item_type () const override final
+  {
+    return WhereClauseItem::ItemType::TYPE_BOUND;
+  }
+
+protected:
+  // Clone function implementation as (not pure) virtual method
+  TypeBoundWhereClauseItem *clone_where_clause_item_impl () const override
+  {
+    return new TypeBoundWhereClauseItem (*this);
+  }
+};
+
+// A where clause
+struct WhereClause
+{
+private:
+  std::vector<std::unique_ptr<WhereClauseItem>> where_clause_items;
+
+  // should this store location info?
+
+public:
+  WhereClause (std::vector<std::unique_ptr<WhereClauseItem>> where_clause_items)
+    : where_clause_items (std::move (where_clause_items))
+  {}
+
+  // copy constructor with vector clone
+  WhereClause (WhereClause const &other)
+  {
+    where_clause_items.reserve (other.where_clause_items.size ());
+    for (const auto &e : other.where_clause_items)
+      where_clause_items.push_back (e->clone_where_clause_item ());
+  }
+
+  // overloaded assignment operator with vector clone
+  WhereClause &operator= (WhereClause const &other)
+  {
+    where_clause_items.reserve (other.where_clause_items.size ());
+    for (const auto &e : other.where_clause_items)
+      where_clause_items.push_back (e->clone_where_clause_item ());
+
+    return *this;
+  }
+
+  // move constructors
+  WhereClause (WhereClause &&other) = default;
+  WhereClause &operator= (WhereClause &&other) = default;
+
+  // Creates a WhereClause with no items.
+  static WhereClause create_empty ()
+  {
+    return WhereClause (std::vector<std::unique_ptr<WhereClauseItem>> ());
+  }
+
+  // Returns whether the WhereClause has no items.
+  bool is_empty () const { return where_clause_items.empty (); }
+
+  std::string as_string () const;
+
+  std::vector<std::unique_ptr<WhereClauseItem>> &get_items ()
+  {
+    return where_clause_items;
+  }
+  const std::vector<std::unique_ptr<WhereClauseItem>> &get_items () const
+  {
+    return where_clause_items;
+  }
+};
+
+// A self parameter in a method
+struct SelfParam
+{
+public:
+  enum ImplicitSelfKind
+  {
+    IMM,     // self
+    MUT,     // mut self
+    IMM_REF, // &self
+    MUT_REF, // &mut self
+    NONE
+  };
+
+private:
+  ImplicitSelfKind self_kind;
+  Lifetime lifetime;
+  std::unique_ptr<Type> type;
+  Location locus;
+  Analysis::NodeMapping mappings;
+
+  SelfParam (Analysis::NodeMapping mappings, ImplicitSelfKind self_kind,
+	     Lifetime lifetime, Type *type)
+    : self_kind (self_kind), lifetime (std::move (lifetime)), type (type),
+      mappings (mappings)
+  {}
+
+public:
+  // Type-based self parameter (not ref, no lifetime)
+  SelfParam (Analysis::NodeMapping mappings, std::unique_ptr<Type> type,
+	     bool is_mut, Location locus)
+    : self_kind (is_mut ? ImplicitSelfKind::MUT : ImplicitSelfKind::IMM),
+      lifetime (
+	Lifetime (mappings, AST::Lifetime::LifetimeType::NAMED, "", locus)),
+      type (std::move (type)), locus (locus), mappings (mappings)
+  {}
+
+  // Lifetime-based self parameter (is ref, no type)
+  SelfParam (Analysis::NodeMapping mappings, Lifetime lifetime, bool is_mut,
+	     Location locus)
+    : self_kind (is_mut ? ImplicitSelfKind::MUT_REF
+			: ImplicitSelfKind::IMM_REF),
+      lifetime (std::move (lifetime)), locus (locus), mappings (mappings)
+  {}
+
+  // Copy constructor requires clone
+  SelfParam (SelfParam const &other)
+    : self_kind (other.self_kind), lifetime (other.lifetime),
+      locus (other.locus), mappings (other.mappings)
+  {
+    if (other.type != nullptr)
+      type = other.type->clone_type ();
+  }
+
+  // Overload assignment operator to use clone
+  SelfParam &operator= (SelfParam const &other)
+  {
+    if (other.type != nullptr)
+      type = other.type->clone_type ();
+
+    self_kind = other.self_kind;
+    lifetime = other.lifetime;
+    locus = other.locus;
+    mappings = other.mappings;
+
+    return *this;
+  }
+
+  // move constructors
+  SelfParam (SelfParam &&other) = default;
+  SelfParam &operator= (SelfParam &&other) = default;
+
+  static SelfParam error ()
+  {
+    return SelfParam (Analysis::NodeMapping::get_error (),
+		      ImplicitSelfKind::NONE, Lifetime::error (), nullptr);
+  }
+
+  // Returns whether the self-param has a type field.
+  bool has_type () const { return type != nullptr; }
+
+  // Returns whether the self-param has a valid lifetime.
+  bool has_lifetime () const { return !lifetime.is_error (); }
+
+  // Returns whether the self-param is in an error state.
+  bool is_error () const { return self_kind == ImplicitSelfKind::NONE; }
+
+  std::string as_string () const;
+
+  Location get_locus () const { return locus; }
+
+  ImplicitSelfKind get_self_kind () const { return self_kind; }
+
+  std::unique_ptr<Type> &get_type ()
+  {
+    rust_assert (has_type ());
+    return type;
+  }
+
+  Analysis::NodeMapping get_mappings () { return mappings; }
+
+  Mutability get_mut () const
+  {
+    return (self_kind == ImplicitSelfKind::MUT
+	    || self_kind == ImplicitSelfKind::MUT_REF)
+	     ? Mutability::Mut
+	     : Mutability::Imm;
+  }
+
+  bool is_mut () const
+  {
+    return self_kind == ImplicitSelfKind::MUT
+	   || self_kind == ImplicitSelfKind::MUT_REF;
+  }
+
+  bool is_ref () const
+  {
+    return self_kind == ImplicitSelfKind::IMM_REF
+	   || self_kind == ImplicitSelfKind::MUT_REF;
+  }
+};
+
+// Qualifiers for function, i.e. const, unsafe, extern etc.
+struct FunctionQualifiers
+{
+private:
+  AsyncConstStatus const_status;
+  Unsafety unsafety;
+  bool has_extern;
+  ABI abi;
+
+public:
+  FunctionQualifiers (AsyncConstStatus const_status, Unsafety unsafety,
+		      bool has_extern, ABI abi)
+    : const_status (const_status), unsafety (unsafety), has_extern (has_extern),
+      abi (abi)
+  {}
+
+  std::string as_string () const;
+
+  AsyncConstStatus get_status () const { return const_status; }
+
+  bool is_const () const { return const_status == AsyncConstStatus::CONST_FN; }
+  bool is_unsafe () const { return unsafety == Unsafety::Unsafe; }
+
+  ABI get_abi () const { return abi; }
+};
+
+// A function parameter
+struct FunctionParam
+{
+  std::unique_ptr<Pattern> param_name;
+  std::unique_ptr<Type> type;
+  Location locus;
+  Analysis::NodeMapping mappings;
+
+public:
+  FunctionParam (Analysis::NodeMapping mappings,
+		 std::unique_ptr<Pattern> param_name,
+		 std::unique_ptr<Type> param_type, Location locus)
+    : param_name (std::move (param_name)), type (std::move (param_type)),
+      locus (locus), mappings (mappings)
+  {}
+
+  // Copy constructor uses clone
+  FunctionParam (FunctionParam const &other)
+    : param_name (other.param_name->clone_pattern ()),
+      type (other.type->clone_type ()), locus (other.locus),
+      mappings (other.mappings)
+  {}
+
+  // Overload assignment operator to use clone
+  FunctionParam &operator= (FunctionParam const &other)
+  {
+    param_name = other.param_name->clone_pattern ();
+    type = other.type->clone_type ();
+    locus = other.locus;
+    mappings = other.mappings;
+
+    return *this;
+  }
+
+  // move constructors
+  FunctionParam (FunctionParam &&other) = default;
+  FunctionParam &operator= (FunctionParam &&other) = default;
+
+  std::string as_string () const;
+
+  Location get_locus () const { return locus; }
+
+  Pattern *get_param_name () { return param_name.get (); }
+
+  Type *get_type () { return type.get (); }
+
+  const Analysis::NodeMapping &get_mappings () const { return mappings; }
+};
+
+// Visibility of an item
+struct Visibility
+{
+public:
+  enum VisType
+  {
+    PRIVATE,
+    PUBLIC,
+    RESTRICTED,
+    ERROR,
+  };
+
+private:
+  VisType vis_type;
+  HIR::SimplePath path;
+
+  // should this store location info?
+
+public:
+  Visibility (VisType vis_type,
+	      HIR::SimplePath path = HIR::SimplePath::create_empty ())
+    : vis_type (vis_type), path (std::move (path))
+  {}
+
+  // Returns whether visibility is in an error state.
+  bool is_error () const { return vis_type == ERROR; }
+
+  // Does the current visibility refer to a simple `pub <item>` entirely public
+  bool is_public () const { return vis_type == PUBLIC; }
+
+  // Is the current visibility public restricted to a certain path
+  bool is_restricted () const { return vis_type == RESTRICTED; }
+
+  // Creates an error visibility.
+  static Visibility create_error ()
+  {
+    return Visibility (ERROR, HIR::SimplePath::create_empty ());
+  }
+
+  VisType get_vis_type () const { return vis_type; }
+
+  const HIR::SimplePath &get_path () const
+  {
+    rust_assert (!is_error ());
+    return path;
+  }
+
+  std::string as_string () const;
+};
+
+// Item that supports visibility - abstract base class
+class VisItem : public Item
+{
+  Visibility visibility;
+
+protected:
+  // Visibility constructor
+  VisItem (Analysis::NodeMapping mappings, Visibility visibility,
+	   AST::AttrVec outer_attrs = AST::AttrVec ())
+    : Item (std::move (mappings), std::move (outer_attrs)),
+      visibility (std::move (visibility))
+  {}
+
+  // Visibility copy constructor
+  VisItem (VisItem const &other) : Item (other), visibility (other.visibility)
+  {}
+
+  // Overload assignment operator to clone
+  VisItem &operator= (VisItem const &other)
+  {
+    Item::operator= (other);
+    visibility = other.visibility;
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  VisItem (VisItem &&other) = default;
+  VisItem &operator= (VisItem &&other) = default;
+
+public:
+  using HIR::Stmt::accept_vis;
+
+  BaseKind get_hir_kind () override final { return VIS_ITEM; }
+
+  /* Does the item have some kind of public visibility (non-default
+   * visibility)? */
+  bool has_visibility () const { return !visibility.is_error (); }
+
+  virtual void accept_vis (HIRVisItemVisitor &vis) = 0;
+
+  Visibility &get_visibility () { return visibility; }
+  const Visibility &get_visibility () const { return visibility; }
+
+  std::string as_string () const override;
+};
+
+// Rust module item - abstract base class
+class Module : public VisItem
+{
+  Identifier module_name;
+  Location locus;
+  // bool has_inner_attrs;
+  AST::AttrVec inner_attrs;
+  // bool has_items;
+  std::vector<std::unique_ptr<Item>> items;
+
+public:
+  std::string as_string () const override;
+
+  // Returns whether the module has items in its body.
+  bool has_items () const { return !items.empty (); }
+
+  // Returns whether the module has any inner attributes.
+  bool has_inner_attrs () const { return !inner_attrs.empty (); }
+
+  // Full constructor
+  Module (Analysis::NodeMapping mappings, Identifier module_name,
+	  Location locus, std::vector<std::unique_ptr<Item>> items,
+	  Visibility visibility = Visibility::create_error (),
+	  AST::AttrVec inner_attrs = AST::AttrVec (),
+	  AST::AttrVec outer_attrs = AST::AttrVec ())
+    : VisItem (std::move (mappings), std::move (visibility),
+	       std::move (outer_attrs)),
+      module_name (module_name), locus (locus),
+      inner_attrs (std::move (inner_attrs)), items (std::move (items))
+  {}
+
+  // Copy constructor with vector clone
+  Module (Module const &other)
+    : VisItem (other), inner_attrs (other.inner_attrs)
+  {
+    items.reserve (other.items.size ());
+    for (const auto &e : other.items)
+      items.push_back (e->clone_item ());
+  }
+
+  // Overloaded assignment operator with vector clone
+  Module &operator= (Module const &other)
+  {
+    VisItem::operator= (other);
+    inner_attrs = other.inner_attrs;
+
+    items.reserve (other.items.size ());
+    for (const auto &e : other.items)
+      items.push_back (e->clone_item ());
+
+    return *this;
+  }
+
+  // move constructors
+  Module (Module &&other) = default;
+  Module &operator= (Module &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+  void accept_vis (HIRVisItemVisitor &vis) override;
+
+  std::vector<std::unique_ptr<Item>> &get_items () { return items; };
+
+  /* Override that runs the function recursively on all items contained within
+   * the module. */
+  void add_crate_name (std::vector<std::string> &names) const override;
+
+  Location get_locus () const override final { return locus; }
+
+  ItemKind get_item_kind () const override { return ItemKind::Module; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  Module *clone_item_impl () const override { return new Module (*this); }
+
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  /*virtual Module* clone_statement_impl() const override {
+      return new Module(*this);
+  }*/
+};
+
+// Rust extern crate declaration HIR node
+class ExternCrate : public VisItem
+{
+  // this is either an identifier or "self", with self parsed to string
+  std::string referenced_crate;
+  // bool has_as_clause;
+  // AsClause as_clause;
+  // this is either an identifier or "_", with _ parsed to string
+  std::string as_clause_name;
+
+  Location locus;
+
+  /* e.g.
+      "extern crate foo as _"
+      "extern crate foo"
+      "extern crate std as cool_std"  */
+public:
+  std::string as_string () const override;
+
+  // Returns whether extern crate declaration has an as clause.
+  bool has_as_clause () const { return !as_clause_name.empty (); }
+
+  /* Returns whether extern crate declaration references the current crate
+   * (i.e. self). */
+  bool references_self () const { return referenced_crate == "self"; }
+
+  // Constructor
+  ExternCrate (Analysis::NodeMapping mappings, std::string referenced_crate,
+	       Visibility visibility, AST::AttrVec outer_attrs, Location locus,
+	       std::string as_clause_name = std::string ())
+    : VisItem (std::move (mappings), std::move (visibility),
+	       std::move (outer_attrs)),
+      referenced_crate (std::move (referenced_crate)),
+      as_clause_name (std::move (as_clause_name)), locus (locus)
+  {}
+
+  Location get_locus () const override final { return locus; }
+
+  ItemKind get_item_kind () const override { return ItemKind::ExternCrate; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+  void accept_vis (HIRVisItemVisitor &vis) override;
+
+  // Override that adds extern crate name in decl to passed list of names.
+  void add_crate_name (std::vector<std::string> &names) const override
+  {
+    names.push_back (referenced_crate);
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  ExternCrate *clone_item_impl () const override
+  {
+    return new ExternCrate (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  /*virtual ExternCrate* clone_statement_impl() const override {
+      return new ExternCrate(*this);
+  }*/
+};
+
+// The path-ish thing referred to in a use declaration - abstract base class
+class UseTree
+{
+  Location locus;
+
+public:
+  virtual ~UseTree () {}
+
+  // Unique pointer custom clone function
+  std::unique_ptr<UseTree> clone_use_tree () const
+  {
+    return std::unique_ptr<UseTree> (clone_use_tree_impl ());
+  }
+
+  virtual std::string as_string () const = 0;
+
+  Location get_locus () const { return locus; }
+
+  virtual void accept_vis (HIRFullVisitor &vis) = 0;
+
+protected:
+  // Clone function implementation as pure virtual method
+  virtual UseTree *clone_use_tree_impl () const = 0;
+
+  UseTree (Location locus) : locus (locus) {}
+};
+
+// Use tree with a glob (wildcard) operator
+class UseTreeGlob : public UseTree
+{
+public:
+  enum PathType
+  {
+    NO_PATH,
+    GLOBAL,
+    PATH_PREFIXED
+  };
+
+private:
+  PathType glob_type;
+  AST::SimplePath path;
+
+public:
+  UseTreeGlob (PathType glob_type, AST::SimplePath path, Location locus)
+    : UseTree (locus), glob_type (glob_type), path (std::move (path))
+  {
+    if (this->glob_type != PATH_PREFIXED)
+      {
+	// compiler implementation error if there is a path with a
+	// non-path-prefixed use tree glob
+	gcc_assert (!has_path ());
+      }
+    // TODO: do path-prefixed paths also have to have a path? If so, have an
+    // assert for that too.
+  }
+
+  /* Returns whether has path. Should be made redundant by PathType
+   * PATH_PREFIXED. */
+  bool has_path () const { return !path.is_empty (); }
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  /* TODO: find way to ensure only PATH_PREFIXED glob_type has path - factory
+   * methods? */
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  UseTreeGlob *clone_use_tree_impl () const override
+  {
+    return new UseTreeGlob (*this);
+  }
+};
+
+// Use tree with a list of paths with a common prefix
+class UseTreeList : public UseTree
+{
+public:
+  enum PathType
+  {
+    NO_PATH,
+    GLOBAL,
+    PATH_PREFIXED
+  };
+
+private:
+  PathType path_type;
+  AST::SimplePath path;
+
+  std::vector<std::unique_ptr<UseTree>> trees;
+
+public:
+  UseTreeList (PathType path_type, AST::SimplePath path,
+	       std::vector<std::unique_ptr<UseTree>> trees, Location locus)
+    : UseTree (locus), path_type (path_type), path (std::move (path)),
+      trees (std::move (trees))
+  {
+    if (this->path_type != PATH_PREFIXED)
+      {
+	// compiler implementation error if there is a path with a
+	// non-path-prefixed use tree glob
+	gcc_assert (!has_path ());
+      }
+    // TODO: do path-prefixed paths also have to have a path? If so, have an
+    // assert for that too.
+  }
+
+  // copy constructor with vector clone
+  UseTreeList (UseTreeList const &other)
+    : UseTree (other), path_type (other.path_type), path (other.path)
+  {
+    trees.reserve (other.trees.size ());
+    for (const auto &e : other.trees)
+      trees.push_back (e->clone_use_tree ());
+  }
+
+  // overloaded assignment operator with vector clone
+  UseTreeList &operator= (UseTreeList const &other)
+  {
+    UseTree::operator= (other);
+    path_type = other.path_type;
+    path = other.path;
+
+    trees.reserve (other.trees.size ());
+    for (const auto &e : other.trees)
+      trees.push_back (e->clone_use_tree ());
+
+    return *this;
+  }
+
+  // move constructors
+  UseTreeList (UseTreeList &&other) = default;
+  UseTreeList &operator= (UseTreeList &&other) = default;
+
+  // Returns whether has path. Should be made redundant by path_type.
+  bool has_path () const { return !path.is_empty (); }
+
+  // Returns whether has inner tree elements.
+  bool has_trees () const { return !trees.empty (); }
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  // TODO: find way to ensure only PATH_PREFIXED path_type has path - factory
+  // methods?
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  UseTreeList *clone_use_tree_impl () const override
+  {
+    return new UseTreeList (*this);
+  }
+};
+
+// Use tree where it rebinds the module name as something else
+class UseTreeRebind : public UseTree
+{
+public:
+  enum NewBindType
+  {
+    NONE,
+    IDENTIFIER,
+    WILDCARD
+  };
+
+private:
+  AST::SimplePath path;
+
+  NewBindType bind_type;
+  Identifier identifier; // only if NewBindType is IDENTIFIER
+
+public:
+  UseTreeRebind (NewBindType bind_type, AST::SimplePath path, Location locus,
+		 Identifier identifier = std::string ())
+    : UseTree (locus), path (std::move (path)), bind_type (bind_type),
+      identifier (std::move (identifier))
+  {}
+
+  // Returns whether has path (this should always be true).
+  bool has_path () const { return !path.is_empty (); }
+
+  // Returns whether has identifier (or, rather, is allowed to).
+  bool has_identifier () const { return bind_type == IDENTIFIER; }
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  // TODO: find way to ensure only PATH_PREFIXED path_type has path - factory
+  // methods?
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  virtual UseTreeRebind *clone_use_tree_impl () const override
+  {
+    return new UseTreeRebind (*this);
+  }
+};
+
+// Rust use declaration (i.e. for modules) HIR node
+class UseDeclaration : public VisItem
+{
+  std::unique_ptr<UseTree> use_tree;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  UseDeclaration (Analysis::NodeMapping mappings,
+		  std::unique_ptr<UseTree> use_tree, Visibility visibility,
+		  AST::AttrVec outer_attrs, Location locus)
+    : VisItem (std::move (mappings), std::move (visibility),
+	       std::move (outer_attrs)),
+      use_tree (std::move (use_tree)), locus (locus)
+  {}
+
+  // Copy constructor with clone
+  UseDeclaration (UseDeclaration const &other)
+    : VisItem (other), use_tree (other.use_tree->clone_use_tree ()),
+      locus (other.locus)
+  {}
+
+  // Overloaded assignment operator to clone
+  UseDeclaration &operator= (UseDeclaration const &other)
+  {
+    VisItem::operator= (other);
+    use_tree = other.use_tree->clone_use_tree ();
+    // visibility = other.visibility->clone_visibility();
+    // outer_attrs = other.outer_attrs;
+    locus = other.locus;
+
+    return *this;
+  }
+
+  // move constructors
+  UseDeclaration (UseDeclaration &&other) = default;
+  UseDeclaration &operator= (UseDeclaration &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+  ItemKind get_item_kind () const override { return ItemKind::UseDeclaration; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+  void accept_vis (HIRVisItemVisitor &vis) override;
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  UseDeclaration *clone_item_impl () const override
+  {
+    return new UseDeclaration (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  /*virtual UseDeclaration* clone_statement_impl() const override {
+      return new UseDeclaration(*this);
+  }*/
+};
+
+class LetStmt;
+
+// Rust function declaration HIR node
+class Function : public VisItem, public ImplItem
+{
+  FunctionQualifiers qualifiers;
+  Identifier function_name;
+  std::vector<std::unique_ptr<GenericParam>> generic_params;
+  std::vector<FunctionParam> function_params;
+  std::unique_ptr<Type> return_type;
+  WhereClause where_clause;
+  std::unique_ptr<BlockExpr> function_body;
+  SelfParam self;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  // Returns whether function has generic parameters.
+  bool has_generics () const { return !generic_params.empty (); }
+
+  // Returns whether function has regular parameters.
+  bool has_function_params () const { return !function_params.empty (); }
+
+  // Returns whether function has return type - if not, it is void.
+  bool has_function_return_type () const { return return_type != nullptr; }
+
+  // Returns whether function has a where clause.
+  bool has_where_clause () const { return !where_clause.is_empty (); }
+
+  ImplItemType get_impl_item_type () const override final
+  {
+    return ImplItem::ImplItemType::FUNCTION;
+  }
+
+  ItemKind get_item_kind () const override { return ItemKind::Function; }
+
+  // Mega-constructor with all possible fields
+  Function (Analysis::NodeMapping mappings, Identifier function_name,
+	    FunctionQualifiers qualifiers,
+	    std::vector<std::unique_ptr<GenericParam>> generic_params,
+	    std::vector<FunctionParam> function_params,
+	    std::unique_ptr<Type> return_type, WhereClause where_clause,
+	    std::unique_ptr<BlockExpr> function_body, Visibility vis,
+	    AST::AttrVec outer_attrs, SelfParam self, Location locus)
+    : VisItem (std::move (mappings), std::move (vis), std::move (outer_attrs)),
+      qualifiers (std::move (qualifiers)),
+      function_name (std::move (function_name)),
+      generic_params (std::move (generic_params)),
+      function_params (std::move (function_params)),
+      return_type (std::move (return_type)),
+      where_clause (std::move (where_clause)),
+      function_body (std::move (function_body)), self (std::move (self)),
+      locus (locus)
+  {}
+
+  // Copy constructor with clone
+  Function (Function const &other)
+    : VisItem (other), qualifiers (other.qualifiers),
+      function_name (other.function_name),
+      function_params (other.function_params),
+      where_clause (other.where_clause),
+      function_body (other.function_body->clone_block_expr ()),
+      self (other.self), locus (other.locus)
+  {
+    // guard to prevent null dereference (always required)
+    if (other.return_type != nullptr)
+      return_type = other.return_type->clone_type ();
+    else
+      return_type = nullptr;
+
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+  }
+
+  // Overloaded assignment operator to clone
+  Function &operator= (Function const &other)
+  {
+    VisItem::operator= (other);
+    function_name = other.function_name;
+    qualifiers = other.qualifiers;
+    function_params = other.function_params;
+
+    // guard to prevent null dereference (always required)
+    if (other.return_type != nullptr)
+      return_type = other.return_type->clone_type ();
+    else
+      return_type = nullptr;
+
+    where_clause = other.where_clause;
+    function_body = other.function_body->clone_block_expr ();
+    locus = other.locus;
+    self = other.self;
+
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+
+    return *this;
+  }
+
+  // move constructors
+  Function (Function &&other) = default;
+  Function &operator= (Function &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRImplVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+  void accept_vis (HIRVisItemVisitor &vis) override;
+
+  Analysis::NodeMapping get_impl_mappings () const override
+  {
+    return get_mappings ();
+  };
+
+  std::vector<FunctionParam> &get_function_params () { return function_params; }
+  const std::vector<FunctionParam> &get_function_params () const
+  {
+    return function_params;
+  }
+
+  std::vector<std::unique_ptr<GenericParam>> &get_generic_params ()
+  {
+    return generic_params;
+  }
+  const std::vector<std::unique_ptr<GenericParam>> &get_generic_params () const
+  {
+    return generic_params;
+  }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<BlockExpr> &get_definition ()
+  {
+    rust_assert (function_body != nullptr);
+    return function_body;
+  }
+
+  const FunctionQualifiers &get_qualifiers () const { return qualifiers; }
+
+  Identifier get_function_name () const { return function_name; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  WhereClause &get_where_clause () { return where_clause; }
+
+  bool has_return_type () const { return return_type != nullptr; }
+
+  // TODO: is this better? Or is a "vis_block" better?
+  std::unique_ptr<Type> &get_return_type ()
+  {
+    rust_assert (has_return_type ());
+    return return_type;
+  }
+
+  bool is_method () const { return !self.is_error (); }
+
+  SelfParam &get_self_param () { return self; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  Function *clone_item_impl () const override { return new Function (*this); }
+
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  Function *clone_inherent_impl_item_impl () const override
+  {
+    return new Function (*this);
+  }
+};
+
+// Rust type alias (i.e. typedef) HIR node
+class TypeAlias : public VisItem, public ImplItem
+{
+  Identifier new_type_name;
+
+  // bool has_generics;
+  // Generics generic_params;
+  std::vector<std::unique_ptr<GenericParam>> generic_params; // inlined
+
+  // bool has_where_clause;
+  WhereClause where_clause;
+
+  std::unique_ptr<Type> existing_type;
+
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  // Returns whether type alias has generic parameters.
+  bool has_generics () const { return !generic_params.empty (); }
+
+  // Returns whether type alias has a where clause.
+  bool has_where_clause () const { return !where_clause.is_empty (); }
+
+  ImplItemType get_impl_item_type () const override final
+  {
+    return ImplItem::ImplItemType::TYPE_ALIAS;
+  }
+
+  // Mega-constructor with all possible fields
+  TypeAlias (Analysis::NodeMapping mappings, Identifier new_type_name,
+	     std::vector<std::unique_ptr<GenericParam>> generic_params,
+	     WhereClause where_clause, std::unique_ptr<Type> existing_type,
+	     Visibility vis, AST::AttrVec outer_attrs, Location locus)
+    : VisItem (std::move (mappings), std::move (vis), std::move (outer_attrs)),
+      new_type_name (std::move (new_type_name)),
+      generic_params (std::move (generic_params)),
+      where_clause (std::move (where_clause)),
+      existing_type (std::move (existing_type)), locus (locus)
+  {}
+
+  // Copy constructor
+  TypeAlias (TypeAlias const &other)
+    : VisItem (other), new_type_name (other.new_type_name),
+      where_clause (other.where_clause),
+      existing_type (other.existing_type->clone_type ()), locus (other.locus)
+  {
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+  }
+
+  // Overloaded assignment operator to clone
+  TypeAlias &operator= (TypeAlias const &other)
+  {
+    VisItem::operator= (other);
+    new_type_name = other.new_type_name;
+    where_clause = other.where_clause;
+    existing_type = other.existing_type->clone_type ();
+    locus = other.locus;
+
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+
+    return *this;
+  }
+
+  // move constructors
+  TypeAlias (TypeAlias &&other) = default;
+  TypeAlias &operator= (TypeAlias &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRImplVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+  void accept_vis (HIRVisItemVisitor &vis) override;
+
+  std::vector<std::unique_ptr<GenericParam>> &get_generic_params ()
+  {
+    return generic_params;
+  }
+  const std::vector<std::unique_ptr<GenericParam>> &get_generic_params () const
+  {
+    return generic_params;
+  }
+
+  WhereClause &get_where_clause () { return where_clause; }
+
+  std::unique_ptr<Type> &get_type_aliased ()
+  {
+    rust_assert (existing_type != nullptr);
+    return existing_type;
+  }
+
+  Identifier get_new_type_name () const { return new_type_name; }
+
+  ItemKind get_item_kind () const override { return ItemKind::TypeAlias; }
+
+  Analysis::NodeMapping get_impl_mappings () const override
+  {
+    return get_mappings ();
+  };
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  TypeAlias *clone_item_impl () const override { return new TypeAlias (*this); }
+
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  TypeAlias *clone_inherent_impl_item_impl () const override
+  {
+    return new TypeAlias (*this);
+  }
+};
+
+// Rust base struct declaration HIR node - abstract base class
+class Struct : public VisItem
+{
+protected:
+  // protected to enable access by derived classes - allows better as_string
+  Identifier struct_name;
+
+  // bool has_generics;
+  // Generics generic_params;
+  std::vector<std::unique_ptr<GenericParam>> generic_params; // inlined
+
+  // bool has_where_clause;
+  WhereClause where_clause;
+
+  Location locus;
+
+public:
+  Identifier get_identifier () const { return struct_name; }
+
+  // Returns whether struct has generic parameters.
+  bool has_generics () const { return !generic_params.empty (); }
+
+  // Returns whether struct has a where clause.
+  bool has_where_clause () const { return !where_clause.is_empty (); }
+
+  Location get_locus () const override final { return locus; }
+  ItemKind get_item_kind () const override { return ItemKind::Struct; }
+
+  std::vector<std::unique_ptr<GenericParam>> &get_generic_params ()
+  {
+    return generic_params;
+  }
+
+  WhereClause &get_where_clause () { return where_clause; }
+
+protected:
+  Struct (Analysis::NodeMapping mappings, Identifier struct_name,
+	  std::vector<std::unique_ptr<GenericParam>> generic_params,
+	  WhereClause where_clause, Visibility vis, Location locus,
+	  AST::AttrVec outer_attrs = AST::AttrVec ())
+    : VisItem (std::move (mappings), std::move (vis), std::move (outer_attrs)),
+      struct_name (std::move (struct_name)),
+      generic_params (std::move (generic_params)),
+      where_clause (std::move (where_clause)), locus (locus)
+  {}
+
+  // Copy constructor with vector clone
+  Struct (Struct const &other)
+    : VisItem (other), struct_name (other.struct_name),
+      where_clause (other.where_clause), locus (other.locus)
+  {
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+  }
+
+  // Overloaded assignment operator with vector clone
+  Struct &operator= (Struct const &other)
+  {
+    VisItem::operator= (other);
+    struct_name = other.struct_name;
+    where_clause = other.where_clause;
+    locus = other.locus;
+
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+
+    return *this;
+  }
+
+  // move constructors
+  Struct (Struct &&other) = default;
+  Struct &operator= (Struct &&other) = default;
+};
+
+// A single field in a struct
+struct StructField
+{
+public:
+  // bool has_outer_attributes;
+  AST::AttrVec outer_attrs;
+
+  // bool has_visibility;
+  Visibility visibility;
+
+  Identifier field_name;
+  std::unique_ptr<Type> field_type;
+
+  Analysis::NodeMapping mappings;
+
+  Location locus;
+
+  // Returns whether struct field has any outer attributes.
+  bool has_outer_attributes () const { return !outer_attrs.empty (); }
+
+  // Returns whether struct field has a non-private (non-default) visibility.
+  bool has_visibility () const { return !visibility.is_error (); }
+
+  StructField (Analysis::NodeMapping mappings, Identifier field_name,
+	       std::unique_ptr<Type> field_type, Visibility vis, Location locus,
+	       AST::AttrVec outer_attrs = AST::AttrVec ())
+    : outer_attrs (std::move (outer_attrs)), visibility (std::move (vis)),
+      field_name (std::move (field_name)), field_type (std::move (field_type)),
+      mappings (mappings), locus (locus)
+  {}
+
+  // Copy constructor
+  StructField (StructField const &other)
+    : outer_attrs (other.outer_attrs), visibility (other.visibility),
+      field_name (other.field_name),
+      field_type (other.field_type->clone_type ()), mappings (other.mappings)
+  {}
+
+  ~StructField () = default;
+
+  // Overloaded assignment operator to clone
+  StructField &operator= (StructField const &other)
+  {
+    field_name = other.field_name;
+    field_type = other.field_type->clone_type ();
+    visibility = other.visibility;
+    outer_attrs = other.outer_attrs;
+    mappings = other.mappings;
+
+    return *this;
+  }
+
+  // move constructors
+  StructField (StructField &&other) = default;
+  StructField &operator= (StructField &&other) = default;
+
+  std::string as_string () const;
+
+  Identifier get_field_name () const { return field_name; }
+
+  std::unique_ptr<Type> &get_field_type ()
+  {
+    rust_assert (field_type != nullptr);
+    return field_type;
+  }
+
+  Analysis::NodeMapping get_mappings () const { return mappings; }
+
+  Location get_locus () { return locus; }
+
+  Visibility &get_visibility () { return visibility; }
+};
+
+// Rust struct declaration with true struct type HIR node
+class StructStruct : public Struct
+{
+public:
+  std::vector<StructField> fields;
+  bool is_unit;
+
+  std::string as_string () const override;
+
+  // Mega-constructor with all possible fields
+  StructStruct (Analysis::NodeMapping mappings, std::vector<StructField> fields,
+		Identifier struct_name,
+		std::vector<std::unique_ptr<GenericParam>> generic_params,
+		WhereClause where_clause, bool is_unit, Visibility vis,
+		AST::AttrVec outer_attrs, Location locus)
+    : Struct (std::move (mappings), std::move (struct_name),
+	      std::move (generic_params), std::move (where_clause),
+	      std::move (vis), locus, std::move (outer_attrs)),
+      fields (std::move (fields)), is_unit (is_unit)
+  {}
+
+  // Unit struct constructor
+  StructStruct (Analysis::NodeMapping mappings, Identifier struct_name,
+		std::vector<std::unique_ptr<GenericParam>> generic_params,
+		WhereClause where_clause, Visibility vis,
+		AST::AttrVec outer_attrs, Location locus)
+    : Struct (std::move (mappings), std::move (struct_name),
+	      std::move (generic_params), std::move (where_clause),
+	      std::move (vis), locus, std::move (outer_attrs)),
+      is_unit (true)
+  {}
+  // TODO: can a unit struct have generic fields? assuming yes for now.
+
+  /* Returns whether the struct is a unit struct - struct defined without
+   * fields. This is important because it also means an implicit constant of its
+   * type is defined. */
+  bool is_unit_struct () const { return is_unit; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+  void accept_vis (HIRVisItemVisitor &vis) override;
+
+  std::vector<StructField> &get_fields () { return fields; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  StructStruct *clone_item_impl () const override
+  {
+    return new StructStruct (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  /*virtual StructStruct* clone_statement_impl() const override {
+      return new StructStruct(*this);
+  }*/
+};
+
+// A single field in a tuple
+struct TupleField
+{
+private:
+  // bool has_outer_attributes;
+  AST::AttrVec outer_attrs;
+
+  // bool has_visibility;
+  Visibility visibility;
+
+  std::unique_ptr<Type> field_type;
+
+  Location locus;
+
+  Analysis::NodeMapping mappings;
+
+public:
+  // Returns whether tuple field has outer attributes.
+  bool has_outer_attributes () const { return !outer_attrs.empty (); }
+
+  /* Returns whether tuple field has a non-default visibility (i.e. a public
+   * one) */
+  bool has_visibility () const { return !visibility.is_error (); }
+
+  // Complete constructor
+  TupleField (Analysis::NodeMapping mapping, std::unique_ptr<Type> field_type,
+	      Visibility vis, Location locus,
+	      AST::AttrVec outer_attrs = AST::AttrVec ())
+    : outer_attrs (std::move (outer_attrs)), visibility (std::move (vis)),
+      field_type (std::move (field_type)), locus (locus), mappings (mapping)
+  {}
+
+  // Copy constructor with clone
+  TupleField (TupleField const &other)
+    : outer_attrs (other.outer_attrs), visibility (other.visibility),
+      field_type (other.field_type->clone_type ()), locus (other.locus),
+      mappings (other.mappings)
+  {}
+
+  ~TupleField () = default;
+
+  // Overloaded assignment operator to clone
+  TupleField &operator= (TupleField const &other)
+  {
+    field_type = other.field_type->clone_type ();
+    visibility = other.visibility;
+    outer_attrs = other.outer_attrs;
+    locus = other.locus;
+    mappings = other.mappings;
+
+    return *this;
+  }
+
+  // move constructors
+  TupleField (TupleField &&other) = default;
+  TupleField &operator= (TupleField &&other) = default;
+
+  // Returns whether tuple field is in an error state.
+  bool is_error () const { return field_type == nullptr; }
+
+  std::string as_string () const;
+
+  Analysis::NodeMapping get_mappings () const { return mappings; }
+
+  Location get_locus () const { return locus; }
+
+  std::unique_ptr<HIR::Type> &get_field_type () { return field_type; }
+};
+
+// Rust tuple declared using struct keyword HIR node
+class TupleStruct : public Struct
+{
+  std::vector<TupleField> fields;
+
+public:
+  std::string as_string () const override;
+
+  // Mega-constructor with all possible fields
+  TupleStruct (Analysis::NodeMapping mappings, std::vector<TupleField> fields,
+	       Identifier struct_name,
+	       std::vector<std::unique_ptr<GenericParam>> generic_params,
+	       WhereClause where_clause, Visibility vis,
+	       AST::AttrVec outer_attrs, Location locus)
+    : Struct (std::move (mappings), std::move (struct_name),
+	      std::move (generic_params), std::move (where_clause),
+	      std::move (vis), locus, std::move (outer_attrs)),
+      fields (std::move (fields))
+  {}
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+  void accept_vis (HIRVisItemVisitor &vis) override;
+
+  std::vector<TupleField> &get_fields () { return fields; }
+  const std::vector<TupleField> &get_fields () const { return fields; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  TupleStruct *clone_item_impl () const override
+  {
+    return new TupleStruct (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  /*virtual TupleStruct* clone_statement_impl() const override {
+      return new TupleStruct(*this);
+  }*/
+};
+
+/* An item used in an "enum" tagged union - not abstract: base represents a
+   name-only enum. Syntactically EnumItem's can have a Visibility. But not
+   Semantically. So check there is no Visibility when lowering and make this
+   an Item, not an VisItem.  */
+class EnumItem : public Item
+{
+  Identifier variant_name;
+  Location locus;
+
+public:
+  virtual ~EnumItem () {}
+
+  enum EnumItemKind
+  {
+    Named,
+    Tuple,
+    Struct,
+    Discriminant,
+  };
+
+  EnumItem (Analysis::NodeMapping mappings, Identifier variant_name,
+	    AST::AttrVec outer_attrs, Location locus)
+    : Item (std::move (mappings), std::move (outer_attrs)),
+      variant_name (std::move (variant_name)), locus (locus)
+  {}
+
+  // Unique pointer custom clone function
+  std::unique_ptr<EnumItem> clone_enum_item () const
+  {
+    return std::unique_ptr<EnumItem> (clone_item_impl ());
+  }
+
+  virtual std::string as_string () const override;
+  virtual EnumItemKind get_enum_item_kind () const { return Named; };
+
+  // not pure virtual as not abstract
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+  // void accept_vis (HIRVisItemVisitor &vis) override;
+
+  Location get_locus () const override { return locus; }
+
+  Identifier get_identifier () const { return variant_name; }
+
+  ItemKind get_item_kind () const override { return ItemKind::EnumItem; }
+
+protected:
+  EnumItem *clone_item_impl () const override { return new EnumItem (*this); }
+};
+
+// A tuple item used in an "enum" tagged union
+class EnumItemTuple : public EnumItem
+{
+  // bool has_tuple_fields;
+  std::vector<TupleField> tuple_fields;
+
+public:
+  // Returns whether tuple enum item has tuple fields.
+  bool has_tuple_fields () const { return !tuple_fields.empty (); }
+
+  EnumItemKind get_enum_item_kind () const override
+  {
+    return EnumItemKind::Tuple;
+  }
+
+  EnumItemTuple (Analysis::NodeMapping mappings, Identifier variant_name,
+		 std::vector<TupleField> tuple_fields, AST::AttrVec outer_attrs,
+		 Location locus)
+    : EnumItem (std::move (mappings), std::move (variant_name),
+		std::move (outer_attrs), locus),
+      tuple_fields (std::move (tuple_fields))
+  {}
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+
+  std::vector<TupleField> &get_tuple_fields () { return tuple_fields; }
+
+protected:
+  // Clone function implementation as (not pure) virtual method
+  EnumItemTuple *clone_item_impl () const override
+  {
+    return new EnumItemTuple (*this);
+  }
+};
+
+// A struct item used in an "enum" tagged union
+class EnumItemStruct : public EnumItem
+{
+  // bool has_struct_fields;
+  std::vector<StructField> struct_fields;
+
+public:
+  // Returns whether struct enum item has struct fields.
+  bool has_struct_fields () const { return !struct_fields.empty (); }
+
+  EnumItemKind get_enum_item_kind () const override
+  {
+    return EnumItemKind::Struct;
+  }
+
+  EnumItemStruct (Analysis::NodeMapping mappings, Identifier variant_name,
+		  std::vector<StructField> struct_fields,
+		  AST::AttrVec outer_attrs, Location locus)
+    : EnumItem (std::move (mappings), std::move (variant_name),
+		std::move (outer_attrs), locus),
+      struct_fields (std::move (struct_fields))
+  {}
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+
+  std::vector<StructField> &get_struct_fields () { return struct_fields; }
+
+protected:
+  // Clone function implementation as (not pure) virtual method
+  EnumItemStruct *clone_item_impl () const override
+  {
+    return new EnumItemStruct (*this);
+  }
+};
+
+// A discriminant (numbered enum) item used in an "enum" tagged union
+class EnumItemDiscriminant : public EnumItem
+{
+  std::unique_ptr<Expr> expression;
+
+public:
+  EnumItemDiscriminant (Analysis::NodeMapping mappings, Identifier variant_name,
+			std::unique_ptr<Expr> expr, AST::AttrVec outer_attrs,
+			Location locus)
+    : EnumItem (std::move (mappings), std::move (variant_name),
+		std::move (outer_attrs), locus),
+      expression (std::move (expr))
+  {}
+
+  // Copy constructor with clone
+  EnumItemDiscriminant (EnumItemDiscriminant const &other)
+    : EnumItem (other), expression (other.expression->clone_expr ())
+  {}
+
+  // Overloaded assignment operator to clone
+  EnumItemDiscriminant &operator= (EnumItemDiscriminant const &other)
+  {
+    EnumItem::operator= (other);
+    expression = other.expression->clone_expr ();
+    // variant_name = other.variant_name;
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // move constructors
+  EnumItemDiscriminant (EnumItemDiscriminant &&other) = default;
+  EnumItemDiscriminant &operator= (EnumItemDiscriminant &&other) = default;
+
+  EnumItemKind get_enum_item_kind () const override
+  {
+    return EnumItemKind::Discriminant;
+  }
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+
+  std::unique_ptr<Expr> &get_discriminant_expression () { return expression; }
+
+protected:
+  // Clone function implementation as (not pure) virtual method
+  EnumItemDiscriminant *clone_item_impl () const override
+  {
+    return new EnumItemDiscriminant (*this);
+  }
+};
+
+// HIR node for Rust "enum" - tagged union
+class Enum : public VisItem
+{
+  Identifier enum_name;
+
+  // bool has_generics;
+  // Generics generic_params;
+  std::vector<std::unique_ptr<GenericParam>> generic_params; // inlined
+
+  // bool has_where_clause;
+  WhereClause where_clause;
+
+  std::vector<std::unique_ptr<EnumItem>> items;
+
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  // Returns whether "enum" has generic parameters.
+  bool has_generics () const { return !generic_params.empty (); }
+
+  // Returns whether "enum" has a where clause.
+  bool has_where_clause () const { return !where_clause.is_empty (); }
+
+  /* Returns whether enum is a "zero-variant" (no possible variant) enum,
+   * which cannot be instantiated. */
+  bool is_zero_variant () const { return items.empty (); }
+
+  // Mega-constructor
+  Enum (Analysis::NodeMapping mappings, Identifier enum_name, Visibility vis,
+	std::vector<std::unique_ptr<GenericParam>> generic_params,
+	WhereClause where_clause, std::vector<std::unique_ptr<EnumItem>> items,
+	AST::AttrVec outer_attrs, Location locus)
+    : VisItem (std::move (mappings), std::move (vis), std::move (outer_attrs)),
+      enum_name (std::move (enum_name)),
+      generic_params (std::move (generic_params)),
+      where_clause (std::move (where_clause)), items (std::move (items)),
+      locus (locus)
+  {}
+
+  // TODO: constructor with less arguments
+
+  // Copy constructor with vector clone
+  Enum (Enum const &other)
+    : VisItem (other), enum_name (other.enum_name),
+      where_clause (other.where_clause), locus (other.locus)
+  {
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+
+    items.reserve (other.items.size ());
+    for (const auto &e : other.items)
+      items.push_back (e->clone_enum_item ());
+  }
+
+  // Overloaded assignment operator with vector clone
+  Enum &operator= (Enum const &other)
+  {
+    VisItem::operator= (other);
+    enum_name = other.enum_name;
+    where_clause = other.where_clause;
+    locus = other.locus;
+
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+
+    items.reserve (other.items.size ());
+    for (const auto &e : other.items)
+      items.push_back (e->clone_enum_item ());
+
+    return *this;
+  }
+
+  // Move constructors
+  Enum (Enum &&other) = default;
+  Enum &operator= (Enum &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+  void accept_vis (HIRVisItemVisitor &vis) override;
+
+  Identifier get_identifier () const { return enum_name; }
+  ItemKind get_item_kind () const override { return ItemKind::Enum; }
+
+  std::vector<std::unique_ptr<GenericParam>> &get_generic_params ()
+  {
+    return generic_params;
+  }
+
+  const std::vector<std::unique_ptr<EnumItem>> &get_variants () const
+  {
+    return items;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  Enum *clone_item_impl () const override { return new Enum (*this); }
+
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  /*virtual Enum* clone_statement_impl() const override {
+      return new Enum(*this);
+  }*/
+};
+
+// Rust untagged union used for C compat HIR node
+class Union : public VisItem
+{
+  Identifier union_name;
+
+  // bool has_generics;
+  // Generics generic_params;
+  std::vector<std::unique_ptr<GenericParam>> generic_params; // inlined
+
+  // bool has_where_clause;
+  WhereClause where_clause;
+
+  std::vector<StructField> variants;
+
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  // Returns whether union has generic params.
+  bool has_generics () const { return !generic_params.empty (); }
+
+  // Returns whether union has where clause.
+  bool has_where_clause () const { return !where_clause.is_empty (); }
+
+  Union (Analysis::NodeMapping mappings, Identifier union_name, Visibility vis,
+	 std::vector<std::unique_ptr<GenericParam>> generic_params,
+	 WhereClause where_clause, std::vector<StructField> variants,
+	 AST::AttrVec outer_attrs, Location locus)
+    : VisItem (std::move (mappings), std::move (vis), std::move (outer_attrs)),
+      union_name (std::move (union_name)),
+      generic_params (std::move (generic_params)),
+      where_clause (std::move (where_clause)), variants (std::move (variants)),
+      locus (locus)
+  {}
+
+  // copy constructor with vector clone
+  Union (Union const &other)
+    : VisItem (other), union_name (other.union_name),
+      where_clause (other.where_clause), variants (other.variants),
+      locus (other.locus)
+  {
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+  }
+
+  // overloaded assignment operator with vector clone
+  Union &operator= (Union const &other)
+  {
+    VisItem::operator= (other);
+    union_name = other.union_name;
+    where_clause = other.where_clause;
+    variants = other.variants;
+    locus = other.locus;
+
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+
+    return *this;
+  }
+
+  // move constructors
+  Union (Union &&other) = default;
+  Union &operator= (Union &&other) = default;
+
+  std::vector<std::unique_ptr<GenericParam>> &get_generic_params ()
+  {
+    return generic_params;
+  }
+
+  Identifier get_identifier () const { return union_name; }
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+  void accept_vis (HIRVisItemVisitor &vis) override;
+
+  std::vector<StructField> &get_variants () { return variants; }
+
+  WhereClause &get_where_clause () { return where_clause; }
+
+  ItemKind get_item_kind () const override { return ItemKind::Union; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  Union *clone_item_impl () const override { return new Union (*this); }
+};
+
+class ConstantItem : public VisItem, public ImplItem
+{
+  Identifier identifier;
+  std::unique_ptr<Type> type;
+  std::unique_ptr<Expr> const_expr;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  ConstantItem (Analysis::NodeMapping mappings, Identifier ident,
+		Visibility vis, std::unique_ptr<Type> type,
+		std::unique_ptr<Expr> const_expr, AST::AttrVec outer_attrs,
+		Location locus)
+    : VisItem (std::move (mappings), std::move (vis), std::move (outer_attrs)),
+      identifier (std::move (ident)), type (std::move (type)),
+      const_expr (std::move (const_expr)), locus (locus)
+  {}
+
+  ConstantItem (ConstantItem const &other)
+    : VisItem (other), identifier (other.identifier),
+      type (other.type->clone_type ()),
+      const_expr (other.const_expr->clone_expr ()), locus (other.locus)
+  {}
+
+  // Overload assignment operator to clone
+  ConstantItem &operator= (ConstantItem const &other)
+  {
+    VisItem::operator= (other);
+    identifier = other.identifier;
+    type = other.type->clone_type ();
+    const_expr = other.const_expr->clone_expr ();
+    locus = other.locus;
+
+    return *this;
+  }
+
+  // move constructors
+  ConstantItem (ConstantItem &&other) = default;
+  ConstantItem &operator= (ConstantItem &&other) = default;
+
+  // Returns whether constant item is an "unnamed" (wildcard underscore used
+  // as identifier) constant.
+  bool is_unnamed () const { return identifier == std::string ("_"); }
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+  void accept_vis (HIRImplVisitor &vis) override;
+  void accept_vis (HIRVisItemVisitor &vis) override;
+
+  Type *get_type () { return type.get (); }
+
+  Expr *get_expr () { return const_expr.get (); }
+
+  std::string get_identifier () { return identifier; }
+
+  Analysis::NodeMapping get_impl_mappings () const override
+  {
+    return get_mappings ();
+  };
+
+  ImplItemType get_impl_item_type () const override final
+  {
+    return ImplItem::ImplItemType::CONSTANT;
+  }
+
+  ItemKind get_item_kind () const override { return ItemKind::Constant; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  ConstantItem *clone_item_impl () const override
+  {
+    return new ConstantItem (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  ConstantItem *clone_inherent_impl_item_impl () const override
+  {
+    return new ConstantItem (*this);
+  }
+};
+
+/* Static item HIR node - items within module scope with fixed storage
+ * duration? */
+class StaticItem : public VisItem
+{
+  Mutability mut;
+  Identifier name;
+  std::unique_ptr<Type> type;
+  std::unique_ptr<Expr> expr;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  StaticItem (Analysis::NodeMapping mappings, Identifier name, Mutability mut,
+	      std::unique_ptr<Type> type, std::unique_ptr<Expr> expr,
+	      Visibility vis, AST::AttrVec outer_attrs, Location locus)
+    : VisItem (std::move (mappings), std::move (vis), std::move (outer_attrs)),
+      mut (mut), name (std::move (name)), type (std::move (type)),
+      expr (std::move (expr)), locus (locus)
+  {}
+
+  // Copy constructor with clone
+  StaticItem (StaticItem const &other)
+    : VisItem (other), mut (other.mut), name (other.name),
+      type (other.type->clone_type ()), expr (other.expr->clone_expr ()),
+      locus (other.locus)
+  {}
+
+  // Overloaded assignment operator to clone
+  StaticItem &operator= (StaticItem const &other)
+  {
+    VisItem::operator= (other);
+    name = other.name;
+    mut = other.mut;
+    type = other.type->clone_type ();
+    expr = other.expr->clone_expr ();
+    locus = other.locus;
+
+    return *this;
+  }
+
+  // move constructors
+  StaticItem (StaticItem &&other) = default;
+  StaticItem &operator= (StaticItem &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+  void accept_vis (HIRVisItemVisitor &vis) override;
+
+  Identifier get_identifier () const { return name; }
+
+  Mutability get_mut () const { return mut; }
+
+  bool is_mut () const { return mut == Mutability::Mut; }
+
+  Expr *get_expr () { return expr.get (); }
+
+  Type *get_type () { return type.get (); }
+
+  ItemKind get_item_kind () const override { return ItemKind::Static; }
+
+protected:
+  StaticItem *clone_item_impl () const override
+  {
+    return new StaticItem (*this);
+  }
+};
+
+// Function declaration in traits
+struct TraitFunctionDecl
+{
+private:
+  FunctionQualifiers qualifiers;
+  Identifier function_name;
+  std::vector<std::unique_ptr<GenericParam>> generic_params;
+  std::vector<FunctionParam> function_params;
+  std::unique_ptr<Type> return_type;
+  WhereClause where_clause;
+  SelfParam self;
+
+public:
+  // Mega-constructor
+  TraitFunctionDecl (Identifier function_name, FunctionQualifiers qualifiers,
+		     std::vector<std::unique_ptr<GenericParam>> generic_params,
+		     SelfParam self, std::vector<FunctionParam> function_params,
+		     std::unique_ptr<Type> return_type,
+		     WhereClause where_clause)
+    : qualifiers (std::move (qualifiers)),
+      function_name (std::move (function_name)),
+      generic_params (std::move (generic_params)),
+      function_params (std::move (function_params)),
+      return_type (std::move (return_type)),
+      where_clause (std::move (where_clause)), self (std::move (self))
+  {}
+
+  // Copy constructor with clone
+  TraitFunctionDecl (TraitFunctionDecl const &other)
+    : qualifiers (other.qualifiers), function_name (other.function_name),
+      function_params (other.function_params),
+      return_type (other.return_type->clone_type ()),
+      where_clause (other.where_clause), self (other.self)
+  {
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+  }
+
+  ~TraitFunctionDecl () = default;
+
+  // Overloaded assignment operator with clone
+  TraitFunctionDecl &operator= (TraitFunctionDecl const &other)
+  {
+    function_name = other.function_name;
+    qualifiers = other.qualifiers;
+    function_params = other.function_params;
+    return_type = other.return_type->clone_type ();
+    where_clause = other.where_clause;
+    self = other.self;
+
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+
+    return *this;
+  }
+
+  // move constructors
+  TraitFunctionDecl (TraitFunctionDecl &&other) = default;
+  TraitFunctionDecl &operator= (TraitFunctionDecl &&other) = default;
+
+  std::string as_string () const;
+
+  // Returns whether function decl has generic parameters.
+  bool has_generics () const { return !generic_params.empty (); }
+
+  // Returns whether function decl has regular parameters.
+  bool has_params () const { return !function_params.empty (); }
+
+  // Returns whether function has return type (otherwise is void).
+  bool has_return_type () const { return return_type != nullptr; }
+
+  // Returns whether function has a where clause.
+  bool has_where_clause () const { return !where_clause.is_empty (); }
+
+  bool is_method () const { return !self.is_error (); }
+
+  SelfParam &get_self () { return self; }
+
+  Identifier get_function_name () const { return function_name; }
+
+  std::vector<std::unique_ptr<GenericParam>> &get_generic_params ()
+  {
+    return generic_params;
+  }
+
+  std::unique_ptr<Type> &get_return_type ()
+  {
+    rust_assert (has_return_type ());
+    return return_type;
+  }
+
+  std::vector<FunctionParam> &get_function_params () { return function_params; }
+
+  const FunctionQualifiers &get_qualifiers () const { return qualifiers; }
+};
+
+// Actual trait item function declaration within traits
+class TraitItemFunc : public TraitItem
+{
+  AST::AttrVec outer_attrs;
+  TraitFunctionDecl decl;
+  std::unique_ptr<BlockExpr> block_expr;
+  Location locus;
+
+public:
+  // Returns whether function has a definition or is just a declaration.
+  bool has_definition () const { return block_expr != nullptr; }
+
+  TraitItemFunc (Analysis::NodeMapping mappings, TraitFunctionDecl decl,
+		 std::unique_ptr<BlockExpr> block_expr,
+		 AST::AttrVec outer_attrs, Location locus)
+    : TraitItem (mappings), outer_attrs (std::move (outer_attrs)),
+      decl (std::move (decl)), block_expr (std::move (block_expr)),
+      locus (locus)
+  {}
+
+  // Copy constructor with clone
+  TraitItemFunc (TraitItemFunc const &other)
+    : TraitItem (other.mappings), outer_attrs (other.outer_attrs),
+      decl (other.decl), locus (other.locus)
+  {
+    if (other.block_expr != nullptr)
+      block_expr = other.block_expr->clone_block_expr ();
+  }
+
+  // Overloaded assignment operator to clone
+  TraitItemFunc &operator= (TraitItemFunc const &other)
+  {
+    TraitItem::operator= (other);
+    outer_attrs = other.outer_attrs;
+    decl = other.decl;
+    locus = other.locus;
+    mappings = other.mappings;
+    if (other.block_expr != nullptr)
+      block_expr = other.block_expr->clone_block_expr ();
+
+    return *this;
+  }
+
+  // move constructors
+  TraitItemFunc (TraitItemFunc &&other) = default;
+  TraitItemFunc &operator= (TraitItemFunc &&other) = default;
+
+  std::string as_string () const override;
+
+  Location get_locus () const { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRTraitItemVisitor &vis) override;
+
+  TraitFunctionDecl &get_decl () { return decl; }
+
+  const TraitFunctionDecl &get_decl () const { return decl; }
+
+  bool has_block_defined () const { return block_expr != nullptr; }
+
+  std::unique_ptr<BlockExpr> &get_block_expr ()
+  {
+    rust_assert (has_block_defined ());
+    return block_expr;
+  }
+
+  const std::string trait_identifier () const override final
+  {
+    return decl.get_function_name ();
+  }
+
+  TraitItemKind get_item_kind () const override final
+  {
+    return TraitItemKind::FUNC;
+  }
+
+  AST::AttrVec &get_outer_attrs () override final { return outer_attrs; }
+  const AST::AttrVec &get_outer_attrs () const override final
+  {
+    return outer_attrs;
+  }
+
+protected:
+  // Clone function implementation as (not pure) virtual method
+  TraitItemFunc *clone_trait_item_impl () const override
+  {
+    return new TraitItemFunc (*this);
+  }
+};
+
+// Constant item within traits
+class TraitItemConst : public TraitItem
+{
+  AST::AttrVec outer_attrs;
+  Identifier name;
+  std::unique_ptr<Type> type;
+  std::unique_ptr<Expr> expr;
+  Location locus;
+
+public:
+  // Whether the constant item has an associated expression.
+  bool has_expression () const { return expr != nullptr; }
+
+  TraitItemConst (Analysis::NodeMapping mappings, Identifier name,
+		  std::unique_ptr<Type> type, std::unique_ptr<Expr> expr,
+		  AST::AttrVec outer_attrs, Location locus)
+    : TraitItem (mappings), outer_attrs (std::move (outer_attrs)),
+      name (std::move (name)), type (std::move (type)), expr (std::move (expr)),
+      locus (locus)
+  {}
+
+  // Copy constructor with clones
+  TraitItemConst (TraitItemConst const &other)
+    : TraitItem (other.mappings), outer_attrs (other.outer_attrs),
+      name (other.name), type (other.type->clone_type ()),
+      expr (other.expr->clone_expr ()), locus (other.locus)
+  {}
+
+  // Overloaded assignment operator to clone
+  TraitItemConst &operator= (TraitItemConst const &other)
+  {
+    TraitItem::operator= (other);
+    outer_attrs = other.outer_attrs;
+    name = other.name;
+    type = other.type->clone_type ();
+    expr = other.expr->clone_expr ();
+    locus = other.locus;
+    mappings = other.mappings;
+
+    return *this;
+  }
+
+  // move constructors
+  TraitItemConst (TraitItemConst &&other) = default;
+  TraitItemConst &operator= (TraitItemConst &&other) = default;
+
+  std::string as_string () const override;
+
+  Location get_locus () const { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRTraitItemVisitor &vis) override;
+
+  Identifier get_name () const { return name; }
+
+  bool has_expr () const { return expr != nullptr; }
+
+  std::unique_ptr<Type> &get_type () { return type; }
+
+  std::unique_ptr<Expr> &get_expr ()
+  {
+    rust_assert (has_expr ());
+    return expr;
+  }
+
+  const std::string trait_identifier () const override final { return name; }
+
+  TraitItemKind get_item_kind () const override final
+  {
+    return TraitItemKind::CONST;
+  }
+
+  AST::AttrVec &get_outer_attrs () override final { return outer_attrs; }
+  const AST::AttrVec &get_outer_attrs () const override final
+  {
+    return outer_attrs;
+  }
+
+protected:
+  // Clone function implementation as (not pure) virtual method
+  TraitItemConst *clone_trait_item_impl () const override
+  {
+    return new TraitItemConst (*this);
+  }
+};
+
+// Type items within traits
+class TraitItemType : public TraitItem
+{
+  AST::AttrVec outer_attrs;
+
+  Identifier name;
+  std::vector<std::unique_ptr<TypeParamBound>>
+    type_param_bounds; // inlined form
+  Location locus;
+
+public:
+  // Returns whether trait item type has type param bounds.
+  bool has_type_param_bounds () const { return !type_param_bounds.empty (); }
+
+  TraitItemType (Analysis::NodeMapping mappings, Identifier name,
+		 std::vector<std::unique_ptr<TypeParamBound>> type_param_bounds,
+		 AST::AttrVec outer_attrs, Location locus)
+    : TraitItem (mappings), outer_attrs (std::move (outer_attrs)),
+      name (std::move (name)),
+      type_param_bounds (std::move (type_param_bounds)), locus (locus)
+  {}
+
+  // Copy constructor with vector clone
+  TraitItemType (TraitItemType const &other)
+    : TraitItem (other.mappings), outer_attrs (other.outer_attrs),
+      name (other.name), locus (other.locus)
+  {
+    type_param_bounds.reserve (other.type_param_bounds.size ());
+    for (const auto &e : other.type_param_bounds)
+      type_param_bounds.push_back (e->clone_type_param_bound ());
+  }
+
+  // Overloaded assignment operator with vector clone
+  TraitItemType &operator= (TraitItemType const &other)
+  {
+    TraitItem::operator= (other);
+    outer_attrs = other.outer_attrs;
+    name = other.name;
+    locus = other.locus;
+    mappings = other.mappings;
+
+    type_param_bounds.reserve (other.type_param_bounds.size ());
+    for (const auto &e : other.type_param_bounds)
+      type_param_bounds.push_back (e->clone_type_param_bound ());
+
+    return *this;
+  }
+
+  // default move constructors
+  TraitItemType (TraitItemType &&other) = default;
+  TraitItemType &operator= (TraitItemType &&other) = default;
+
+  std::string as_string () const override;
+
+  Location get_locus () const { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRTraitItemVisitor &vis) override;
+
+  Identifier get_name () const { return name; }
+
+  std::vector<std::unique_ptr<TypeParamBound>> &get_type_param_bounds ()
+  {
+    return type_param_bounds;
+  }
+
+  const std::string trait_identifier () const override final { return name; }
+
+  TraitItemKind get_item_kind () const override final
+  {
+    return TraitItemKind::TYPE;
+  }
+
+  AST::AttrVec &get_outer_attrs () override final { return outer_attrs; }
+  const AST::AttrVec &get_outer_attrs () const override final
+  {
+    return outer_attrs;
+  }
+
+protected:
+  // Clone function implementation as (not pure) virtual method
+  TraitItemType *clone_trait_item_impl () const override
+  {
+    return new TraitItemType (*this);
+  }
+};
+
+// Rust trait item declaration HIR node
+class Trait : public VisItem
+{
+  Unsafety unsafety;
+  Identifier name;
+  std::vector<std::unique_ptr<GenericParam>> generic_params;
+  std::vector<std::unique_ptr<TypeParamBound>> type_param_bounds;
+  WhereClause where_clause;
+  std::vector<std::unique_ptr<TraitItem>> trait_items;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  // Returns whether trait has generic parameters.
+  bool has_generics () const { return !generic_params.empty (); }
+
+  // Returns whether trait has type parameter bounds.
+  bool has_type_param_bounds () const { return !type_param_bounds.empty (); }
+
+  // Returns whether trait has where clause.
+  bool has_where_clause () const { return !where_clause.is_empty (); }
+
+  // Returns whether trait has trait items.
+  bool has_trait_items () const { return !trait_items.empty (); }
+
+  std::vector<std::unique_ptr<TraitItem>> &get_trait_items ()
+  {
+    return trait_items;
+  }
+
+  Identifier get_name () const { return name; }
+
+  // Mega-constructor
+  Trait (Analysis::NodeMapping mappings, Identifier name, Unsafety unsafety,
+	 std::vector<std::unique_ptr<GenericParam>> generic_params,
+	 std::vector<std::unique_ptr<TypeParamBound>> type_param_bounds,
+	 WhereClause where_clause,
+	 std::vector<std::unique_ptr<TraitItem>> trait_items, Visibility vis,
+	 AST::AttrVec outer_attrs, Location locus)
+    : VisItem (std::move (mappings), std::move (vis), std::move (outer_attrs)),
+      unsafety (unsafety), name (std::move (name)),
+      generic_params (std::move (generic_params)),
+      type_param_bounds (std::move (type_param_bounds)),
+      where_clause (std::move (where_clause)),
+      trait_items (std::move (trait_items)), locus (locus)
+  {}
+
+  // Copy constructor with vector clone
+  Trait (Trait const &other)
+    : VisItem (other), unsafety (other.unsafety), name (other.name),
+      where_clause (other.where_clause), locus (other.locus)
+  {
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+
+    type_param_bounds.reserve (other.type_param_bounds.size ());
+    for (const auto &e : other.type_param_bounds)
+      type_param_bounds.push_back (e->clone_type_param_bound ());
+
+    trait_items.reserve (other.trait_items.size ());
+    for (const auto &e : other.trait_items)
+      trait_items.push_back (e->clone_trait_item ());
+  }
+
+  // Overloaded assignment operator with vector clone
+  Trait &operator= (Trait const &other)
+  {
+    VisItem::operator= (other);
+    name = other.name;
+    unsafety = other.unsafety;
+    where_clause = other.where_clause;
+    locus = other.locus;
+
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+
+    type_param_bounds.reserve (other.type_param_bounds.size ());
+    for (const auto &e : other.type_param_bounds)
+      type_param_bounds.push_back (e->clone_type_param_bound ());
+
+    trait_items.reserve (other.trait_items.size ());
+    for (const auto &e : other.trait_items)
+      trait_items.push_back (e->clone_trait_item ());
+
+    return *this;
+  }
+
+  // default move constructors
+  Trait (Trait &&other) = default;
+  Trait &operator= (Trait &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+  void accept_vis (HIRVisItemVisitor &vis) override;
+
+  std::vector<std::unique_ptr<GenericParam>> &get_generic_params ()
+  {
+    return generic_params;
+  }
+
+  const std::vector<std::unique_ptr<GenericParam>> &get_generic_params () const
+  {
+    return generic_params;
+  }
+
+  std::vector<std::unique_ptr<TypeParamBound>> &get_type_param_bounds ()
+  {
+    return type_param_bounds;
+  }
+
+  const std::vector<std::unique_ptr<TypeParamBound>> &
+  get_type_param_bounds () const
+  {
+    return type_param_bounds;
+  }
+
+  ItemKind get_item_kind () const override { return ItemKind::Trait; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  Trait *clone_item_impl () const override { return new Trait (*this); }
+};
+
+class ImplBlock : public VisItem
+{
+  std::vector<std::unique_ptr<GenericParam>> generic_params;
+  std::unique_ptr<Type> impl_type;
+  std::unique_ptr<TypePath> trait_ref;
+  WhereClause where_clause;
+  Polarity polarity;
+  AST::AttrVec inner_attrs;
+  Location locus;
+  std::vector<std::unique_ptr<ImplItem>> impl_items;
+
+public:
+  ImplBlock (Analysis::NodeMapping mappings,
+	     std::vector<std::unique_ptr<ImplItem>> impl_items,
+	     std::vector<std::unique_ptr<GenericParam>> generic_params,
+	     std::unique_ptr<Type> impl_type,
+	     std::unique_ptr<TypePath> trait_ref, WhereClause where_clause,
+	     Polarity polarity, Visibility vis, AST::AttrVec inner_attrs,
+	     AST::AttrVec outer_attrs, Location locus)
+    : VisItem (std::move (mappings), std::move (vis), std::move (outer_attrs)),
+      generic_params (std::move (generic_params)),
+      impl_type (std::move (impl_type)), trait_ref (std::move (trait_ref)),
+      where_clause (std::move (where_clause)), polarity (polarity),
+      inner_attrs (std::move (inner_attrs)), locus (locus),
+      impl_items (std::move (impl_items))
+  {}
+
+  ImplBlock (ImplBlock const &other)
+    : VisItem (other), impl_type (other.impl_type->clone_type ()),
+      where_clause (other.where_clause), polarity (other.polarity),
+      inner_attrs (other.inner_attrs), locus (other.locus)
+  {
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+
+    impl_items.reserve (other.impl_items.size ());
+    for (const auto &e : other.impl_items)
+      impl_items.push_back (e->clone_inherent_impl_item ());
+  }
+
+  ImplBlock &operator= (ImplBlock const &other)
+  {
+    VisItem::operator= (other);
+    impl_type = other.impl_type->clone_type ();
+    where_clause = other.where_clause;
+    polarity = other.polarity;
+    inner_attrs = other.inner_attrs;
+    locus = other.locus;
+
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+
+    impl_items.reserve (other.impl_items.size ());
+    for (const auto &e : other.impl_items)
+      impl_items.push_back (e->clone_inherent_impl_item ());
+
+    return *this;
+  }
+
+  ImplBlock (ImplBlock &&other) = default;
+  ImplBlock &operator= (ImplBlock &&other) = default;
+
+  std::string as_string () const override;
+
+  // Returns whether inherent impl block has inherent impl items.
+  bool has_impl_items () const { return !impl_items.empty (); }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+  void accept_vis (HIRVisItemVisitor &vis) override;
+
+  std::vector<std::unique_ptr<ImplItem>> &get_impl_items ()
+  {
+    return impl_items;
+  };
+
+  const std::vector<std::unique_ptr<ImplItem>> &get_impl_items () const
+  {
+    return impl_items;
+  };
+
+  // Returns whether impl has generic parameters.
+  bool has_generics () const { return !generic_params.empty (); }
+
+  // Returns whether impl has where clause.
+  bool has_where_clause () const { return !where_clause.is_empty (); }
+
+  // Returns the polarity of the impl.
+  Polarity get_polarity () const { return polarity; }
+
+  // Returns whether impl has inner attributes.
+  bool has_inner_attrs () const { return !inner_attrs.empty (); }
+
+  Location get_locus () const override final { return locus; }
+
+  std::unique_ptr<Type> &get_type () { return impl_type; };
+
+  std::vector<std::unique_ptr<GenericParam>> &get_generic_params ()
+  {
+    return generic_params;
+  }
+
+  bool has_trait_ref () const { return trait_ref != nullptr; }
+
+  std::unique_ptr<TypePath> &get_trait_ref ()
+  {
+    rust_assert (has_trait_ref ());
+    return trait_ref;
+  }
+
+  WhereClause &get_where_clause () { return where_clause; }
+
+  ItemKind get_item_kind () const override { return ItemKind::Impl; }
+
+protected:
+  ImplBlock *clone_item_impl () const override { return new ImplBlock (*this); }
+};
+
+// Abstract base class for an item used inside an extern block
+class ExternalItem : public Node
+{
+  Analysis::NodeMapping mappings;
+  AST::AttrVec outer_attrs;
+  Visibility visibility;
+  Identifier item_name;
+  Location locus;
+
+public:
+  enum class ExternKind
+  {
+    Static,
+    Function,
+  };
+
+  virtual ~ExternalItem () {}
+
+  BaseKind get_hir_kind () override final { return EXTERNAL; }
+
+  virtual ExternKind get_extern_kind () = 0;
+
+  // Returns whether item has outer attributes.
+  bool has_outer_attrs () const { return !outer_attrs.empty (); }
+
+  // Returns whether item has non-default visibility.
+  bool has_visibility () const { return !visibility.is_error (); }
+
+  // Unique pointer custom clone function
+  std::unique_ptr<ExternalItem> clone_external_item () const
+  {
+    return std::unique_ptr<ExternalItem> (clone_external_item_impl ());
+  }
+
+  virtual std::string as_string () const;
+
+  Location get_locus () const { return locus; }
+
+  virtual void accept_vis (HIRFullVisitor &vis) = 0;
+  virtual void accept_vis (HIRExternalItemVisitor &vis) = 0;
+
+  Analysis::NodeMapping get_mappings () const { return mappings; }
+
+  Identifier get_item_name () const { return item_name; }
+
+  AST::AttrVec &get_outer_attrs () { return outer_attrs; }
+
+protected:
+  ExternalItem (Analysis::NodeMapping mappings, Identifier item_name,
+		Visibility vis, AST::AttrVec outer_attrs, Location locus)
+    : mappings (mappings), outer_attrs (std::move (outer_attrs)),
+      visibility (std::move (vis)), item_name (std::move (item_name)),
+      locus (locus)
+  {}
+
+  // Copy constructor
+  ExternalItem (ExternalItem const &other)
+    : mappings (other.mappings), outer_attrs (other.outer_attrs),
+      visibility (other.visibility), item_name (other.item_name),
+      locus (other.locus)
+  {}
+
+  // Overloaded assignment operator to clone
+  ExternalItem &operator= (ExternalItem const &other)
+  {
+    mappings = other.mappings;
+    item_name = other.item_name;
+    visibility = other.visibility;
+    outer_attrs = other.outer_attrs;
+    locus = other.locus;
+
+    return *this;
+  }
+
+  // move constructors
+  ExternalItem (ExternalItem &&other) = default;
+  ExternalItem &operator= (ExternalItem &&other) = default;
+
+  // Clone function implementation as pure virtual method
+  virtual ExternalItem *clone_external_item_impl () const = 0;
+};
+
+// A static item used in an extern block
+class ExternalStaticItem : public ExternalItem
+{
+  Mutability mut;
+  std::unique_ptr<Type> item_type;
+
+public:
+  ExternalStaticItem (Analysis::NodeMapping mappings, Identifier item_name,
+		      std::unique_ptr<Type> item_type, Mutability mut,
+		      Visibility vis, AST::AttrVec outer_attrs, Location locus)
+    : ExternalItem (std::move (mappings), std::move (item_name),
+		    std::move (vis), std::move (outer_attrs), locus),
+      mut (mut), item_type (std::move (item_type))
+  {}
+
+  // Copy constructor
+  ExternalStaticItem (ExternalStaticItem const &other)
+    : ExternalItem (other), mut (other.mut),
+      item_type (other.item_type->clone_type ())
+  {}
+
+  // Overloaded assignment operator to clone
+  ExternalStaticItem &operator= (ExternalStaticItem const &other)
+  {
+    ExternalItem::operator= (other);
+    item_type = other.item_type->clone_type ();
+    mut = other.mut;
+
+    return *this;
+  }
+
+  // move constructors
+  ExternalStaticItem (ExternalStaticItem &&other) = default;
+  ExternalStaticItem &operator= (ExternalStaticItem &&other) = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExternalItemVisitor &vis) override;
+
+  bool is_mut () const { return mut == Mutability::Mut; }
+
+  Mutability get_mut () { return mut; }
+
+  std::unique_ptr<Type> &get_item_type () { return item_type; }
+
+  ExternKind get_extern_kind () override { return ExternKind::Static; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  ExternalStaticItem *clone_external_item_impl () const override
+  {
+    return new ExternalStaticItem (*this);
+  }
+};
+
+// A named function parameter used in external functions
+struct NamedFunctionParam
+{
+private:
+  Identifier name;
+  std::unique_ptr<Type> param_type;
+  Analysis::NodeMapping mappings;
+
+public:
+  bool has_name () const { return name != "_"; }
+
+  NamedFunctionParam (Analysis::NodeMapping mappings, Identifier name,
+		      std::unique_ptr<Type> param_type)
+    : name (std::move (name)), param_type (std::move (param_type)),
+      mappings (std::move (mappings))
+  {}
+
+  // Copy constructor
+  NamedFunctionParam (NamedFunctionParam const &other)
+    : name (other.name), param_type (other.param_type->clone_type ()),
+      mappings (other.mappings)
+  {}
+
+  ~NamedFunctionParam () = default;
+
+  // Overloaded assignment operator to clone
+  NamedFunctionParam &operator= (NamedFunctionParam const &other)
+  {
+    mappings = other.mappings;
+    name = other.name;
+    param_type = other.param_type->clone_type ();
+    // has_name = other.has_name;
+
+    return *this;
+  }
+
+  // move constructors
+  NamedFunctionParam (NamedFunctionParam &&other) = default;
+  NamedFunctionParam &operator= (NamedFunctionParam &&other) = default;
+
+  std::string as_string () const;
+
+  Identifier get_param_name () const { return name; }
+
+  std::unique_ptr<Type> &get_type () { return param_type; }
+
+  Analysis::NodeMapping get_mappings () const { return mappings; }
+};
+
+// A function item used in an extern block
+class ExternalFunctionItem : public ExternalItem
+{
+  // bool has_generics;
+  // Generics generic_params;
+  std::vector<std::unique_ptr<GenericParam>> generic_params; // inlined
+
+  // bool has_return_type;
+  // FunctionReturnType return_type;
+  std::unique_ptr<Type> return_type; // inlined
+
+  // bool has_where_clause;
+  WhereClause where_clause;
+
+  std::vector<NamedFunctionParam> function_params;
+  bool has_variadics;
+
+public:
+  // Returns whether item has generic parameters.
+  bool has_generics () const { return !generic_params.empty (); }
+
+  // Returns whether item has a return type (otherwise void).
+  bool has_return_type () const { return return_type != nullptr; }
+
+  // Returns whether item has a where clause.
+  bool has_where_clause () const { return !where_clause.is_empty (); }
+
+  ExternalFunctionItem (
+    Analysis::NodeMapping mappings, Identifier item_name,
+    std::vector<std::unique_ptr<GenericParam>> generic_params,
+    std::unique_ptr<Type> return_type, WhereClause where_clause,
+    std::vector<NamedFunctionParam> function_params, bool has_variadics,
+    Visibility vis, AST::AttrVec outer_attrs, Location locus)
+    : ExternalItem (std::move (mappings), std::move (item_name),
+		    std::move (vis), std::move (outer_attrs), locus),
+      generic_params (std::move (generic_params)),
+      return_type (std::move (return_type)),
+      where_clause (std::move (where_clause)),
+      function_params (std::move (function_params)),
+      has_variadics (has_variadics)
+  {}
+
+  // Copy constructor with clone
+  ExternalFunctionItem (ExternalFunctionItem const &other)
+    : ExternalItem (other), return_type (other.return_type->clone_type ()),
+      where_clause (other.where_clause),
+      function_params (other.function_params),
+      has_variadics (other.has_variadics)
+  {
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+  }
+
+  // Overloaded assignment operator with clone
+  ExternalFunctionItem &operator= (ExternalFunctionItem const &other)
+  {
+    ExternalItem::operator= (other);
+    return_type = other.return_type->clone_type ();
+    where_clause = other.where_clause;
+    function_params = other.function_params;
+    has_variadics = other.has_variadics;
+
+    generic_params.reserve (other.generic_params.size ());
+    for (const auto &e : other.generic_params)
+      generic_params.push_back (e->clone_generic_param ());
+
+    return *this;
+  }
+
+  // move constructors
+  ExternalFunctionItem (ExternalFunctionItem &&other) = default;
+  ExternalFunctionItem &operator= (ExternalFunctionItem &&other) = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExternalItemVisitor &vis) override;
+
+  std::vector<std::unique_ptr<GenericParam>> &get_generic_params ()
+  {
+    return generic_params;
+  }
+
+  std::unique_ptr<Type> &get_return_type () { return return_type; }
+
+  std::vector<NamedFunctionParam> &get_function_params ()
+  {
+    return function_params;
+  }
+
+  bool is_variadic () const { return has_variadics; }
+
+  ExternKind get_extern_kind () override { return ExternKind::Function; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  ExternalFunctionItem *clone_external_item_impl () const override
+  {
+    return new ExternalFunctionItem (*this);
+  }
+};
+
+// An extern block HIR node
+class ExternBlock : public VisItem
+{
+  ABI abi;
+  AST::AttrVec inner_attrs;
+  std::vector<std::unique_ptr<ExternalItem>> extern_items;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  // Returns whether extern block has inner attributes.
+  bool has_inner_attrs () const { return !inner_attrs.empty (); }
+
+  // Returns whether extern block has extern items.
+  bool has_extern_items () const { return !extern_items.empty (); }
+
+  ABI get_abi () const { return abi; }
+
+  ExternBlock (Analysis::NodeMapping mappings, ABI abi,
+	       std::vector<std::unique_ptr<ExternalItem>> extern_items,
+	       Visibility vis, AST::AttrVec inner_attrs,
+	       AST::AttrVec outer_attrs, Location locus)
+    : VisItem (std::move (mappings), std::move (vis), std::move (outer_attrs)),
+      abi (abi), inner_attrs (std::move (inner_attrs)),
+      extern_items (std::move (extern_items)), locus (locus)
+  {}
+
+  // Copy constructor with vector clone
+  ExternBlock (ExternBlock const &other)
+    : VisItem (other), abi (other.abi), inner_attrs (other.inner_attrs),
+      locus (other.locus)
+  {
+    extern_items.reserve (other.extern_items.size ());
+    for (const auto &e : other.extern_items)
+      extern_items.push_back (e->clone_external_item ());
+  }
+
+  // Overloaded assignment operator with vector clone
+  ExternBlock &operator= (ExternBlock const &other)
+  {
+    VisItem::operator= (other);
+    abi = other.abi;
+    inner_attrs = other.inner_attrs;
+    locus = other.locus;
+
+    extern_items.reserve (other.extern_items.size ());
+    for (const auto &e : other.extern_items)
+      extern_items.push_back (e->clone_external_item ());
+
+    return *this;
+  }
+
+  // move constructors
+  ExternBlock (ExternBlock &&other) = default;
+  ExternBlock &operator= (ExternBlock &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+  void accept_vis (HIRVisItemVisitor &vis) override;
+
+  std::vector<std::unique_ptr<ExternalItem>> &get_extern_items ()
+  {
+    return extern_items;
+  }
+
+  ItemKind get_item_kind () const override { return ItemKind::ExternBlock; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  ExternBlock *clone_item_impl () const override
+  {
+    return new ExternBlock (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object
+   * rather than base */
+  /*virtual ExternBlock* clone_statement_impl() const override {
+      return new ExternBlock(*this);
+  }*/
+};
+
+} // namespace HIR
+} // namespace Rust
+
+#endif
diff --git a/gcc/rust/hir/tree/rust-hir-path.h b/gcc/rust/hir/tree/rust-hir-path.h
new file mode 100644
index 00000000000..03cf5f5d2e8
--- /dev/null
+++ b/gcc/rust/hir/tree/rust-hir-path.h
@@ -0,0 +1,1013 @@
+// 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_HIR_PATH_H
+#define RUST_HIR_PATH_H
+
+#include "rust-hir.h"
+
+namespace Rust {
+namespace HIR {
+
+// The "identifier" (not generic args) aspect of each path expression segment
+class PathIdentSegment
+{
+  std::string segment_name;
+
+  // TODO: should this have location info stored?
+
+  // only allow identifiers, "super", "self", "Self", "crate", or "$crate"
+public:
+  PathIdentSegment (std::string segment_name)
+    : segment_name (std::move (segment_name))
+  {}
+
+  /* TODO: insert check in constructor for this? Or is this a semantic error
+   * best handled then? */
+
+  /* TODO: does this require visitor? pretty sure this isn't polymorphic, but
+   * not entirely sure */
+
+  // Creates an error PathIdentSegment.
+  static PathIdentSegment create_error () { return PathIdentSegment (""); }
+
+  // Returns whether PathIdentSegment is in an error state.
+  bool is_error () const { return segment_name.empty (); }
+
+  std::string as_string () const { return segment_name; }
+};
+
+// A binding of an identifier to a type used in generic arguments in paths
+struct GenericArgsBinding
+{
+private:
+  Identifier identifier;
+  std::unique_ptr<Type> type;
+
+  Location locus;
+
+public:
+  // Returns whether binding is in an error state.
+  bool is_error () const
+  {
+    return type == nullptr;
+    // and also identifier is empty, but cheaper computation
+  }
+
+  // Creates an error state generic args binding.
+  static GenericArgsBinding create_error ()
+  {
+    return GenericArgsBinding ("", nullptr);
+  }
+
+  // Pointer type for type in constructor to enable polymorphism
+  GenericArgsBinding (Identifier ident, std::unique_ptr<Type> type_ptr,
+		      Location locus = Location ())
+    : identifier (std::move (ident)), type (std::move (type_ptr)), locus (locus)
+  {}
+
+  // Copy constructor has to deep copy the type as it is a unique pointer
+  GenericArgsBinding (GenericArgsBinding const &other)
+    : identifier (other.identifier), type (other.type->clone_type ()),
+      locus (other.locus)
+  {}
+
+  // default destructor
+  ~GenericArgsBinding () = default;
+
+  // Overload assignment operator to deep copy the pointed-to type
+  GenericArgsBinding &operator= (GenericArgsBinding const &other)
+  {
+    identifier = other.identifier;
+    type = other.type->clone_type ();
+    locus = other.locus;
+    return *this;
+  }
+
+  // move constructors
+  GenericArgsBinding (GenericArgsBinding &&other) = default;
+  GenericArgsBinding &operator= (GenericArgsBinding &&other) = default;
+
+  std::string as_string () const;
+
+  Identifier get_identifier () const { return identifier; }
+
+  std::unique_ptr<Type> &get_type () { return type; }
+
+  Location get_locus () const { return locus; }
+};
+
+class ConstGenericArg
+{
+  // FIXME: Do we need to disambiguate or no? We should be able to disambiguate
+  // at name-resolution, hence no need for ambiguities here
+
+public:
+  ConstGenericArg (std::unique_ptr<Expr> expression, Location locus)
+    : expression (std::move (expression)), locus (locus)
+  {}
+
+  ConstGenericArg (const ConstGenericArg &other) : locus (other.locus)
+  {
+    expression = other.expression->clone_expr ();
+  }
+
+  ConstGenericArg operator= (const ConstGenericArg &other)
+  {
+    expression = other.expression->clone_expr ();
+    locus = other.locus;
+
+    return *this;
+  }
+
+private:
+  std::unique_ptr<Expr> expression;
+  Location locus;
+};
+
+// Generic arguments allowed in each path expression segment - inline?
+struct GenericArgs
+{
+  std::vector<Lifetime> lifetime_args;
+  std::vector<std::unique_ptr<Type> > type_args;
+  std::vector<GenericArgsBinding> binding_args;
+  std::vector<ConstGenericArg> const_args;
+  Location locus;
+
+public:
+  // Returns true if there are any generic arguments
+  bool has_generic_args () const
+  {
+    return !(lifetime_args.empty () && type_args.empty ()
+	     && binding_args.empty ());
+  }
+
+  GenericArgs (std::vector<Lifetime> lifetime_args,
+	       std::vector<std::unique_ptr<Type> > type_args,
+	       std::vector<GenericArgsBinding> binding_args,
+	       std::vector<ConstGenericArg> const_args, Location locus)
+    : lifetime_args (std::move (lifetime_args)),
+      type_args (std::move (type_args)),
+      binding_args (std::move (binding_args)),
+      const_args (std::move (const_args)), locus (locus)
+  {}
+
+  // copy constructor with vector clone
+  GenericArgs (GenericArgs const &other)
+    : lifetime_args (other.lifetime_args), binding_args (other.binding_args),
+      const_args (other.const_args), locus (other.locus)
+  {
+    type_args.reserve (other.type_args.size ());
+
+    for (const auto &e : other.type_args)
+      type_args.push_back (e->clone_type ());
+  }
+
+  ~GenericArgs () = default;
+
+  // overloaded assignment operator to vector clone
+  GenericArgs &operator= (GenericArgs const &other)
+  {
+    lifetime_args = other.lifetime_args;
+    binding_args = other.binding_args;
+    const_args = other.const_args;
+    locus = other.locus;
+
+    type_args.reserve (other.type_args.size ());
+    for (const auto &e : other.type_args)
+      type_args.push_back (e->clone_type ());
+
+    return *this;
+  }
+
+  // move constructors
+  GenericArgs (GenericArgs &&other) = default;
+  GenericArgs &operator= (GenericArgs &&other) = default;
+
+  // Creates an empty GenericArgs (no arguments)
+  static GenericArgs create_empty (Location locus = Location ())
+  {
+    return GenericArgs ({}, {}, {}, {}, locus);
+  }
+
+  bool is_empty () const
+  {
+    return lifetime_args.size () == 0 && type_args.size () == 0
+	   && binding_args.size () == 0;
+  }
+
+  std::string as_string () const;
+
+  std::vector<Lifetime> &get_lifetime_args () { return lifetime_args; }
+
+  std::vector<std::unique_ptr<Type> > &get_type_args () { return type_args; }
+
+  std::vector<GenericArgsBinding> &get_binding_args () { return binding_args; }
+
+  std::vector<ConstGenericArg> &get_const_args () { return const_args; }
+
+  Location get_locus () const { return locus; }
+};
+
+/* A segment of a path in expression, including an identifier aspect and maybe
+ * generic args */
+class PathExprSegment
+{
+private:
+  Analysis::NodeMapping mappings;
+  PathIdentSegment segment_name;
+  GenericArgs generic_args;
+  Location locus;
+
+public:
+  // Returns true if there are any generic arguments
+  bool has_generic_args () const { return generic_args.has_generic_args (); }
+
+  // Constructor for segment (from IdentSegment and GenericArgs)
+  PathExprSegment (Analysis::NodeMapping mappings,
+		   PathIdentSegment segment_name, Location locus = Location (),
+		   GenericArgs generic_args = GenericArgs::create_empty ())
+    : mappings (std::move (mappings)), segment_name (std::move (segment_name)),
+      generic_args (std::move (generic_args)), locus (locus)
+  {}
+
+  std::string as_string () const;
+
+  Location get_locus () const { return locus; }
+
+  PathIdentSegment get_segment () const { return segment_name; }
+
+  GenericArgs &get_generic_args () { return generic_args; }
+
+  const Analysis::NodeMapping &get_mappings () const { return mappings; }
+};
+
+// HIR node representing a pattern that involves a "path" - abstract base class
+class PathPattern : public Pattern
+{
+  std::vector<PathExprSegment> segments;
+
+protected:
+  PathPattern (std::vector<PathExprSegment> segments)
+    : segments (std::move (segments))
+  {}
+
+  // Returns whether path has segments.
+  bool has_segments () const { return !segments.empty (); }
+
+  /* Converts path segments to their equivalent SimplePath segments if possible,
+   * and creates a SimplePath from them. */
+  AST::SimplePath
+  convert_to_simple_path (bool with_opening_scope_resolution) const;
+
+public:
+  /* Returns whether the path is a single segment (excluding qualified path
+   * initial as segment). */
+  bool is_single_segment () const { return segments.size () == 1; }
+
+  std::string as_string () const override;
+
+  void iterate_path_segments (std::function<bool (PathExprSegment &)> cb)
+  {
+    for (auto it = segments.begin (); it != segments.end (); it++)
+      {
+	if (!cb (*it))
+	  return;
+      }
+  }
+
+  size_t get_num_segments () const { return segments.size (); }
+
+  std::vector<PathExprSegment> &get_segments () { return segments; }
+
+  const std::vector<PathExprSegment> &get_segments () const { return segments; }
+
+  PathExprSegment &get_root_seg () { return segments.at (0); }
+
+  PathExprSegment get_final_segment () const { return segments.back (); }
+
+  PatternType get_pattern_type () const override final
+  {
+    return PatternType::PATH;
+  }
+};
+
+/* HIR node representing a path-in-expression pattern (path that allows generic
+ * arguments) */
+class PathInExpression : public PathPattern, public PathExpr
+{
+  bool has_opening_scope_resolution;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  // Constructor
+  PathInExpression (Analysis::NodeMapping mappings,
+		    std::vector<PathExprSegment> path_segments,
+		    Location locus = Location (),
+		    bool has_opening_scope_resolution = false,
+		    std::vector<AST::Attribute> outer_attrs
+		    = std::vector<AST::Attribute> ())
+    : PathPattern (std::move (path_segments)),
+      PathExpr (std::move (mappings), std::move (outer_attrs)),
+      has_opening_scope_resolution (has_opening_scope_resolution), locus (locus)
+  {}
+
+  // Creates an error state path in expression.
+  static PathInExpression create_error ()
+  {
+    return PathInExpression (Analysis::NodeMapping::get_error (),
+			     std::vector<PathExprSegment> ());
+  }
+
+  // Returns whether path in expression is in an error state.
+  bool is_error () const { return !has_segments (); }
+
+  /* Converts PathInExpression to SimplePath if possible (i.e. no generic
+   * arguments). Otherwise returns an empty SimplePath. */
+  AST::SimplePath as_simple_path () const
+  {
+    /* delegate to parent class as can't access segments. however,
+     * QualifiedPathInExpression conversion to simple path wouldn't make sense,
+     * so the method in the parent class should be protected, not public. Have
+     * to pass in opening scope resolution as parent class has no access to it.
+     */
+    return convert_to_simple_path (has_opening_scope_resolution);
+  }
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+  void accept_vis (HIRPatternVisitor &vis) override;
+
+  bool opening_scope_resolution () { return has_opening_scope_resolution; }
+
+  bool is_self () const
+  {
+    if (!is_single_segment ())
+      return false;
+
+    return get_final_segment ().get_segment ().as_string ().compare ("self")
+	   == 0;
+  }
+
+  Analysis::NodeMapping get_pattern_mappings () const override final
+  {
+    return get_mappings ();
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  PathInExpression *clone_pattern_impl () const override
+  {
+    return new PathInExpression (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  PathInExpression *clone_expr_without_block_impl () const override
+  {
+    return new PathInExpression (*this);
+  }
+};
+
+/* Base class for segments used in type paths - not abstract (represents an
+ * ident-only segment) */
+class TypePathSegment
+{
+public:
+  enum SegmentType
+  {
+    REG,
+    GENERIC,
+    FUNCTION
+  };
+
+private:
+  Analysis::NodeMapping mappings;
+  PathIdentSegment ident_segment;
+  Location locus;
+
+protected:
+  bool has_separating_scope_resolution;
+  SegmentType type;
+
+  // Clone function implementation - not pure virtual as overrided by subclasses
+  virtual TypePathSegment *clone_type_path_segment_impl () const
+  {
+    return new TypePathSegment (*this);
+  }
+
+public:
+  virtual ~TypePathSegment () {}
+
+  virtual SegmentType get_type () const { return SegmentType::REG; }
+
+  // Unique pointer custom clone function
+  std::unique_ptr<TypePathSegment> clone_type_path_segment () const
+  {
+    return std::unique_ptr<TypePathSegment> (clone_type_path_segment_impl ());
+  }
+
+  TypePathSegment (Analysis::NodeMapping mappings,
+		   PathIdentSegment ident_segment,
+		   bool has_separating_scope_resolution, Location locus)
+    : mappings (std::move (mappings)),
+      ident_segment (std::move (ident_segment)), locus (locus),
+      has_separating_scope_resolution (has_separating_scope_resolution),
+      type (SegmentType::REG)
+  {}
+
+  TypePathSegment (Analysis::NodeMapping mappings, std::string segment_name,
+		   bool has_separating_scope_resolution, Location locus)
+    : mappings (std::move (mappings)),
+      ident_segment (PathIdentSegment (std::move (segment_name))),
+      locus (locus),
+      has_separating_scope_resolution (has_separating_scope_resolution),
+      type (SegmentType::REG)
+  {}
+
+  virtual std::string as_string () const { return ident_segment.as_string (); }
+
+  /* Returns whether the type path segment is in an error state. May be virtual
+   * in future. */
+  bool is_error () const { return ident_segment.is_error (); }
+
+  /* Returns whether segment is identifier only (as opposed to generic args or
+   * function). Overriden in derived classes with other segments. */
+  virtual bool is_ident_only () const { return true; }
+
+  Location get_locus () const { return locus; }
+
+  // not pure virtual as class not abstract
+  virtual void accept_vis (HIRFullVisitor &vis);
+
+  const Analysis::NodeMapping &get_mappings () const { return mappings; }
+
+  const PathIdentSegment &get_ident_segment () const { return ident_segment; }
+
+  bool is_generic_segment () const
+  {
+    return get_type () == SegmentType::GENERIC;
+  }
+};
+
+// Segment used in type path with generic args
+class TypePathSegmentGeneric : public TypePathSegment
+{
+  GenericArgs generic_args;
+
+public:
+  bool has_generic_args () const { return generic_args.has_generic_args (); }
+
+  bool is_ident_only () const override { return false; }
+
+  // Constructor with PathIdentSegment and GenericArgs
+  TypePathSegmentGeneric (Analysis::NodeMapping mappings,
+			  PathIdentSegment ident_segment,
+			  bool has_separating_scope_resolution,
+			  GenericArgs generic_args, Location locus)
+    : TypePathSegment (std::move (mappings), std::move (ident_segment),
+		       has_separating_scope_resolution, locus),
+      generic_args (std::move (generic_args))
+  {}
+
+  // Constructor from segment name and all args
+  TypePathSegmentGeneric (Analysis::NodeMapping mappings,
+			  std::string segment_name,
+			  bool has_separating_scope_resolution,
+			  std::vector<Lifetime> lifetime_args,
+			  std::vector<std::unique_ptr<Type> > type_args,
+			  std::vector<GenericArgsBinding> binding_args,
+			  std::vector<ConstGenericArg> const_args,
+			  Location locus)
+    : TypePathSegment (std::move (mappings), std::move (segment_name),
+		       has_separating_scope_resolution, locus),
+      generic_args (
+	GenericArgs (std::move (lifetime_args), std::move (type_args),
+		     std::move (binding_args), std::move (const_args), locus))
+  {}
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  GenericArgs &get_generic_args () { return generic_args; }
+
+  virtual SegmentType get_type () const override final
+  {
+    return SegmentType::GENERIC;
+  }
+
+protected:
+  // Use covariance to override base class method
+  TypePathSegmentGeneric *clone_type_path_segment_impl () const override
+  {
+    return new TypePathSegmentGeneric (*this);
+  }
+};
+
+// A function as represented in a type path
+struct TypePathFunction
+{
+private:
+  // TODO: remove
+  /*bool has_inputs;
+  TypePathFnInputs inputs;*/
+  // inlined from TypePathFnInputs
+  std::vector<std::unique_ptr<Type> > inputs;
+
+  // bool has_type;
+  std::unique_ptr<Type> return_type;
+
+  // FIXME: think of better way to mark as invalid than taking up storage
+  bool is_invalid;
+
+  // TODO: should this have location info?
+
+protected:
+  // Constructor only used to create invalid type path functions.
+  TypePathFunction (bool is_invalid) : is_invalid (is_invalid) {}
+
+public:
+  // Returns whether the return type of the function has been specified.
+  bool has_return_type () const { return return_type != nullptr; }
+
+  // Returns whether the function has inputs.
+  bool has_inputs () const { return !inputs.empty (); }
+
+  // Returns whether function is in an error state.
+  bool is_error () const { return is_invalid; }
+
+  // Creates an error state function.
+  static TypePathFunction create_error () { return TypePathFunction (true); }
+
+  // Constructor
+  TypePathFunction (std::vector<std::unique_ptr<Type> > inputs,
+		    Type *type = nullptr)
+    : inputs (std::move (inputs)), return_type (type), is_invalid (false)
+  {}
+  // FIXME: deprecated
+
+  // Constructor
+  TypePathFunction (std::vector<std::unique_ptr<Type> > inputs,
+		    std::unique_ptr<Type> type = nullptr)
+    : inputs (std::move (inputs)), return_type (std::move (type)),
+      is_invalid (false)
+  {}
+
+  // Copy constructor with clone
+  TypePathFunction (TypePathFunction const &other)
+    : return_type (other.return_type->clone_type ()),
+      is_invalid (other.is_invalid)
+  {
+    inputs.reserve (other.inputs.size ());
+    for (const auto &e : other.inputs)
+      inputs.push_back (e->clone_type ());
+  }
+
+  ~TypePathFunction () = default;
+
+  // Overloaded assignment operator to clone type
+  TypePathFunction &operator= (TypePathFunction const &other)
+  {
+    return_type = other.return_type->clone_type ();
+    is_invalid = other.is_invalid;
+
+    inputs.reserve (other.inputs.size ());
+    for (const auto &e : other.inputs)
+      inputs.push_back (e->clone_type ());
+
+    return *this;
+  }
+
+  // move constructors
+  TypePathFunction (TypePathFunction &&other) = default;
+  TypePathFunction &operator= (TypePathFunction &&other) = default;
+
+  std::string as_string () const;
+};
+
+// Segment used in type path with a function argument
+class TypePathSegmentFunction : public TypePathSegment
+{
+  TypePathFunction function_path;
+
+public:
+  // Constructor with PathIdentSegment and TypePathFn
+  TypePathSegmentFunction (Analysis::NodeMapping mappings,
+			   PathIdentSegment ident_segment,
+			   bool has_separating_scope_resolution,
+			   TypePathFunction function_path, Location locus)
+    : TypePathSegment (std::move (mappings), std::move (ident_segment),
+		       has_separating_scope_resolution, locus),
+      function_path (std::move (function_path))
+  {}
+
+  // Constructor with segment name and TypePathFn
+  TypePathSegmentFunction (Analysis::NodeMapping mappings,
+			   std::string segment_name,
+			   bool has_separating_scope_resolution,
+			   TypePathFunction function_path, Location locus)
+    : TypePathSegment (std::move (mappings), std::move (segment_name),
+		       has_separating_scope_resolution, locus),
+      function_path (std::move (function_path))
+  {}
+
+  std::string as_string () const override;
+
+  bool is_ident_only () const override { return false; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  virtual SegmentType get_type () const override final
+  {
+    return SegmentType::FUNCTION;
+  }
+
+protected:
+  // Use covariance to override base class method
+  TypePathSegmentFunction *clone_type_path_segment_impl () const override
+  {
+    return new TypePathSegmentFunction (*this);
+  }
+};
+
+// Path used inside types
+class TypePath : public TypeNoBounds
+{
+public:
+  bool has_opening_scope_resolution;
+  std::vector<std::unique_ptr<TypePathSegment> > segments;
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TypePath *clone_type_impl () const override { return new TypePath (*this); }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TypePath *clone_type_no_bounds_impl () const override
+  {
+    return new TypePath (*this);
+  }
+
+public:
+  /* Returns whether the TypePath has an opening scope resolution operator (i.e.
+   * is global path or crate-relative path, not module-relative) */
+  bool has_opening_scope_resolution_op () const
+  {
+    return has_opening_scope_resolution;
+  }
+
+  // Returns whether the TypePath is in an invalid state.
+  bool is_error () const { return segments.empty (); }
+
+  // Creates an error state TypePath.
+  static TypePath create_error ()
+  {
+    return TypePath (Analysis::NodeMapping::get_error (),
+		     std::vector<std::unique_ptr<TypePathSegment> > (),
+		     Location ());
+  }
+
+  // Constructor
+  TypePath (Analysis::NodeMapping mappings,
+	    std::vector<std::unique_ptr<TypePathSegment> > segments,
+	    Location locus, bool has_opening_scope_resolution = false)
+    : TypeNoBounds (mappings, locus),
+      has_opening_scope_resolution (has_opening_scope_resolution),
+      segments (std::move (segments))
+  {}
+
+  // Copy constructor with vector clone
+  TypePath (TypePath const &other)
+    : TypeNoBounds (other.mappings, other.locus),
+      has_opening_scope_resolution (other.has_opening_scope_resolution)
+  {
+    segments.reserve (other.segments.size ());
+    for (const auto &e : other.segments)
+      segments.push_back (e->clone_type_path_segment ());
+  }
+
+  // Overloaded assignment operator with clone
+  TypePath &operator= (TypePath const &other)
+  {
+    has_opening_scope_resolution = other.has_opening_scope_resolution;
+    locus = other.locus;
+    mappings = other.mappings;
+
+    segments.reserve (other.segments.size ());
+    for (const auto &e : other.segments)
+      segments.push_back (e->clone_type_path_segment ());
+
+    return *this;
+  }
+
+  // move constructors
+  TypePath (TypePath &&other) = default;
+  TypePath &operator= (TypePath &&other) = default;
+
+  std::string as_string () const override;
+
+  /* Converts TypePath to SimplePath if possible (i.e. no generic or function
+   * arguments). Otherwise returns an empty SimplePath. */
+  AST::SimplePath as_simple_path () const;
+
+  // Creates a trait bound with a clone of this type path as its only element.
+  TraitBound *to_trait_bound (bool in_parens) const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRTypeVisitor &vis) override;
+
+  size_t get_num_segments () const { return segments.size (); }
+
+  std::vector<std::unique_ptr<TypePathSegment> > &get_segments ()
+  {
+    return segments;
+  }
+
+  std::unique_ptr<TypePathSegment> &get_final_segment ()
+  {
+    return segments.back ();
+  }
+};
+
+struct QualifiedPathType
+{
+private:
+  std::unique_ptr<Type> type;
+  std::unique_ptr<TypePath> trait;
+  Location locus;
+  Analysis::NodeMapping mappings;
+
+public:
+  // Constructor
+  QualifiedPathType (Analysis::NodeMapping mappings, std::unique_ptr<Type> type,
+		     std::unique_ptr<TypePath> trait, Location locus)
+    : type (std::move (type)), trait (std::move (trait)), locus (locus),
+      mappings (mappings)
+  {}
+
+  // Copy constructor uses custom deep copy for Type to preserve polymorphism
+  QualifiedPathType (QualifiedPathType const &other)
+    : type (other.type->clone_type ()),
+      trait (other.has_as_clause () ? std::unique_ptr<HIR::TypePath> (
+	       new HIR::TypePath (*other.trait))
+				    : nullptr),
+      locus (other.locus), mappings (other.mappings)
+  {}
+
+  // default destructor
+  ~QualifiedPathType () = default;
+
+  // overload assignment operator to use custom clone method
+  QualifiedPathType &operator= (QualifiedPathType const &other)
+  {
+    type = other.type->clone_type ();
+    locus = other.locus;
+    mappings = other.mappings;
+    trait
+      = other.has_as_clause ()
+	  ? std::unique_ptr<HIR::TypePath> (new HIR::TypePath (*other.trait))
+	  : nullptr;
+
+    return *this;
+  }
+
+  // move constructor
+  QualifiedPathType (QualifiedPathType &&other) = default;
+  QualifiedPathType &operator= (QualifiedPathType &&other) = default;
+
+  // Returns whether the qualified path type has a rebind as clause.
+  bool has_as_clause () const { return trait != nullptr; }
+
+  std::string as_string () const;
+
+  Location get_locus () const { return locus; }
+
+  Analysis::NodeMapping get_mappings () const { return mappings; }
+
+  std::unique_ptr<Type> &get_type () { return type; }
+
+  std::unique_ptr<TypePath> &get_trait ()
+  {
+    rust_assert (has_as_clause ());
+    return trait;
+  }
+
+  bool trait_has_generic_args () const
+  {
+    rust_assert (has_as_clause ());
+    bool is_generic_seg = trait->get_final_segment ()->get_type ()
+			  == TypePathSegment::SegmentType::GENERIC;
+    if (!is_generic_seg)
+      return false;
+
+    TypePathSegmentGeneric *seg = static_cast<TypePathSegmentGeneric *> (
+      trait->get_final_segment ().get ());
+    return seg->has_generic_args ();
+  }
+
+  GenericArgs &get_trait_generic_args ()
+  {
+    rust_assert (trait_has_generic_args ());
+    TypePathSegmentGeneric *seg = static_cast<TypePathSegmentGeneric *> (
+      trait->get_final_segment ().get ());
+    return seg->get_generic_args ();
+  }
+};
+
+/* HIR node representing a qualified path-in-expression pattern (path that
+ * allows specifying trait functions) */
+class QualifiedPathInExpression : public PathPattern, public PathExpr
+{
+  QualifiedPathType path_type;
+  Location locus;
+
+public:
+  std::string as_string () const override;
+
+  QualifiedPathInExpression (Analysis::NodeMapping mappings,
+			     QualifiedPathType qual_path_type,
+			     std::vector<PathExprSegment> path_segments,
+			     Location locus = Location (),
+			     std::vector<AST::Attribute> outer_attrs
+			     = std::vector<AST::Attribute> ())
+    : PathPattern (std::move (path_segments)),
+      PathExpr (std::move (mappings), std::move (outer_attrs)),
+      path_type (std::move (qual_path_type)), locus (locus)
+  {}
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+  void accept_vis (HIRPatternVisitor &vis) override;
+
+  QualifiedPathType &get_path_type () { return path_type; }
+
+  Location get_locus () { return locus; }
+
+  Analysis::NodeMapping get_pattern_mappings () const override final
+  {
+    return get_mappings ();
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  QualifiedPathInExpression *clone_pattern_impl () const override
+  {
+    return new QualifiedPathInExpression (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  QualifiedPathInExpression *clone_expr_without_block_impl () const override
+  {
+    return new QualifiedPathInExpression (*this);
+  }
+};
+
+/* Represents a qualified path in a type; used for disambiguating trait function
+ * calls */
+class QualifiedPathInType : public TypeNoBounds
+{
+  QualifiedPathType path_type;
+  std::unique_ptr<TypePathSegment> associated_segment;
+  std::vector<std::unique_ptr<TypePathSegment> > segments;
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  QualifiedPathInType *clone_type_impl () const override
+  {
+    return new QualifiedPathInType (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  QualifiedPathInType *clone_type_no_bounds_impl () const override
+  {
+    return new QualifiedPathInType (*this);
+  }
+
+public:
+  QualifiedPathInType (
+    Analysis::NodeMapping mappings, QualifiedPathType qual_path_type,
+    std::unique_ptr<TypePathSegment> associated_segment,
+    std::vector<std::unique_ptr<TypePathSegment> > path_segments,
+    Location locus = Location ())
+    : TypeNoBounds (mappings, locus), path_type (std::move (qual_path_type)),
+      associated_segment (std::move (associated_segment)),
+      segments (std::move (path_segments))
+  {}
+
+  /* TODO: maybe make a shortcut constructor that has QualifiedPathType elements
+   * as params */
+
+  // Copy constructor with vector clone
+  QualifiedPathInType (QualifiedPathInType const &other)
+    : TypeNoBounds (other.mappings, other.locus), path_type (other.path_type)
+  {
+    segments.reserve (other.segments.size ());
+    for (const auto &e : other.segments)
+      segments.push_back (e->clone_type_path_segment ());
+
+    // Untested.
+    gcc_unreachable ();
+  }
+
+  // Overloaded assignment operator with vector clone
+  QualifiedPathInType &operator= (QualifiedPathInType const &other)
+  {
+    path_type = other.path_type;
+    locus = other.locus;
+    mappings = other.mappings;
+
+    segments.reserve (other.segments.size ());
+    for (const auto &e : other.segments)
+      segments.push_back (e->clone_type_path_segment ());
+
+    return *this;
+  }
+
+  // move constructors
+  QualifiedPathInType (QualifiedPathInType &&other) = default;
+  QualifiedPathInType &operator= (QualifiedPathInType &&other) = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRTypeVisitor &vis) override;
+
+  QualifiedPathType &get_path_type () { return path_type; }
+
+  std::unique_ptr<TypePathSegment> &get_associated_segment ()
+  {
+    return associated_segment;
+  }
+
+  std::vector<std::unique_ptr<TypePathSegment> > &get_segments ()
+  {
+    return segments;
+  }
+};
+
+class SimplePathSegment
+{
+  Analysis::NodeMapping mappings;
+
+public:
+  SimplePathSegment (Analysis::NodeMapping mappings) : mappings (mappings) {}
+
+  const Analysis::NodeMapping &get_mappings () const { return mappings; }
+};
+
+class SimplePath
+{
+  std::vector<SimplePathSegment> segments;
+  Analysis::NodeMapping mappings;
+  Location locus;
+
+public:
+  SimplePath (std::vector<SimplePathSegment> segments,
+	      Analysis::NodeMapping mappings, Location locus)
+    : segments (std::move (segments)), mappings (mappings), locus (locus)
+  {}
+
+  static HIR::SimplePath create_empty ()
+  {
+    return HIR::SimplePath ({}, Analysis::NodeMapping::get_error (),
+			    Location ());
+  }
+
+  bool is_error () const { return segments.empty (); }
+
+  const Analysis::NodeMapping &get_mappings () const { return mappings; }
+  const Location &get_locus () const { return locus; }
+};
+
+} // namespace HIR
+} // namespace Rust
+
+#endif
diff --git a/gcc/rust/hir/tree/rust-hir-pattern.h b/gcc/rust/hir/tree/rust-hir-pattern.h
new file mode 100644
index 00000000000..7129b5a3684
--- /dev/null
+++ b/gcc/rust/hir/tree/rust-hir-pattern.h
@@ -0,0 +1,1356 @@
+// 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_HIR_PATTERN_H
+#define RUST_HIR_PATTERN_H
+
+#include "rust-common.h"
+#include "rust-hir.h"
+
+namespace Rust {
+namespace HIR {
+
+// Literal pattern HIR node (comparing to a literal)
+class LiteralPattern : public Pattern
+{
+  Literal lit;
+  Location locus;
+  Analysis::NodeMapping mappings;
+
+public:
+  std::string as_string () const override;
+
+  // Constructor for a literal pattern
+  LiteralPattern (Analysis::NodeMapping mappings, Literal lit, Location locus)
+    : lit (std::move (lit)), locus (locus), mappings (mappings)
+  {}
+
+  LiteralPattern (Analysis::NodeMapping mappings, std::string val,
+		  Literal::LitType type, Location locus)
+    : lit (Literal (std::move (val), type, PrimitiveCoreType::CORETYPE_STR)),
+      locus (locus), mappings (mappings)
+  {}
+
+  Location get_locus () const override { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRPatternVisitor &vis) override;
+
+  Analysis::NodeMapping get_pattern_mappings () const override final
+  {
+    return mappings;
+  }
+
+  PatternType get_pattern_type () const override final
+  {
+    return PatternType::LITERAL;
+  }
+
+  Literal &get_literal () { return lit; }
+  const Literal &get_literal () const { return lit; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  virtual LiteralPattern *clone_pattern_impl () const override
+  {
+    return new LiteralPattern (*this);
+  }
+};
+
+// Identifier pattern HIR node (bind value matched to a variable)
+class IdentifierPattern : public Pattern
+{
+  Identifier variable_ident;
+  bool is_ref;
+  Mutability mut;
+  std::unique_ptr<Pattern> to_bind;
+  Location locus;
+  Analysis::NodeMapping mappings;
+
+public:
+  std::string as_string () const override;
+
+  // Returns whether the IdentifierPattern has a pattern to bind.
+  bool has_pattern_to_bind () const { return to_bind != nullptr; }
+
+  // Constructor
+  IdentifierPattern (Analysis::NodeMapping mappings, Identifier ident,
+		     Location locus, bool is_ref = false,
+		     Mutability mut = Mutability::Imm,
+		     std::unique_ptr<Pattern> to_bind = nullptr)
+    : variable_ident (std::move (ident)), is_ref (is_ref), mut (mut),
+      to_bind (std::move (to_bind)), locus (locus), mappings (mappings)
+  {}
+
+  // Copy constructor with clone
+  IdentifierPattern (IdentifierPattern const &other)
+    : variable_ident (other.variable_ident), is_ref (other.is_ref),
+      mut (other.mut), locus (other.locus), mappings (other.mappings)
+  {
+    // fix to get prevent null pointer dereference
+    if (other.to_bind != nullptr)
+      to_bind = other.to_bind->clone_pattern ();
+  }
+
+  // Overload assignment operator to use clone
+  IdentifierPattern &operator= (IdentifierPattern const &other)
+  {
+    variable_ident = other.variable_ident;
+    is_ref = other.is_ref;
+    mut = other.mut;
+    locus = other.locus;
+    mappings = other.mappings;
+
+    // fix to get prevent null pointer dereference
+    if (other.to_bind != nullptr)
+      to_bind = other.to_bind->clone_pattern ();
+
+    return *this;
+  }
+
+  // default move semantics
+  IdentifierPattern (IdentifierPattern &&other) = default;
+  IdentifierPattern &operator= (IdentifierPattern &&other) = default;
+
+  Location get_locus () const override { return locus; }
+
+  bool is_mut () const { return mut == Mutability::Mut; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRPatternVisitor &vis) override;
+
+  Analysis::NodeMapping get_pattern_mappings () const override final
+  {
+    return mappings;
+  }
+
+  Identifier get_identifier () const { return variable_ident; }
+
+  PatternType get_pattern_type () const override final
+  {
+    return PatternType::IDENTIFIER;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  IdentifierPattern *clone_pattern_impl () const override
+  {
+    return new IdentifierPattern (*this);
+  }
+};
+
+// HIR node for using the '_' wildcard "match any value" pattern
+class WildcardPattern : public Pattern
+{
+  Location locus;
+  Analysis::NodeMapping mappings;
+
+public:
+  std::string as_string () const override { return std::string (1, '_'); }
+
+  WildcardPattern (Analysis::NodeMapping mappings, Location locus)
+    : locus (locus), mappings (mappings)
+  {}
+
+  Location get_locus () const override { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRPatternVisitor &vis) override;
+
+  Analysis::NodeMapping get_pattern_mappings () const override final
+  {
+    return mappings;
+  }
+
+  PatternType get_pattern_type () const override final
+  {
+    return PatternType::WILDCARD;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  WildcardPattern *clone_pattern_impl () const override
+  {
+    return new WildcardPattern (*this);
+  }
+};
+
+// Base range pattern bound (lower or upper limit) - abstract
+class RangePatternBound
+{
+public:
+  enum RangePatternBoundType
+  {
+    LITERAL,
+    PATH,
+    QUALPATH
+  };
+
+  virtual ~RangePatternBound () {}
+
+  // Unique pointer custom clone function
+  std::unique_ptr<RangePatternBound> clone_range_pattern_bound () const
+  {
+    return std::unique_ptr<RangePatternBound> (
+      clone_range_pattern_bound_impl ());
+  }
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (HIRFullVisitor &vis) = 0;
+
+  virtual RangePatternBoundType get_bound_type () const = 0;
+
+protected:
+  // pure virtual as RangePatternBound is abstract
+  virtual RangePatternBound *clone_range_pattern_bound_impl () const = 0;
+};
+
+// Literal-based pattern bound
+class RangePatternBoundLiteral : public RangePatternBound
+{
+  Literal literal;
+  /* Can only be a char, byte, int, or float literal - same impl here as
+   * previously */
+
+  // Minus prefixed to literal (if integer or floating-point)
+  bool has_minus;
+
+  Location locus;
+
+public:
+  // Constructor
+  RangePatternBoundLiteral (Literal literal, Location locus,
+			    bool has_minus = false)
+    : literal (literal), has_minus (has_minus), locus (locus)
+  {}
+
+  std::string as_string () const override;
+
+  Location get_locus () const { return locus; }
+
+  Literal get_literal () const { return literal; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  RangePatternBoundType get_bound_type () const override
+  {
+    return RangePatternBoundType::LITERAL;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangePatternBoundLiteral *clone_range_pattern_bound_impl () const override
+  {
+    return new RangePatternBoundLiteral (*this);
+  }
+};
+
+// Path-based pattern bound
+class RangePatternBoundPath : public RangePatternBound
+{
+  PathInExpression path;
+
+  /* TODO: should this be refactored so that PathInExpression is a subclass of
+   * RangePatternBound? */
+
+public:
+  RangePatternBoundPath (PathInExpression path) : path (std::move (path)) {}
+
+  std::string as_string () const override { return path.as_string (); }
+
+  Location get_locus () const { return path.get_locus (); }
+
+  PathInExpression &get_path () { return path; }
+  const PathInExpression &get_path () const { return path; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  RangePatternBoundType get_bound_type () const override
+  {
+    return RangePatternBoundType::PATH;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangePatternBoundPath *clone_range_pattern_bound_impl () const override
+  {
+    return new RangePatternBoundPath (*this);
+  }
+};
+
+// Qualified path-based pattern bound
+class RangePatternBoundQualPath : public RangePatternBound
+{
+  QualifiedPathInExpression path;
+
+  /* TODO: should this be refactored so that QualifiedPathInExpression is a
+   * subclass of RangePatternBound? */
+
+public:
+  RangePatternBoundQualPath (QualifiedPathInExpression path)
+    : path (std::move (path))
+  {}
+
+  std::string as_string () const override { return path.as_string (); }
+
+  Location get_locus () const { return path.get_locus (); }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  QualifiedPathInExpression &get_qualified_path () { return path; }
+  const QualifiedPathInExpression &get_qualified_path () const { return path; }
+
+  RangePatternBoundType get_bound_type () const override
+  {
+    return RangePatternBoundType::QUALPATH;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangePatternBoundQualPath *clone_range_pattern_bound_impl () const override
+  {
+    return new RangePatternBoundQualPath (*this);
+  }
+};
+
+// HIR node for matching within a certain range (range pattern)
+class RangePattern : public Pattern
+{
+  std::unique_ptr<RangePatternBound> lower;
+  std::unique_ptr<RangePatternBound> upper;
+
+  bool has_ellipsis_syntax;
+
+  /* location only stored to avoid a dereference - lower pattern should give
+   * correct location so maybe change in future */
+  Location locus;
+  Analysis::NodeMapping mappings;
+
+public:
+  std::string as_string () const override;
+
+  // Constructor
+  RangePattern (Analysis::NodeMapping mappings,
+		std::unique_ptr<RangePatternBound> lower,
+		std::unique_ptr<RangePatternBound> upper, Location locus,
+		bool has_ellipsis_syntax = false)
+    : lower (std::move (lower)), upper (std::move (upper)),
+      has_ellipsis_syntax (has_ellipsis_syntax), locus (locus),
+      mappings (mappings)
+  {}
+
+  // Copy constructor with clone
+  RangePattern (RangePattern const &other)
+    : lower (other.lower->clone_range_pattern_bound ()),
+      upper (other.upper->clone_range_pattern_bound ()),
+      has_ellipsis_syntax (other.has_ellipsis_syntax), locus (other.locus),
+      mappings (other.mappings)
+  {}
+
+  // Overloaded assignment operator to clone
+  RangePattern &operator= (RangePattern const &other)
+  {
+    lower = other.lower->clone_range_pattern_bound ();
+    upper = other.upper->clone_range_pattern_bound ();
+    has_ellipsis_syntax = other.has_ellipsis_syntax;
+    locus = other.locus;
+    mappings = other.mappings;
+
+    return *this;
+  }
+
+  // default move semantics
+  RangePattern (RangePattern &&other) = default;
+  RangePattern &operator= (RangePattern &&other) = default;
+
+  Location get_locus () const override { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRPatternVisitor &vis) override;
+
+  Analysis::NodeMapping get_pattern_mappings () const override final
+  {
+    return mappings;
+  }
+
+  PatternType get_pattern_type () const override final
+  {
+    return PatternType::RANGE;
+  }
+
+  std::unique_ptr<RangePatternBound> &get_lower_bound ()
+  {
+    rust_assert (lower != nullptr);
+    return lower;
+  }
+
+  std::unique_ptr<RangePatternBound> &get_upper_bound ()
+  {
+    rust_assert (upper != nullptr);
+    return upper;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RangePattern *clone_pattern_impl () const override
+  {
+    return new RangePattern (*this);
+  }
+};
+
+// HIR node for pattern based on dereferencing the pointers given
+class ReferencePattern : public Pattern
+{
+  bool has_two_amps;
+  Mutability mut;
+  std::unique_ptr<Pattern> pattern;
+  Location locus;
+  Analysis::NodeMapping mappings;
+
+public:
+  std::string as_string () const override;
+
+  ReferencePattern (Analysis::NodeMapping mappings,
+		    std::unique_ptr<Pattern> pattern, Mutability reference_mut,
+		    bool ref_has_two_amps, Location locus)
+    : has_two_amps (ref_has_two_amps), mut (reference_mut),
+      pattern (std::move (pattern)), locus (locus), mappings (mappings)
+  {}
+
+  // Copy constructor requires clone
+  ReferencePattern (ReferencePattern const &other)
+    : has_two_amps (other.has_two_amps), mut (other.mut),
+      pattern (other.pattern->clone_pattern ()), locus (other.locus),
+      mappings (other.mappings)
+  {}
+
+  // Overload assignment operator to clone
+  ReferencePattern &operator= (ReferencePattern const &other)
+  {
+    pattern = other.pattern->clone_pattern ();
+    mut = other.mut;
+    has_two_amps = other.has_two_amps;
+    locus = other.locus;
+    mappings = other.mappings;
+
+    return *this;
+  }
+
+  // default move semantics
+  ReferencePattern (ReferencePattern &&other) = default;
+  ReferencePattern &operator= (ReferencePattern &&other) = default;
+
+  bool is_mut () const { return mut == Mutability::Mut; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRPatternVisitor &vis) override;
+
+  Analysis::NodeMapping get_pattern_mappings () const override final
+  {
+    return mappings;
+  }
+
+  Location get_locus () const override final { return locus; }
+
+  PatternType get_pattern_type () const override final
+  {
+    return PatternType::REFERENCE;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ReferencePattern *clone_pattern_impl () const override
+  {
+    return new ReferencePattern (*this);
+  }
+};
+
+// Base class for a single field in a struct pattern - abstract
+class StructPatternField
+{
+  AST::AttrVec outer_attrs;
+  Location locus;
+  Analysis::NodeMapping mappings;
+
+public:
+  enum ItemType
+  {
+    TUPLE_PAT,
+    IDENT_PAT,
+    IDENT
+  };
+
+  virtual ~StructPatternField () {}
+
+  // Unique pointer custom clone function
+  std::unique_ptr<StructPatternField> clone_struct_pattern_field () const
+  {
+    return std::unique_ptr<StructPatternField> (
+      clone_struct_pattern_field_impl ());
+  }
+
+  virtual std::string as_string () const;
+  virtual void accept_vis (HIRFullVisitor &vis) = 0;
+  virtual ItemType get_item_type () const = 0;
+
+  Location get_locus () const { return locus; }
+  Analysis::NodeMapping get_mappings () const { return mappings; };
+
+protected:
+  StructPatternField (Analysis::NodeMapping mappings,
+		      AST::AttrVec outer_attribs, Location locus)
+    : outer_attrs (std::move (outer_attribs)), locus (locus),
+      mappings (mappings)
+  {}
+
+  // Clone function implementation as pure virtual method
+  virtual StructPatternField *clone_struct_pattern_field_impl () const = 0;
+};
+
+// Tuple pattern single field in a struct pattern
+class StructPatternFieldTuplePat : public StructPatternField
+{
+  TupleIndex index;
+  std::unique_ptr<Pattern> tuple_pattern;
+
+public:
+  StructPatternFieldTuplePat (Analysis::NodeMapping mappings, TupleIndex index,
+			      std::unique_ptr<Pattern> tuple_pattern,
+			      AST::AttrVec outer_attribs, Location locus)
+    : StructPatternField (mappings, std::move (outer_attribs), locus),
+      index (index), tuple_pattern (std::move (tuple_pattern))
+  {}
+
+  // Copy constructor requires clone
+  StructPatternFieldTuplePat (StructPatternFieldTuplePat const &other)
+    : StructPatternField (other), index (other.index),
+      tuple_pattern (other.tuple_pattern->clone_pattern ())
+  {}
+
+  // Overload assignment operator to perform clone
+  StructPatternFieldTuplePat &
+  operator= (StructPatternFieldTuplePat const &other)
+  {
+    StructPatternField::operator= (other);
+    tuple_pattern = other.tuple_pattern->clone_pattern ();
+    index = other.index;
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // default move semantics
+  StructPatternFieldTuplePat (StructPatternFieldTuplePat &&other) = default;
+  StructPatternFieldTuplePat &operator= (StructPatternFieldTuplePat &&other)
+    = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  ItemType get_item_type () const override final { return ItemType::TUPLE_PAT; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  StructPatternFieldTuplePat *clone_struct_pattern_field_impl () const override
+  {
+    return new StructPatternFieldTuplePat (*this);
+  }
+};
+
+// Identifier pattern single field in a struct pattern
+class StructPatternFieldIdentPat : public StructPatternField
+{
+  Identifier ident;
+  std::unique_ptr<Pattern> ident_pattern;
+
+public:
+  StructPatternFieldIdentPat (Analysis::NodeMapping mappings, Identifier ident,
+			      std::unique_ptr<Pattern> ident_pattern,
+			      AST::AttrVec outer_attrs, Location locus)
+    : StructPatternField (mappings, std::move (outer_attrs), locus),
+      ident (std::move (ident)), ident_pattern (std::move (ident_pattern))
+  {}
+
+  // Copy constructor requires clone
+  StructPatternFieldIdentPat (StructPatternFieldIdentPat const &other)
+    : StructPatternField (other), ident (other.ident),
+      ident_pattern (other.ident_pattern->clone_pattern ())
+  {}
+
+  // Overload assignment operator to clone
+  StructPatternFieldIdentPat &
+  operator= (StructPatternFieldIdentPat const &other)
+  {
+    StructPatternField::operator= (other);
+    ident = other.ident;
+    ident_pattern = other.ident_pattern->clone_pattern ();
+    // outer_attrs = other.outer_attrs;
+
+    return *this;
+  }
+
+  // default move semantics
+  StructPatternFieldIdentPat (StructPatternFieldIdentPat &&other) = default;
+  StructPatternFieldIdentPat &operator= (StructPatternFieldIdentPat &&other)
+    = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  ItemType get_item_type () const override final { return ItemType::IDENT_PAT; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  StructPatternFieldIdentPat *clone_struct_pattern_field_impl () const override
+  {
+    return new StructPatternFieldIdentPat (*this);
+  }
+};
+
+// Identifier only (with no pattern) single field in a struct pattern
+class StructPatternFieldIdent : public StructPatternField
+{
+  bool has_ref;
+  Mutability mut;
+  Identifier ident;
+
+public:
+  StructPatternFieldIdent (Analysis::NodeMapping mappings, Identifier ident,
+			   bool is_ref, Mutability mut,
+			   AST::AttrVec outer_attrs, Location locus)
+    : StructPatternField (mappings, std::move (outer_attrs), locus),
+      has_ref (is_ref), mut (mut), ident (std::move (ident))
+  {}
+
+  std::string as_string () const override;
+
+  bool is_mut () const { return mut == Mutability::Mut; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  ItemType get_item_type () const override final { return ItemType::IDENT; }
+
+  Identifier get_identifier () const { return ident; };
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  StructPatternFieldIdent *clone_struct_pattern_field_impl () const override
+  {
+    return new StructPatternFieldIdent (*this);
+  }
+};
+
+// Elements of a struct pattern
+struct StructPatternElements
+{
+private:
+  std::vector<std::unique_ptr<StructPatternField> > fields;
+
+public:
+  // Returns whether there are any struct pattern fields
+  bool has_struct_pattern_fields () const { return !fields.empty (); }
+
+  /* Returns whether the struct pattern elements is entirely empty (no fields,
+   * no etc). */
+  bool is_empty () const { return !has_struct_pattern_fields (); }
+
+  // Constructor for StructPatternElements with both (potentially)
+  StructPatternElements (
+    std::vector<std::unique_ptr<StructPatternField> > fields)
+    : fields (std::move (fields))
+  {}
+
+  // Copy constructor with vector clone
+  StructPatternElements (StructPatternElements const &other)
+  {
+    fields.reserve (other.fields.size ());
+    for (const auto &e : other.fields)
+      fields.push_back (e->clone_struct_pattern_field ());
+  }
+
+  // Overloaded assignment operator with vector clone
+  StructPatternElements &operator= (StructPatternElements const &other)
+  {
+    fields.reserve (other.fields.size ());
+    for (const auto &e : other.fields)
+      fields.push_back (e->clone_struct_pattern_field ());
+
+    return *this;
+  }
+
+  // move constructors
+  StructPatternElements (StructPatternElements &&other) = default;
+  StructPatternElements &operator= (StructPatternElements &&other) = default;
+
+  // Creates an empty StructPatternElements
+  static StructPatternElements create_empty ()
+  {
+    return StructPatternElements (
+      std::vector<std::unique_ptr<StructPatternField> > ());
+  }
+
+  std::string as_string () const;
+
+  std::vector<std::unique_ptr<StructPatternField> > &
+  get_struct_pattern_fields ()
+  {
+    return fields;
+  }
+};
+
+// Struct pattern HIR node representation
+class StructPattern : public Pattern
+{
+  PathInExpression path;
+  StructPatternElements elems;
+  Analysis::NodeMapping mappings;
+
+public:
+  std::string as_string () const override;
+
+  StructPattern (Analysis::NodeMapping mappings, PathInExpression struct_path,
+		 StructPatternElements elems)
+    : path (std::move (struct_path)), elems (std::move (elems)),
+      mappings (mappings)
+  {}
+
+  bool has_struct_pattern_elems () const { return !elems.is_empty (); }
+
+  Location get_locus () const override { return path.get_locus (); }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRPatternVisitor &vis) override;
+
+  PathInExpression &get_path () { return path; }
+  StructPatternElements &get_struct_pattern_elems () { return elems; }
+
+  Analysis::NodeMapping get_pattern_mappings () const override final
+  {
+    return mappings;
+  }
+
+  PatternType get_pattern_type () const override final
+  {
+    return PatternType::STRUCT;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  StructPattern *clone_pattern_impl () const override
+  {
+    return new StructPattern (*this);
+  }
+};
+
+// Base abstract class for patterns used in TupleStructPattern
+class TupleStructItems
+{
+public:
+  enum ItemType
+  {
+    RANGE,
+    NO_RANGE
+  };
+
+  virtual ~TupleStructItems () {}
+
+  // TODO: should this store location data?
+
+  // Unique pointer custom clone function
+  std::unique_ptr<TupleStructItems> clone_tuple_struct_items () const
+  {
+    return std::unique_ptr<TupleStructItems> (clone_tuple_struct_items_impl ());
+  }
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (HIRFullVisitor &vis) = 0;
+
+  virtual ItemType get_item_type () const = 0;
+
+protected:
+  // pure virtual clone implementation
+  virtual TupleStructItems *clone_tuple_struct_items_impl () const = 0;
+};
+
+// Class for non-ranged tuple struct pattern patterns
+class TupleStructItemsNoRange : public TupleStructItems
+{
+  std::vector<std::unique_ptr<Pattern> > patterns;
+
+public:
+  TupleStructItemsNoRange (std::vector<std::unique_ptr<Pattern> > patterns)
+    : patterns (std::move (patterns))
+  {}
+
+  // Copy constructor with vector clone
+  TupleStructItemsNoRange (TupleStructItemsNoRange const &other)
+  {
+    patterns.reserve (other.patterns.size ());
+    for (const auto &e : other.patterns)
+      patterns.push_back (e->clone_pattern ());
+  }
+
+  // Overloaded assignment operator with vector clone
+  TupleStructItemsNoRange &operator= (TupleStructItemsNoRange const &other)
+  {
+    patterns.reserve (other.patterns.size ());
+    for (const auto &e : other.patterns)
+      patterns.push_back (e->clone_pattern ());
+
+    return *this;
+  }
+
+  // move constructors
+  TupleStructItemsNoRange (TupleStructItemsNoRange &&other) = default;
+  TupleStructItemsNoRange &operator= (TupleStructItemsNoRange &&other)
+    = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  std::vector<std::unique_ptr<Pattern> > &get_patterns () { return patterns; }
+  const std::vector<std::unique_ptr<Pattern> > &get_patterns () const
+  {
+    return patterns;
+  }
+
+  ItemType get_item_type () const override final { return ItemType::NO_RANGE; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TupleStructItemsNoRange *clone_tuple_struct_items_impl () const override
+  {
+    return new TupleStructItemsNoRange (*this);
+  }
+};
+
+// Class for ranged tuple struct pattern patterns
+class TupleStructItemsRange : public TupleStructItems
+{
+  std::vector<std::unique_ptr<Pattern> > lower_patterns;
+  std::vector<std::unique_ptr<Pattern> > upper_patterns;
+
+public:
+  TupleStructItemsRange (std::vector<std::unique_ptr<Pattern> > lower_patterns,
+			 std::vector<std::unique_ptr<Pattern> > upper_patterns)
+    : lower_patterns (std::move (lower_patterns)),
+      upper_patterns (std::move (upper_patterns))
+  {}
+
+  // Copy constructor with vector clone
+  TupleStructItemsRange (TupleStructItemsRange const &other)
+  {
+    lower_patterns.reserve (other.lower_patterns.size ());
+    for (const auto &e : other.lower_patterns)
+      lower_patterns.push_back (e->clone_pattern ());
+
+    upper_patterns.reserve (other.upper_patterns.size ());
+    for (const auto &e : other.upper_patterns)
+      upper_patterns.push_back (e->clone_pattern ());
+  }
+
+  // Overloaded assignment operator to clone
+  TupleStructItemsRange &operator= (TupleStructItemsRange const &other)
+  {
+    lower_patterns.reserve (other.lower_patterns.size ());
+    for (const auto &e : other.lower_patterns)
+      lower_patterns.push_back (e->clone_pattern ());
+
+    upper_patterns.reserve (other.upper_patterns.size ());
+    for (const auto &e : other.upper_patterns)
+      upper_patterns.push_back (e->clone_pattern ());
+
+    return *this;
+  }
+
+  // move constructors
+  TupleStructItemsRange (TupleStructItemsRange &&other) = default;
+  TupleStructItemsRange &operator= (TupleStructItemsRange &&other) = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  std::vector<std::unique_ptr<Pattern> > &get_lower_patterns ()
+  {
+    return lower_patterns;
+  }
+  const std::vector<std::unique_ptr<Pattern> > &get_lower_patterns () const
+  {
+    return lower_patterns;
+  }
+
+  // TODO: seems kinda dodgy. Think of better way.
+  std::vector<std::unique_ptr<Pattern> > &get_upper_patterns ()
+  {
+    return upper_patterns;
+  }
+  const std::vector<std::unique_ptr<Pattern> > &get_upper_patterns () const
+  {
+    return upper_patterns;
+  }
+
+  ItemType get_item_type () const override final { return ItemType::RANGE; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TupleStructItemsRange *clone_tuple_struct_items_impl () const override
+  {
+    return new TupleStructItemsRange (*this);
+  }
+};
+
+// HIR node representing a tuple struct pattern
+class TupleStructPattern : public Pattern
+{
+  PathInExpression path;
+  std::unique_ptr<TupleStructItems> items;
+  Analysis::NodeMapping mappings;
+
+  /* TOOD: should this store location data? current accessor uses path location
+   * data */
+
+public:
+  std::string as_string () const override;
+
+  TupleStructPattern (Analysis::NodeMapping mappings,
+		      PathInExpression tuple_struct_path,
+		      std::unique_ptr<TupleStructItems> items)
+    : path (std::move (tuple_struct_path)), items (std::move (items)),
+      mappings (mappings)
+  {}
+
+  // Copy constructor required to clone
+  TupleStructPattern (TupleStructPattern const &other)
+    : path (other.path), items (other.items->clone_tuple_struct_items ()),
+      mappings (other.mappings)
+  {}
+
+  // Operator overload assignment operator to clone
+  TupleStructPattern &operator= (TupleStructPattern const &other)
+  {
+    path = other.path;
+    items = other.items->clone_tuple_struct_items ();
+    mappings = other.mappings;
+
+    return *this;
+  }
+
+  // move constructors
+  TupleStructPattern (TupleStructPattern &&other) = default;
+  TupleStructPattern &operator= (TupleStructPattern &&other) = default;
+
+  Location get_locus () const override { return path.get_locus (); }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRPatternVisitor &vis) override;
+
+  PathInExpression &get_path () { return path; }
+
+  std::unique_ptr<TupleStructItems> &get_items () { return items; }
+
+  Analysis::NodeMapping get_pattern_mappings () const override final
+  {
+    return mappings;
+  }
+
+  PatternType get_pattern_type () const override final
+  {
+    return PatternType::TUPLE_STRUCT;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TupleStructPattern *clone_pattern_impl () const override
+  {
+    return new TupleStructPattern (*this);
+  }
+};
+
+// Base abstract class representing TuplePattern patterns
+class TuplePatternItems
+{
+public:
+  enum TuplePatternItemType
+  {
+    MULTIPLE,
+    RANGED,
+  };
+
+  virtual ~TuplePatternItems () {}
+
+  // TODO: should this store location data?
+
+  // Unique pointer custom clone function
+  std::unique_ptr<TuplePatternItems> clone_tuple_pattern_items () const
+  {
+    return std::unique_ptr<TuplePatternItems> (
+      clone_tuple_pattern_items_impl ());
+  }
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (HIRFullVisitor &vis) = 0;
+
+  virtual TuplePatternItemType get_pattern_type () const = 0;
+
+protected:
+  // pure virtual clone implementation
+  virtual TuplePatternItems *clone_tuple_pattern_items_impl () const = 0;
+};
+
+// Class representing TuplePattern patterns where there are multiple patterns
+class TuplePatternItemsMultiple : public TuplePatternItems
+{
+  std::vector<std::unique_ptr<Pattern> > patterns;
+
+public:
+  TuplePatternItemsMultiple (std::vector<std::unique_ptr<Pattern> > patterns)
+    : patterns (std::move (patterns))
+  {}
+
+  // Copy constructor with vector clone
+  TuplePatternItemsMultiple (TuplePatternItemsMultiple const &other)
+  {
+    patterns.reserve (other.patterns.size ());
+    for (const auto &e : other.patterns)
+      patterns.push_back (e->clone_pattern ());
+  }
+
+  // Overloaded assignment operator to vector clone
+  TuplePatternItemsMultiple &operator= (TuplePatternItemsMultiple const &other)
+  {
+    patterns.reserve (other.patterns.size ());
+    for (const auto &e : other.patterns)
+      patterns.push_back (e->clone_pattern ());
+
+    return *this;
+  }
+
+  // move constructors
+  TuplePatternItemsMultiple (TuplePatternItemsMultiple &&other) = default;
+  TuplePatternItemsMultiple &operator= (TuplePatternItemsMultiple &&other)
+    = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  TuplePatternItemType get_pattern_type () const override
+  {
+    return TuplePatternItemType::MULTIPLE;
+  }
+
+  std::vector<std::unique_ptr<Pattern> > &get_patterns () { return patterns; }
+  const std::vector<std::unique_ptr<Pattern> > &get_patterns () const
+  {
+    return patterns;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TuplePatternItemsMultiple *clone_tuple_pattern_items_impl () const override
+  {
+    return new TuplePatternItemsMultiple (*this);
+  }
+};
+
+// Class representing TuplePattern patterns where there are a range of patterns
+class TuplePatternItemsRanged : public TuplePatternItems
+{
+  std::vector<std::unique_ptr<Pattern> > lower_patterns;
+  std::vector<std::unique_ptr<Pattern> > upper_patterns;
+
+public:
+  TuplePatternItemsRanged (
+    std::vector<std::unique_ptr<Pattern> > lower_patterns,
+    std::vector<std::unique_ptr<Pattern> > upper_patterns)
+    : lower_patterns (std::move (lower_patterns)),
+      upper_patterns (std::move (upper_patterns))
+  {}
+
+  // Copy constructor with vector clone
+  TuplePatternItemsRanged (TuplePatternItemsRanged const &other)
+  {
+    lower_patterns.reserve (other.lower_patterns.size ());
+    for (const auto &e : other.lower_patterns)
+      lower_patterns.push_back (e->clone_pattern ());
+
+    upper_patterns.reserve (other.upper_patterns.size ());
+    for (const auto &e : other.upper_patterns)
+      upper_patterns.push_back (e->clone_pattern ());
+  }
+
+  // Overloaded assignment operator to clone
+  TuplePatternItemsRanged &operator= (TuplePatternItemsRanged const &other)
+  {
+    lower_patterns.reserve (other.lower_patterns.size ());
+    for (const auto &e : other.lower_patterns)
+      lower_patterns.push_back (e->clone_pattern ());
+
+    upper_patterns.reserve (other.upper_patterns.size ());
+    for (const auto &e : other.upper_patterns)
+      upper_patterns.push_back (e->clone_pattern ());
+
+    return *this;
+  }
+
+  // move constructors
+  TuplePatternItemsRanged (TuplePatternItemsRanged &&other) = default;
+  TuplePatternItemsRanged &operator= (TuplePatternItemsRanged &&other)
+    = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  TuplePatternItemType get_pattern_type () const override
+  {
+    return TuplePatternItemType::RANGED;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TuplePatternItemsRanged *clone_tuple_pattern_items_impl () const override
+  {
+    return new TuplePatternItemsRanged (*this);
+  }
+};
+
+// HIR node representing a tuple pattern
+class TuplePattern : public Pattern
+{
+  std::unique_ptr<TuplePatternItems> items;
+  Location locus;
+  Analysis::NodeMapping mappings;
+
+public:
+  std::string as_string () const override;
+
+  // Returns true if the tuple pattern has items
+  bool has_tuple_pattern_items () const { return items != nullptr; }
+
+  TuplePattern (Analysis::NodeMapping mappings,
+		std::unique_ptr<TuplePatternItems> items, Location locus)
+    : items (std::move (items)), locus (locus), mappings (mappings)
+  {}
+
+  // Copy constructor requires clone
+  TuplePattern (TuplePattern const &other)
+    : items (other.items->clone_tuple_pattern_items ()), locus (other.locus),
+      mappings (other.mappings)
+  {}
+
+  // Overload assignment operator to clone
+  TuplePattern &operator= (TuplePattern const &other)
+  {
+    items = other.items->clone_tuple_pattern_items ();
+    locus = other.locus;
+    mappings = other.mappings;
+
+    return *this;
+  }
+
+  Location get_locus () const override { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRPatternVisitor &vis) override;
+
+  Analysis::NodeMapping get_pattern_mappings () const override final
+  {
+    return mappings;
+  }
+
+  PatternType get_pattern_type () const override final
+  {
+    return PatternType::TUPLE;
+  }
+
+  std::unique_ptr<TuplePatternItems> &get_items () { return items; }
+  const std::unique_ptr<TuplePatternItems> &get_items () const { return items; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TuplePattern *clone_pattern_impl () const override
+  {
+    return new TuplePattern (*this);
+  }
+};
+
+// HIR node representing a pattern in parentheses, used to control precedence
+class GroupedPattern : public Pattern
+{
+  std::unique_ptr<Pattern> pattern_in_parens;
+  Location locus;
+  Analysis::NodeMapping mappings;
+
+public:
+  std::string as_string () const override
+  {
+    return "(" + pattern_in_parens->as_string () + ")";
+  }
+
+  GroupedPattern (Analysis::NodeMapping mappings,
+		  std::unique_ptr<Pattern> pattern_in_parens, Location locus)
+    : pattern_in_parens (std::move (pattern_in_parens)), locus (locus),
+      mappings (mappings)
+  {}
+
+  // Copy constructor uses clone
+  GroupedPattern (GroupedPattern const &other)
+    : pattern_in_parens (other.pattern_in_parens->clone_pattern ()),
+      locus (other.locus), mappings (other.mappings)
+  {}
+
+  // Overload assignment operator to clone
+  GroupedPattern &operator= (GroupedPattern const &other)
+  {
+    pattern_in_parens = other.pattern_in_parens->clone_pattern ();
+    locus = other.locus;
+    mappings = other.mappings;
+
+    return *this;
+  }
+
+  // default move semantics
+  GroupedPattern (GroupedPattern &&other) = default;
+  GroupedPattern &operator= (GroupedPattern &&other) = default;
+
+  Location get_locus () const override { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRPatternVisitor &vis) override;
+
+  Analysis::NodeMapping get_pattern_mappings () const override final
+  {
+    return mappings;
+  }
+
+  PatternType get_pattern_type () const override final
+  {
+    return PatternType::GROUPED;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  GroupedPattern *clone_pattern_impl () const override
+  {
+    return new GroupedPattern (*this);
+  }
+};
+
+// HIR node representing patterns that can match slices and arrays
+class SlicePattern : public Pattern
+{
+  std::vector<std::unique_ptr<Pattern> > items;
+  Location locus;
+  Analysis::NodeMapping mappings;
+
+public:
+  std::string as_string () const override;
+
+  SlicePattern (Analysis::NodeMapping mappings,
+		std::vector<std::unique_ptr<Pattern> > items, Location locus)
+    : items (std::move (items)), locus (locus), mappings (mappings)
+  {}
+
+  // Copy constructor with vector clone
+  SlicePattern (SlicePattern const &other)
+    : locus (other.locus), mappings (other.mappings)
+  {
+    items.reserve (other.items.size ());
+    for (const auto &e : other.items)
+      items.push_back (e->clone_pattern ());
+  }
+
+  // Overloaded assignment operator to vector clone
+  SlicePattern &operator= (SlicePattern const &other)
+  {
+    locus = other.locus;
+    mappings = other.mappings;
+
+    items.reserve (other.items.size ());
+    for (const auto &e : other.items)
+      items.push_back (e->clone_pattern ());
+
+    return *this;
+  }
+
+  // move constructors
+  SlicePattern (SlicePattern &&other) = default;
+  SlicePattern &operator= (SlicePattern &&other) = default;
+
+  Location get_locus () const override { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRPatternVisitor &vis) override;
+
+  Analysis::NodeMapping get_pattern_mappings () const override final
+  {
+    return mappings;
+  }
+
+  PatternType get_pattern_type () const override final
+  {
+    return PatternType::SLICE;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  SlicePattern *clone_pattern_impl () const override
+  {
+    return new SlicePattern (*this);
+  }
+};
+
+// Moved definition to rust-path.h
+class PathPattern;
+
+// Forward decls for paths (defined in rust-path.h)
+class PathInExpression;
+class QualifiedPathInExpression;
+
+} // namespace HIR
+} // namespace Rust
+
+#endif
diff --git a/gcc/rust/hir/tree/rust-hir-stmt.h b/gcc/rust/hir/tree/rust-hir-stmt.h
new file mode 100644
index 00000000000..5247b0aa0f0
--- /dev/null
+++ b/gcc/rust/hir/tree/rust-hir-stmt.h
@@ -0,0 +1,273 @@
+// 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_HIR_STATEMENT_H
+#define RUST_HIR_STATEMENT_H
+
+#include "rust-hir.h"
+#include "rust-hir-path.h"
+#include "rust-hir-expr.h"
+
+namespace Rust {
+namespace HIR {
+// Just a semi-colon, which apparently is a statement.
+class EmptyStmt : public Stmt
+{
+  Location locus;
+
+public:
+  std::string as_string () const override { return std::string (1, ';'); }
+
+  EmptyStmt (Analysis::NodeMapping mappings, Location locus)
+    : Stmt (std::move (mappings)), locus (locus)
+  {}
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+
+  bool is_item () const override final { return false; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  EmptyStmt *clone_stmt_impl () const override { return new EmptyStmt (*this); }
+};
+
+/* Variable assignment let statement - type of "declaration statement" as it
+ * introduces new name into scope */
+class LetStmt : public Stmt
+{
+  // bool has_outer_attrs;
+  AST::AttrVec outer_attrs;
+
+  std::unique_ptr<Pattern> variables_pattern;
+
+  // bool has_type;
+  std::unique_ptr<Type> type;
+
+  // bool has_init_expr;
+  std::unique_ptr<Expr> init_expr;
+
+  Location locus;
+
+public:
+  // Returns whether let statement has outer attributes.
+  bool has_outer_attrs () const { return !outer_attrs.empty (); }
+
+  // Returns whether let statement has a given return type.
+  bool has_type () const { return type != nullptr; }
+
+  // Returns whether let statement has an initialisation expression.
+  bool has_init_expr () const { return init_expr != nullptr; }
+
+  std::string as_string () const override;
+
+  LetStmt (Analysis::NodeMapping mappings,
+	   std::unique_ptr<Pattern> variables_pattern,
+	   std::unique_ptr<Expr> init_expr, std::unique_ptr<Type> type,
+	   AST::AttrVec outer_attrs, Location locus)
+    : Stmt (std::move (mappings)), outer_attrs (std::move (outer_attrs)),
+      variables_pattern (std::move (variables_pattern)),
+      type (std::move (type)), init_expr (std::move (init_expr)), locus (locus)
+  {}
+
+  // Copy constructor with clone
+  LetStmt (LetStmt const &other)
+    : Stmt (other.mappings), outer_attrs (other.outer_attrs),
+      locus (other.locus)
+  {
+    // guard to prevent null dereference (only required if error state)
+    if (other.variables_pattern != nullptr)
+      variables_pattern = other.variables_pattern->clone_pattern ();
+
+    // guard to prevent null dereference (always required)
+    if (other.init_expr != nullptr)
+      init_expr = other.init_expr->clone_expr ();
+    if (other.type != nullptr)
+      type = other.type->clone_type ();
+  }
+
+  // Overloaded assignment operator to clone
+  LetStmt &operator= (LetStmt const &other)
+  {
+    outer_attrs = other.outer_attrs;
+    locus = other.locus;
+
+    // guard to prevent null dereference (only required if error state)
+    if (other.variables_pattern != nullptr)
+      variables_pattern = other.variables_pattern->clone_pattern ();
+    else
+      variables_pattern = nullptr;
+
+    // guard to prevent null dereference (always required)
+    if (other.init_expr != nullptr)
+      init_expr = other.init_expr->clone_expr ();
+    else
+      init_expr = nullptr;
+    if (other.type != nullptr)
+      type = other.type->clone_type ();
+    else
+      type = nullptr;
+
+    return *this;
+  }
+
+  // move constructors
+  LetStmt (LetStmt &&other) = default;
+  LetStmt &operator= (LetStmt &&other) = default;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+
+  HIR::Type *get_type () { return type.get (); }
+
+  HIR::Expr *get_init_expr () { return init_expr.get (); }
+
+  HIR::Pattern *get_pattern () { return variables_pattern.get (); }
+
+  bool is_item () const override final { return false; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  LetStmt *clone_stmt_impl () const override { return new LetStmt (*this); }
+};
+
+/* Abstract base class for expression statements (statements containing an
+ * expression) */
+class ExprStmt : public Stmt
+{
+  // TODO: add any useful virtual functions
+
+  Location locus;
+
+public:
+  Location get_locus () const override final { return locus; }
+
+  bool is_item () const override final { return false; }
+
+protected:
+  ExprStmt (Analysis::NodeMapping mappings, Location locus)
+    : Stmt (std::move (mappings)), locus (locus)
+  {}
+};
+
+/* Statement containing an expression without a block (or, due to technical
+ * difficulties, can only be guaranteed to hold an expression). */
+class ExprStmtWithoutBlock : public ExprStmt
+{
+  std::unique_ptr<Expr> expr;
+
+public:
+  std::string as_string () const override;
+
+  ExprStmtWithoutBlock (Analysis::NodeMapping mappings,
+			std::unique_ptr<Expr> expr, Location locus)
+    : ExprStmt (std::move (mappings), locus), expr (std::move (expr))
+  {}
+
+  // Copy constructor with clone
+  ExprStmtWithoutBlock (ExprStmtWithoutBlock const &other)
+    : ExprStmt (other), expr (other.expr->clone_expr ())
+  {}
+
+  // Overloaded assignment operator to clone
+  ExprStmtWithoutBlock &operator= (ExprStmtWithoutBlock const &other)
+  {
+    ExprStmt::operator= (other);
+    expr = other.expr->clone_expr ();
+
+    return *this;
+  }
+
+  // move constructors
+  ExprStmtWithoutBlock (ExprStmtWithoutBlock &&other) = default;
+  ExprStmtWithoutBlock &operator= (ExprStmtWithoutBlock &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+
+  Expr *get_expr () { return expr.get (); }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ExprStmtWithoutBlock *clone_stmt_impl () const override
+  {
+    return new ExprStmtWithoutBlock (*this);
+  }
+};
+
+// Statement containing an expression with a block
+class ExprStmtWithBlock : public ExprStmt
+{
+  std::unique_ptr<ExprWithBlock> expr;
+  bool must_be_unit;
+
+public:
+  std::string as_string () const override;
+
+  ExprStmtWithBlock (Analysis::NodeMapping mappings,
+		     std::unique_ptr<ExprWithBlock> expr, Location locus,
+		     bool must_be_unit)
+    : ExprStmt (std::move (mappings), locus), expr (std::move (expr)),
+      must_be_unit (must_be_unit)
+  {}
+
+  // Copy constructor with clone
+  ExprStmtWithBlock (ExprStmtWithBlock const &other)
+    : ExprStmt (other), expr (other.expr->clone_expr_with_block ())
+  {}
+
+  // Overloaded assignment operator to clone
+  ExprStmtWithBlock &operator= (ExprStmtWithBlock const &other)
+  {
+    ExprStmt::operator= (other);
+    expr = other.expr->clone_expr_with_block ();
+
+    return *this;
+  }
+
+  // move constructors
+  ExprStmtWithBlock (ExprStmtWithBlock &&other) = default;
+  ExprStmtWithBlock &operator= (ExprStmtWithBlock &&other) = default;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRStmtVisitor &vis) override;
+
+  ExprWithBlock *get_expr () { return expr.get (); }
+
+  bool is_unit_check_needed () const override { return must_be_unit; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ExprStmtWithBlock *clone_stmt_impl () const override
+  {
+    return new ExprStmtWithBlock (*this);
+  }
+};
+
+} // namespace HIR
+} // namespace Rust
+
+#endif
diff --git a/gcc/rust/hir/tree/rust-hir-type.h b/gcc/rust/hir/tree/rust-hir-type.h
new file mode 100644
index 00000000000..0d2e7436acc
--- /dev/null
+++ b/gcc/rust/hir/tree/rust-hir-type.h
@@ -0,0 +1,860 @@
+// 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_HIR_TYPE_H
+#define RUST_HIR_TYPE_H
+
+#include "rust-common.h"
+#include "rust-hir.h"
+#include "rust-hir-path.h"
+
+namespace Rust {
+namespace HIR {
+// definitions moved to rust-ast.h
+class TypeParamBound;
+class Lifetime;
+
+// A trait bound
+class TraitBound : public TypeParamBound
+{
+  bool in_parens;
+  bool opening_question_mark;
+  std::vector<LifetimeParam> for_lifetimes;
+  TypePath type_path;
+  Location locus;
+
+  Analysis::NodeMapping mappings;
+
+public:
+  // Returns whether trait bound has "for" lifetimes
+  bool has_for_lifetimes () const { return !for_lifetimes.empty (); }
+
+  TraitBound (Analysis::NodeMapping mapping, TypePath type_path, Location locus,
+	      bool in_parens = false, bool opening_question_mark = false,
+	      std::vector<LifetimeParam> for_lifetimes
+	      = std::vector<LifetimeParam> ())
+    : in_parens (in_parens), opening_question_mark (opening_question_mark),
+      for_lifetimes (std::move (for_lifetimes)),
+      type_path (std::move (type_path)), locus (locus), mappings (mapping)
+  {}
+
+  std::string as_string () const override;
+
+  Location get_locus () const override final { return locus; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  Analysis::NodeMapping get_mappings () const override final
+  {
+    return mappings;
+  }
+
+  BoundType get_bound_type () const final override { return TRAITBOUND; }
+
+  TypePath &get_path () { return type_path; }
+
+  const TypePath &get_path () const { return type_path; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TraitBound *clone_type_param_bound_impl () const override
+  {
+    return new TraitBound (*this);
+  }
+};
+
+// definition moved to rust-ast.h
+class TypeNoBounds;
+
+// An impl trait? Poor reference material here.
+class ImplTraitType : public Type
+{
+  // TypeParamBounds type_param_bounds;
+  // inlined form
+  std::vector<std::unique_ptr<TypeParamBound>> type_param_bounds;
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ImplTraitType *clone_type_impl () const override
+  {
+    return new ImplTraitType (*this);
+  }
+
+public:
+  ImplTraitType (Analysis::NodeMapping mappings,
+		 std::vector<std::unique_ptr<TypeParamBound>> type_param_bounds,
+		 Location locus)
+    : Type (mappings, locus), type_param_bounds (std::move (type_param_bounds))
+  {}
+
+  // copy constructor with vector clone
+  ImplTraitType (ImplTraitType const &other)
+    : Type (other.mappings, other.locus)
+  {
+    type_param_bounds.reserve (other.type_param_bounds.size ());
+    for (const auto &e : other.type_param_bounds)
+      type_param_bounds.push_back (e->clone_type_param_bound ());
+  }
+
+  // overloaded assignment operator to clone
+  ImplTraitType &operator= (ImplTraitType const &other)
+  {
+    locus = other.locus;
+    mappings = other.mappings;
+
+    type_param_bounds.reserve (other.type_param_bounds.size ());
+    for (const auto &e : other.type_param_bounds)
+      type_param_bounds.push_back (e->clone_type_param_bound ());
+
+    return *this;
+  }
+
+  // move constructors
+  ImplTraitType (ImplTraitType &&other) = default;
+  ImplTraitType &operator= (ImplTraitType &&other) = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRTypeVisitor &vis) override;
+};
+
+// An opaque value of another type that implements a set of traits
+class TraitObjectType : public Type
+{
+  bool has_dyn;
+  std::vector<std::unique_ptr<TypeParamBound>> type_param_bounds;
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TraitObjectType *clone_type_impl () const override
+  {
+    return new TraitObjectType (*this);
+  }
+
+public:
+  TraitObjectType (
+    Analysis::NodeMapping mappings,
+    std::vector<std::unique_ptr<TypeParamBound>> type_param_bounds,
+    Location locus, bool is_dyn_dispatch)
+    : Type (mappings, locus), has_dyn (is_dyn_dispatch),
+      type_param_bounds (std::move (type_param_bounds))
+  {}
+
+  // copy constructor with vector clone
+  TraitObjectType (TraitObjectType const &other)
+    : Type (other.mappings, other.locus), has_dyn (other.has_dyn)
+  {
+    type_param_bounds.reserve (other.type_param_bounds.size ());
+    for (const auto &e : other.type_param_bounds)
+      type_param_bounds.push_back (e->clone_type_param_bound ());
+  }
+
+  // overloaded assignment operator to clone
+  TraitObjectType &operator= (TraitObjectType const &other)
+  {
+    mappings = other.mappings;
+    has_dyn = other.has_dyn;
+    locus = other.locus;
+    type_param_bounds.reserve (other.type_param_bounds.size ());
+    for (const auto &e : other.type_param_bounds)
+      type_param_bounds.push_back (e->clone_type_param_bound ());
+
+    return *this;
+  }
+
+  // move constructors
+  TraitObjectType (TraitObjectType &&other) = default;
+  TraitObjectType &operator= (TraitObjectType &&other) = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRTypeVisitor &vis) override;
+
+  std::vector<std::unique_ptr<TypeParamBound>> &get_type_param_bounds ()
+  {
+    return type_param_bounds;
+  }
+
+  const std::vector<std::unique_ptr<TypeParamBound>> &
+  get_type_param_bounds () const
+  {
+    return type_param_bounds;
+  }
+};
+
+// A type with parentheses around it, used to avoid ambiguity.
+class ParenthesisedType : public TypeNoBounds
+{
+  std::unique_ptr<Type> type_in_parens;
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ParenthesisedType *clone_type_impl () const override
+  {
+    return new ParenthesisedType (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ParenthesisedType *clone_type_no_bounds_impl () const override
+  {
+    return new ParenthesisedType (*this);
+  }
+
+public:
+  // Constructor uses Type pointer for polymorphism
+  ParenthesisedType (Analysis::NodeMapping mappings,
+		     std::unique_ptr<Type> type_inside_parens, Location locus)
+    : TypeNoBounds (mappings, locus),
+      type_in_parens (std::move (type_inside_parens))
+  {}
+
+  /* Copy constructor uses custom deep copy method for type to preserve
+   * polymorphism */
+  ParenthesisedType (ParenthesisedType const &other)
+    : TypeNoBounds (other.mappings, other.locus),
+      type_in_parens (other.type_in_parens->clone_type ())
+  {}
+
+  // overload assignment operator to use custom clone method
+  ParenthesisedType &operator= (ParenthesisedType const &other)
+  {
+    mappings = other.mappings;
+    type_in_parens = other.type_in_parens->clone_type ();
+    locus = other.locus;
+    return *this;
+  }
+
+  // default move semantics
+  ParenthesisedType (ParenthesisedType &&other) = default;
+  ParenthesisedType &operator= (ParenthesisedType &&other) = default;
+
+  std::string as_string () const override
+  {
+    return "(" + type_in_parens->as_string () + ")";
+  }
+
+  // Creates a trait bound (clone of this one's trait bound) - HACK
+  TraitBound *to_trait_bound (bool in_parens ATTRIBUTE_UNUSED) const override
+  {
+    /* NOTE: obviously it is unknown whether the internal type is a trait bound
+     * due to polymorphism, so just let the internal type handle it. As
+     * parenthesised type, it must be in parentheses. */
+    return type_in_parens->to_trait_bound (true);
+  }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRTypeVisitor &vis) override;
+};
+
+// Impl trait with a single bound? Poor reference material here.
+class ImplTraitTypeOneBound : public TypeNoBounds
+{
+  TraitBound trait_bound;
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ImplTraitTypeOneBound *clone_type_impl () const override
+  {
+    return new ImplTraitTypeOneBound (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ImplTraitTypeOneBound *clone_type_no_bounds_impl () const override
+  {
+    return new ImplTraitTypeOneBound (*this);
+  }
+
+public:
+  ImplTraitTypeOneBound (Analysis::NodeMapping mappings, TraitBound trait_bound,
+			 Location locus)
+    : TypeNoBounds (mappings, locus), trait_bound (std::move (trait_bound))
+  {}
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRTypeVisitor &vis) override;
+};
+
+class TypePath; // definition moved to "rust-path.h"
+
+/* A type consisting of the "product" of others (the tuple's elements) in a
+ * specific order */
+class TupleType : public TypeNoBounds
+{
+  std::vector<std::unique_ptr<Type>> elems;
+
+public:
+  // Returns whether the tuple type is the unit type, i.e. has no elements.
+  bool is_unit_type () const { return elems.empty (); }
+
+  TupleType (Analysis::NodeMapping mappings,
+	     std::vector<std::unique_ptr<Type>> elems, Location locus)
+    : TypeNoBounds (mappings, locus), elems (std::move (elems))
+  {}
+
+  // copy constructor with vector clone
+  TupleType (TupleType const &other)
+    : TypeNoBounds (other.mappings, other.locus)
+  {
+    mappings = other.mappings;
+    elems.reserve (other.elems.size ());
+    for (const auto &e : other.elems)
+      elems.push_back (e->clone_type ());
+  }
+
+  // overloaded assignment operator to clone
+  TupleType &operator= (TupleType const &other)
+  {
+    locus = other.locus;
+
+    elems.reserve (other.elems.size ());
+    for (const auto &e : other.elems)
+      elems.push_back (e->clone_type ());
+
+    return *this;
+  }
+
+  // move constructors
+  TupleType (TupleType &&other) = default;
+  TupleType &operator= (TupleType &&other) = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRTypeVisitor &vis) override;
+
+  std::vector<std::unique_ptr<Type>> &get_elems () { return elems; }
+  const std::vector<std::unique_ptr<Type>> &get_elems () const { return elems; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TupleType *clone_type_impl () const override { return new TupleType (*this); }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  TupleType *clone_type_no_bounds_impl () const override
+  {
+    return new TupleType (*this);
+  }
+};
+
+/* A type with no values, representing the result of computations that never
+ * complete. Expressions of NeverType can be coerced into any other types.
+ * Represented as "!". */
+class NeverType : public TypeNoBounds
+{
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  NeverType *clone_type_impl () const override { return new NeverType (*this); }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  NeverType *clone_type_no_bounds_impl () const override
+  {
+    return new NeverType (*this);
+  }
+
+public:
+  NeverType (Analysis::NodeMapping mappings, Location locus)
+    : TypeNoBounds (mappings, locus)
+  {}
+
+  std::string as_string () const override { return "! (never type)"; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRTypeVisitor &vis) override;
+};
+
+// A type consisting of a pointer without safety or liveness guarantees
+class RawPointerType : public TypeNoBounds
+{
+private:
+  Mutability mut;
+  std::unique_ptr<Type> type;
+
+public:
+  // Constructor requires pointer for polymorphism reasons
+  RawPointerType (Analysis::NodeMapping mappings, Mutability mut,
+		  std::unique_ptr<Type> type, Location locus)
+    : TypeNoBounds (mappings, locus), mut (mut), type (std::move (type))
+  {}
+
+  // Copy constructor calls custom polymorphic clone function
+  RawPointerType (RawPointerType const &other)
+    : TypeNoBounds (other.mappings, other.locus), mut (other.mut),
+      type (other.type->clone_type ())
+  {}
+
+  // overload assignment operator to use custom clone method
+  RawPointerType &operator= (RawPointerType const &other)
+  {
+    mappings = other.mappings;
+    mut = other.mut;
+    type = other.type->clone_type ();
+    locus = other.locus;
+    return *this;
+  }
+
+  // default move semantics
+  RawPointerType (RawPointerType &&other) = default;
+  RawPointerType &operator= (RawPointerType &&other) = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRTypeVisitor &vis) override;
+
+  std::unique_ptr<Type> &get_type () { return type; }
+
+  Mutability get_mut () const { return mut; }
+
+  bool is_mut () const { return mut == Mutability::Mut; }
+
+  bool is_const () const { return mut == Mutability::Imm; }
+
+  std::unique_ptr<Type> &get_base_type () { return type; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RawPointerType *clone_type_impl () const override
+  {
+    return new RawPointerType (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  RawPointerType *clone_type_no_bounds_impl () const override
+  {
+    return new RawPointerType (*this);
+  }
+};
+
+// A type pointing to memory owned by another value
+class ReferenceType : public TypeNoBounds
+{
+  // bool has_lifetime; // TODO: handle in lifetime or something?
+  Lifetime lifetime;
+
+  Mutability mut;
+  std::unique_ptr<Type> type;
+
+public:
+  // Returns whether the reference is mutable or immutable.
+  bool is_mut () const { return mut == Mutability::Mut; }
+
+  // Returns whether the reference has a lifetime.
+  bool has_lifetime () const { return !lifetime.is_error (); }
+
+  // Constructor
+  ReferenceType (Analysis::NodeMapping mappings, Mutability mut,
+		 std::unique_ptr<Type> type_no_bounds, Location locus,
+		 Lifetime lifetime)
+    : TypeNoBounds (mappings, locus), lifetime (std::move (lifetime)),
+      mut (mut), type (std::move (type_no_bounds))
+  {}
+
+  // Copy constructor with custom clone method
+  ReferenceType (ReferenceType const &other)
+    : TypeNoBounds (other.mappings, other.locus), lifetime (other.lifetime),
+      mut (other.mut), type (other.type->clone_type ())
+  {}
+
+  // Operator overload assignment operator to custom clone the unique pointer
+  ReferenceType &operator= (ReferenceType const &other)
+  {
+    mappings = other.mappings;
+    lifetime = other.lifetime;
+    mut = other.mut;
+    type = other.type->clone_type ();
+    locus = other.locus;
+
+    return *this;
+  }
+
+  // move constructors
+  ReferenceType (ReferenceType &&other) = default;
+  ReferenceType &operator= (ReferenceType &&other) = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRTypeVisitor &vis) override;
+
+  Lifetime &get_lifetime () { return lifetime; }
+
+  Mutability get_mut () const { return mut; }
+
+  std::unique_ptr<Type> &get_base_type () { return type; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ReferenceType *clone_type_impl () const override
+  {
+    return new ReferenceType (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ReferenceType *clone_type_no_bounds_impl () const override
+  {
+    return new ReferenceType (*this);
+  }
+};
+
+// A fixed-size sequence of elements of a specified type
+class ArrayType : public TypeNoBounds
+{
+  std::unique_ptr<Type> elem_type;
+  std::unique_ptr<Expr> size;
+
+public:
+  // Constructor requires pointers for polymorphism
+  ArrayType (Analysis::NodeMapping mappings, std::unique_ptr<Type> type,
+	     std::unique_ptr<Expr> array_size, Location locus)
+    : TypeNoBounds (mappings, locus), elem_type (std::move (type)),
+      size (std::move (array_size))
+  {}
+
+  // Copy constructor requires deep copies of both unique pointers
+  ArrayType (ArrayType const &other)
+    : TypeNoBounds (other.mappings, other.locus),
+      elem_type (other.elem_type->clone_type ()),
+      size (other.size->clone_expr ())
+  {}
+
+  // Overload assignment operator to deep copy pointers
+  ArrayType &operator= (ArrayType const &other)
+  {
+    mappings = other.mappings;
+    elem_type = other.elem_type->clone_type ();
+    size = other.size->clone_expr ();
+    locus = other.locus;
+    return *this;
+  }
+
+  // move constructors
+  ArrayType (ArrayType &&other) = default;
+  ArrayType &operator= (ArrayType &&other) = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRTypeVisitor &vis) override;
+
+  Type *get_element_type () { return elem_type.get (); }
+
+  Expr *get_size_expr () { return size.get (); }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ArrayType *clone_type_impl () const override { return new ArrayType (*this); }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ArrayType *clone_type_no_bounds_impl () const override
+  {
+    return new ArrayType (*this);
+  }
+};
+
+/* A dynamically-sized type representing a "view" into a sequence of elements of
+ * a type */
+class SliceType : public TypeNoBounds
+{
+  std::unique_ptr<Type> elem_type;
+
+public:
+  // Constructor requires pointer for polymorphism
+  SliceType (Analysis::NodeMapping mappings, std::unique_ptr<Type> type,
+	     Location locus)
+    : TypeNoBounds (mappings, locus), elem_type (std::move (type))
+  {}
+
+  // Copy constructor requires deep copy of Type smart pointer
+  SliceType (SliceType const &other)
+    : TypeNoBounds (other.mappings, other.locus),
+      elem_type (other.elem_type->clone_type ())
+  {}
+
+  // Overload assignment operator to deep copy
+  SliceType &operator= (SliceType const &other)
+  {
+    mappings = other.mappings;
+    elem_type = other.elem_type->clone_type ();
+    locus = other.locus;
+
+    return *this;
+  }
+
+  // move constructors
+  SliceType (SliceType &&other) = default;
+  SliceType &operator= (SliceType &&other) = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRTypeVisitor &vis) override;
+
+  std::unique_ptr<Type> &get_element_type () { return elem_type; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  SliceType *clone_type_impl () const override { return new SliceType (*this); }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  SliceType *clone_type_no_bounds_impl () const override
+  {
+    return new SliceType (*this);
+  }
+};
+
+/* Type used in generic arguments to explicitly request type inference (wildcard
+ * pattern) */
+class InferredType : public TypeNoBounds
+{
+  // e.g. Vec<_> = whatever
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  InferredType *clone_type_impl () const override
+  {
+    return new InferredType (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  InferredType *clone_type_no_bounds_impl () const override
+  {
+    return new InferredType (*this);
+  }
+
+public:
+  InferredType (Analysis::NodeMapping mappings, Location locus)
+    : TypeNoBounds (mappings, locus)
+  {}
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRTypeVisitor &vis) override;
+};
+
+class QualifiedPathInType; // definition moved to "rust-path.h"
+
+// A possibly named param used in a BaseFunctionType
+struct MaybeNamedParam
+{
+public:
+  enum ParamKind
+  {
+    UNNAMED,
+    IDENTIFIER,
+    WILDCARD
+  };
+
+private:
+  std::unique_ptr<Type> param_type;
+
+  ParamKind param_kind;
+  Identifier name; // technically, can be an identifier or '_'
+
+  Location locus;
+
+public:
+  MaybeNamedParam (Identifier name, ParamKind param_kind,
+		   std::unique_ptr<Type> param_type, Location locus)
+    : param_type (std::move (param_type)), param_kind (param_kind),
+      name (std::move (name)), locus (locus)
+  {}
+
+  // Copy constructor with clone
+  MaybeNamedParam (MaybeNamedParam const &other)
+    : param_type (other.param_type->clone_type ()),
+      param_kind (other.param_kind), name (other.name), locus (other.locus)
+  {}
+
+  ~MaybeNamedParam () = default;
+
+  // Overloaded assignment operator with clone
+  MaybeNamedParam &operator= (MaybeNamedParam const &other)
+  {
+    name = other.name;
+    param_kind = other.param_kind;
+    param_type = other.param_type->clone_type ();
+    locus = other.locus;
+
+    return *this;
+  }
+
+  // move constructors
+  MaybeNamedParam (MaybeNamedParam &&other) = default;
+  MaybeNamedParam &operator= (MaybeNamedParam &&other) = default;
+
+  std::string as_string () const;
+
+  // Returns whether the param is in an error state.
+  bool is_error () const { return param_type == nullptr; }
+
+  // Creates an error state param.
+  static MaybeNamedParam create_error ()
+  {
+    return MaybeNamedParam ("", UNNAMED, nullptr, Location ());
+  }
+
+  Location get_locus () const { return locus; }
+
+  std::unique_ptr<Type> &get_type ()
+  {
+    rust_assert (param_type != nullptr);
+    return param_type;
+  }
+
+  ParamKind get_param_kind () const { return param_kind; }
+
+  Identifier get_name () const { return name; }
+};
+
+/* A function pointer type - can be created via coercion from function items and
+ * non- capturing closures. */
+class BareFunctionType : public TypeNoBounds
+{
+  // bool has_for_lifetimes;
+  // ForLifetimes for_lifetimes;
+  std::vector<LifetimeParam> for_lifetimes; // inlined version
+
+  FunctionQualifiers function_qualifiers;
+  std::vector<MaybeNamedParam> params;
+  bool is_variadic;
+
+  std::unique_ptr<Type> return_type; // inlined version
+
+public:
+  // Whether a return type is defined with the function.
+  bool has_return_type () const { return return_type != nullptr; }
+
+  // Whether the function has ForLifetimes.
+  bool has_for_lifetimes () const { return !for_lifetimes.empty (); }
+
+  BareFunctionType (Analysis::NodeMapping mappings,
+		    std::vector<LifetimeParam> lifetime_params,
+		    FunctionQualifiers qualifiers,
+		    std::vector<MaybeNamedParam> named_params, bool is_variadic,
+		    std::unique_ptr<Type> type, Location locus)
+    : TypeNoBounds (mappings, locus),
+      for_lifetimes (std::move (lifetime_params)),
+      function_qualifiers (std::move (qualifiers)),
+      params (std::move (named_params)), is_variadic (is_variadic),
+      return_type (std::move (type))
+  {}
+
+  // Copy constructor with clone
+  BareFunctionType (BareFunctionType const &other)
+    : TypeNoBounds (other.mappings, other.locus),
+      for_lifetimes (other.for_lifetimes),
+      function_qualifiers (other.function_qualifiers), params (other.params),
+      is_variadic (other.is_variadic),
+      return_type (other.return_type->clone_type ())
+  {}
+
+  // Overload assignment operator to deep copy
+  BareFunctionType &operator= (BareFunctionType const &other)
+  {
+    mappings = other.mappings;
+    for_lifetimes = other.for_lifetimes;
+    function_qualifiers = other.function_qualifiers;
+    params = other.params;
+    is_variadic = other.is_variadic;
+    return_type = other.return_type->clone_type ();
+    locus = other.locus;
+
+    return *this;
+  }
+
+  // move constructors
+  BareFunctionType (BareFunctionType &&other) = default;
+  BareFunctionType &operator= (BareFunctionType &&other) = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRTypeVisitor &vis) override;
+
+  std::vector<MaybeNamedParam> &get_function_params () { return params; }
+  const std::vector<MaybeNamedParam> &get_function_params () const
+  {
+    return params;
+  }
+
+  // TODO: would a "vis_type" be better?
+  std::unique_ptr<Type> &get_return_type ()
+  {
+    rust_assert (has_return_type ());
+    return return_type;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  BareFunctionType *clone_type_impl () const override
+  {
+    return new BareFunctionType (*this);
+  }
+
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  BareFunctionType *clone_type_no_bounds_impl () const override
+  {
+    return new BareFunctionType (*this);
+  }
+};
+
+/* TODO: possible types
+ * struct type?
+ * "enum" (tagged union) type?
+ * C-like union type?
+ * function item type?
+ * closure expression types?
+ * primitive types (bool, int, float, char, str (the slice))
+ * Although supposedly TypePaths are used to reference these types (including
+ * primitives) */
+
+/* FIXME: Incomplete spec references:
+ *  anonymous type parameters, aka "impl Trait in argument position" - impl then
+ * trait bounds abstract return types, aka "impl Trait in return position" -
+ * impl then trait bounds */
+} // namespace HIR
+} // namespace Rust
+
+#endif
diff --git a/gcc/rust/hir/tree/rust-hir-visitor.h b/gcc/rust/hir/tree/rust-hir-visitor.h
new file mode 100644
index 00000000000..b3c0b9359cc
--- /dev/null
+++ b/gcc/rust/hir/tree/rust-hir-visitor.h
@@ -0,0 +1,493 @@
+// 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_HIR_VISITOR_H
+#define RUST_HIR_VISITOR_H
+
+#include "rust-hir-full-decls.h"
+
+namespace Rust {
+namespace HIR {
+
+class HIRFullVisitor
+{
+public:
+  virtual void visit (Lifetime &lifetime) = 0;
+  virtual void visit (LifetimeParam &lifetime_param) = 0;
+  virtual void visit (PathInExpression &path) = 0;
+  virtual void visit (TypePathSegment &segment) = 0;
+  virtual void visit (TypePathSegmentGeneric &segment) = 0;
+  virtual void visit (TypePathSegmentFunction &segment) = 0;
+  virtual void visit (TypePath &path) = 0;
+  virtual void visit (QualifiedPathInExpression &path) = 0;
+  virtual void visit (QualifiedPathInType &path) = 0;
+  virtual void visit (LiteralExpr &expr) = 0;
+  virtual void visit (BorrowExpr &expr) = 0;
+  virtual void visit (DereferenceExpr &expr) = 0;
+  virtual void visit (ErrorPropagationExpr &expr) = 0;
+  virtual void visit (NegationExpr &expr) = 0;
+  virtual void visit (ArithmeticOrLogicalExpr &expr) = 0;
+  virtual void visit (ComparisonExpr &expr) = 0;
+  virtual void visit (LazyBooleanExpr &expr) = 0;
+  virtual void visit (TypeCastExpr &expr) = 0;
+  virtual void visit (AssignmentExpr &expr) = 0;
+  virtual void visit (CompoundAssignmentExpr &expr) = 0;
+  virtual void visit (GroupedExpr &expr) = 0;
+  virtual void visit (ArrayElemsValues &elems) = 0;
+  virtual void visit (ArrayElemsCopied &elems) = 0;
+  virtual void visit (ArrayExpr &expr) = 0;
+  virtual void visit (ArrayIndexExpr &expr) = 0;
+  virtual void visit (TupleExpr &expr) = 0;
+  virtual void visit (TupleIndexExpr &expr) = 0;
+  virtual void visit (StructExprStruct &expr) = 0;
+  virtual void visit (StructExprFieldIdentifier &field) = 0;
+  virtual void visit (StructExprFieldIdentifierValue &field) = 0;
+  virtual void visit (StructExprFieldIndexValue &field) = 0;
+  virtual void visit (StructExprStructFields &expr) = 0;
+  virtual void visit (StructExprStructBase &expr) = 0;
+  virtual void visit (CallExpr &expr) = 0;
+  virtual void visit (MethodCallExpr &expr) = 0;
+  virtual void visit (FieldAccessExpr &expr) = 0;
+  virtual void visit (ClosureExprInner &expr) = 0;
+  virtual void visit (BlockExpr &expr) = 0;
+  virtual void visit (ClosureExprInnerTyped &expr) = 0;
+  virtual void visit (ContinueExpr &expr) = 0;
+  virtual void visit (BreakExpr &expr) = 0;
+  virtual void visit (RangeFromToExpr &expr) = 0;
+  virtual void visit (RangeFromExpr &expr) = 0;
+  virtual void visit (RangeToExpr &expr) = 0;
+  virtual void visit (RangeFullExpr &expr) = 0;
+  virtual void visit (RangeFromToInclExpr &expr) = 0;
+  virtual void visit (RangeToInclExpr &expr) = 0;
+  virtual void visit (ReturnExpr &expr) = 0;
+  virtual void visit (UnsafeBlockExpr &expr) = 0;
+  virtual void visit (LoopExpr &expr) = 0;
+  virtual void visit (WhileLoopExpr &expr) = 0;
+  virtual void visit (WhileLetLoopExpr &expr) = 0;
+  virtual void visit (ForLoopExpr &expr) = 0;
+  virtual void visit (IfExpr &expr) = 0;
+  virtual void visit (IfExprConseqElse &expr) = 0;
+  virtual void visit (IfExprConseqIf &expr) = 0;
+  virtual void visit (IfExprConseqIfLet &expr) = 0;
+  virtual void visit (IfLetExpr &expr) = 0;
+  virtual void visit (IfLetExprConseqElse &expr) = 0;
+  virtual void visit (IfLetExprConseqIf &expr) = 0;
+  virtual void visit (IfLetExprConseqIfLet &expr) = 0;
+  virtual void visit (MatchExpr &expr) = 0;
+  virtual void visit (AwaitExpr &expr) = 0;
+  virtual void visit (AsyncBlockExpr &expr) = 0;
+  virtual void visit (TypeParam &param) = 0;
+  virtual void visit (ConstGenericParam &param) = 0;
+  virtual void visit (LifetimeWhereClauseItem &item) = 0;
+  virtual void visit (TypeBoundWhereClauseItem &item) = 0;
+  virtual void visit (Module &module) = 0;
+  virtual void visit (ExternCrate &crate) = 0;
+  virtual void visit (UseTreeGlob &use_tree) = 0;
+  virtual void visit (UseTreeList &use_tree) = 0;
+  virtual void visit (UseTreeRebind &use_tree) = 0;
+  virtual void visit (UseDeclaration &use_decl) = 0;
+  virtual void visit (Function &function) = 0;
+  virtual void visit (TypeAlias &type_alias) = 0;
+  virtual void visit (StructStruct &struct_item) = 0;
+  virtual void visit (TupleStruct &tuple_struct) = 0;
+  virtual void visit (EnumItem &item) = 0;
+  virtual void visit (EnumItemTuple &item) = 0;
+  virtual void visit (EnumItemStruct &item) = 0;
+  virtual void visit (EnumItemDiscriminant &item) = 0;
+  virtual void visit (Enum &enum_item) = 0;
+  virtual void visit (Union &union_item) = 0;
+  virtual void visit (ConstantItem &const_item) = 0;
+  virtual void visit (StaticItem &static_item) = 0;
+  virtual void visit (TraitItemFunc &item) = 0;
+  virtual void visit (TraitItemConst &item) = 0;
+  virtual void visit (TraitItemType &item) = 0;
+  virtual void visit (Trait &trait) = 0;
+  virtual void visit (ImplBlock &impl) = 0;
+  virtual void visit (ExternalStaticItem &item) = 0;
+  virtual void visit (ExternalFunctionItem &item) = 0;
+  virtual void visit (ExternBlock &block) = 0;
+  virtual void visit (LiteralPattern &pattern) = 0;
+  virtual void visit (IdentifierPattern &pattern) = 0;
+  virtual void visit (WildcardPattern &pattern) = 0;
+  virtual void visit (RangePatternBoundLiteral &bound) = 0;
+  virtual void visit (RangePatternBoundPath &bound) = 0;
+  virtual void visit (RangePatternBoundQualPath &bound) = 0;
+  virtual void visit (RangePattern &pattern) = 0;
+  virtual void visit (ReferencePattern &pattern) = 0;
+  virtual void visit (StructPatternFieldTuplePat &field) = 0;
+  virtual void visit (StructPatternFieldIdentPat &field) = 0;
+  virtual void visit (StructPatternFieldIdent &field) = 0;
+  virtual void visit (StructPattern &pattern) = 0;
+  virtual void visit (TupleStructItemsNoRange &tuple_items) = 0;
+  virtual void visit (TupleStructItemsRange &tuple_items) = 0;
+  virtual void visit (TupleStructPattern &pattern) = 0;
+  virtual void visit (TuplePatternItemsMultiple &tuple_items) = 0;
+  virtual void visit (TuplePatternItemsRanged &tuple_items) = 0;
+  virtual void visit (TuplePattern &pattern) = 0;
+  virtual void visit (GroupedPattern &pattern) = 0;
+  virtual void visit (SlicePattern &pattern) = 0;
+  virtual void visit (EmptyStmt &stmt) = 0;
+  virtual void visit (LetStmt &stmt) = 0;
+  virtual void visit (ExprStmtWithoutBlock &stmt) = 0;
+  virtual void visit (ExprStmtWithBlock &stmt) = 0;
+  virtual void visit (TraitBound &bound) = 0;
+  virtual void visit (ImplTraitType &type) = 0;
+  virtual void visit (TraitObjectType &type) = 0;
+  virtual void visit (ParenthesisedType &type) = 0;
+  virtual void visit (ImplTraitTypeOneBound &type) = 0;
+  virtual void visit (TupleType &type) = 0;
+  virtual void visit (NeverType &type) = 0;
+  virtual void visit (RawPointerType &type) = 0;
+  virtual void visit (ReferenceType &type) = 0;
+  virtual void visit (ArrayType &type) = 0;
+  virtual void visit (SliceType &type) = 0;
+  virtual void visit (InferredType &type) = 0;
+  virtual void visit (BareFunctionType &type) = 0;
+};
+
+class HIRFullVisitorBase : public HIRFullVisitor
+{
+public:
+  virtual ~HIRFullVisitorBase () {}
+
+  virtual void visit (Lifetime &) override {}
+  virtual void visit (LifetimeParam &) override {}
+  virtual void visit (PathInExpression &) override {}
+  virtual void visit (TypePathSegment &) override {}
+  virtual void visit (TypePathSegmentGeneric &) override {}
+  virtual void visit (TypePathSegmentFunction &) override {}
+  virtual void visit (TypePath &) override {}
+  virtual void visit (QualifiedPathInExpression &) override {}
+  virtual void visit (QualifiedPathInType &) override {}
+
+  virtual void visit (LiteralExpr &) override {}
+  virtual void visit (BorrowExpr &) override {}
+  virtual void visit (DereferenceExpr &) override {}
+  virtual void visit (ErrorPropagationExpr &) override {}
+  virtual void visit (NegationExpr &) override {}
+  virtual void visit (ArithmeticOrLogicalExpr &) override {}
+  virtual void visit (ComparisonExpr &) override {}
+  virtual void visit (LazyBooleanExpr &) override {}
+  virtual void visit (TypeCastExpr &) override {}
+  virtual void visit (AssignmentExpr &) override {}
+  virtual void visit (CompoundAssignmentExpr &) override {}
+  virtual void visit (GroupedExpr &) override {}
+
+  virtual void visit (ArrayElemsValues &) override {}
+  virtual void visit (ArrayElemsCopied &) override {}
+  virtual void visit (ArrayExpr &) override {}
+  virtual void visit (ArrayIndexExpr &) override {}
+  virtual void visit (TupleExpr &) override {}
+  virtual void visit (TupleIndexExpr &) override {}
+  virtual void visit (StructExprStruct &) override {}
+
+  virtual void visit (StructExprFieldIdentifier &) override {}
+  virtual void visit (StructExprFieldIdentifierValue &) override {}
+
+  virtual void visit (StructExprFieldIndexValue &) override {}
+  virtual void visit (StructExprStructFields &) override {}
+  virtual void visit (StructExprStructBase &) override {}
+
+  virtual void visit (CallExpr &) override {}
+  virtual void visit (MethodCallExpr &) override {}
+  virtual void visit (FieldAccessExpr &) override {}
+  virtual void visit (ClosureExprInner &) override {}
+  virtual void visit (BlockExpr &) override {}
+  virtual void visit (ClosureExprInnerTyped &) override {}
+  virtual void visit (ContinueExpr &) override {}
+  virtual void visit (BreakExpr &) override {}
+  virtual void visit (RangeFromToExpr &) override {}
+  virtual void visit (RangeFromExpr &) override {}
+  virtual void visit (RangeToExpr &) override {}
+  virtual void visit (RangeFullExpr &) override {}
+  virtual void visit (RangeFromToInclExpr &) override {}
+  virtual void visit (RangeToInclExpr &) override {}
+  virtual void visit (ReturnExpr &) override {}
+  virtual void visit (UnsafeBlockExpr &) override {}
+  virtual void visit (LoopExpr &) override {}
+  virtual void visit (WhileLoopExpr &) override {}
+  virtual void visit (WhileLetLoopExpr &) override {}
+  virtual void visit (ForLoopExpr &) override {}
+  virtual void visit (IfExpr &) override {}
+  virtual void visit (IfExprConseqElse &) override {}
+  virtual void visit (IfExprConseqIf &) override {}
+  virtual void visit (IfExprConseqIfLet &) override {}
+  virtual void visit (IfLetExpr &) override {}
+  virtual void visit (IfLetExprConseqElse &) override {}
+  virtual void visit (IfLetExprConseqIf &) override {}
+  virtual void visit (IfLetExprConseqIfLet &) override {}
+
+  virtual void visit (MatchExpr &) override {}
+  virtual void visit (AwaitExpr &) override {}
+  virtual void visit (AsyncBlockExpr &) override {}
+
+  virtual void visit (TypeParam &) override {}
+  virtual void visit (ConstGenericParam &) override {}
+
+  virtual void visit (LifetimeWhereClauseItem &) override {}
+  virtual void visit (TypeBoundWhereClauseItem &) override {}
+  virtual void visit (Module &) override {}
+  virtual void visit (ExternCrate &) override {}
+
+  virtual void visit (UseTreeGlob &) override {}
+  virtual void visit (UseTreeList &) override {}
+  virtual void visit (UseTreeRebind &) override {}
+  virtual void visit (UseDeclaration &) override {}
+  virtual void visit (Function &) override {}
+  virtual void visit (TypeAlias &) override {}
+  virtual void visit (StructStruct &) override {}
+  virtual void visit (TupleStruct &) override {}
+  virtual void visit (EnumItem &) override {}
+  virtual void visit (EnumItemTuple &) override {}
+  virtual void visit (EnumItemStruct &) override {}
+  virtual void visit (EnumItemDiscriminant &) override {}
+  virtual void visit (Enum &) override {}
+  virtual void visit (Union &) override {}
+  virtual void visit (ConstantItem &) override {}
+  virtual void visit (StaticItem &) override {}
+  virtual void visit (TraitItemFunc &) override {}
+  virtual void visit (TraitItemConst &) override {}
+  virtual void visit (TraitItemType &) override {}
+  virtual void visit (Trait &) override {}
+  virtual void visit (ImplBlock &) override {}
+
+  virtual void visit (ExternalStaticItem &) override {}
+  virtual void visit (ExternalFunctionItem &) override {}
+  virtual void visit (ExternBlock &) override {}
+
+  virtual void visit (LiteralPattern &) override {}
+  virtual void visit (IdentifierPattern &) override {}
+  virtual void visit (WildcardPattern &) override {}
+
+  virtual void visit (RangePatternBoundLiteral &) override {}
+  virtual void visit (RangePatternBoundPath &) override {}
+  virtual void visit (RangePatternBoundQualPath &) override {}
+  virtual void visit (RangePattern &) override {}
+  virtual void visit (ReferencePattern &) override {}
+
+  virtual void visit (StructPatternFieldTuplePat &) override {}
+  virtual void visit (StructPatternFieldIdentPat &) override {}
+  virtual void visit (StructPatternFieldIdent &) override {}
+  virtual void visit (StructPattern &) override {}
+
+  virtual void visit (TupleStructItemsNoRange &) override {}
+  virtual void visit (TupleStructItemsRange &) override {}
+  virtual void visit (TupleStructPattern &) override {}
+
+  virtual void visit (TuplePatternItemsMultiple &) override {}
+  virtual void visit (TuplePatternItemsRanged &) override {}
+  virtual void visit (TuplePattern &) override {}
+  virtual void visit (GroupedPattern &) override {}
+  virtual void visit (SlicePattern &) override {}
+
+  virtual void visit (EmptyStmt &) override {}
+  virtual void visit (LetStmt &) override {}
+  virtual void visit (ExprStmtWithoutBlock &) override {}
+  virtual void visit (ExprStmtWithBlock &) override {}
+
+  virtual void visit (TraitBound &) override {}
+  virtual void visit (ImplTraitType &) override {}
+  virtual void visit (TraitObjectType &) override {}
+  virtual void visit (ParenthesisedType &) override {}
+  virtual void visit (ImplTraitTypeOneBound &) override {}
+  virtual void visit (TupleType &) override {}
+  virtual void visit (NeverType &) override {}
+  virtual void visit (RawPointerType &) override {}
+  virtual void visit (ReferenceType &) override {}
+  virtual void visit (ArrayType &) override {}
+  virtual void visit (SliceType &) override {}
+  virtual void visit (InferredType &) override {}
+  virtual void visit (BareFunctionType &) override {}
+};
+
+class HIRExternalItemVisitor
+{
+public:
+  virtual void visit (ExternalStaticItem &item) = 0;
+  virtual void visit (ExternalFunctionItem &item) = 0;
+};
+
+class HIRTraitItemVisitor
+{
+public:
+  virtual void visit (TraitItemFunc &item) = 0;
+  virtual void visit (TraitItemConst &item) = 0;
+  virtual void visit (TraitItemType &item) = 0;
+};
+
+class HIRVisItemVisitor
+{
+public:
+  virtual void visit (Module &module) = 0;
+  virtual void visit (ExternCrate &crate) = 0;
+  virtual void visit (UseDeclaration &use_decl) = 0;
+  virtual void visit (Function &function) = 0;
+  virtual void visit (TypeAlias &type_alias) = 0;
+  virtual void visit (StructStruct &struct_item) = 0;
+  virtual void visit (TupleStruct &tuple_struct) = 0;
+  virtual void visit (Enum &enum_item) = 0;
+  virtual void visit (Union &union_item) = 0;
+  virtual void visit (ConstantItem &const_item) = 0;
+  virtual void visit (StaticItem &static_item) = 0;
+  virtual void visit (Trait &trait) = 0;
+  virtual void visit (ImplBlock &impl) = 0;
+  virtual void visit (ExternBlock &block) = 0;
+};
+
+class HIRImplVisitor
+{
+public:
+  virtual void visit (Function &function) = 0;
+  virtual void visit (ConstantItem &const_item) = 0;
+  virtual void visit (TypeAlias &type_alias) = 0;
+};
+
+class HIRTypeVisitor
+{
+public:
+  virtual void visit (TypePathSegmentFunction &segment) = 0;
+  virtual void visit (TypePath &path) = 0;
+  virtual void visit (QualifiedPathInType &path) = 0;
+  virtual void visit (TraitBound &bound) = 0;
+  virtual void visit (ImplTraitType &type) = 0;
+  virtual void visit (TraitObjectType &type) = 0;
+  virtual void visit (ParenthesisedType &type) = 0;
+  virtual void visit (ImplTraitTypeOneBound &type) = 0;
+  virtual void visit (TupleType &type) = 0;
+  virtual void visit (NeverType &type) = 0;
+  virtual void visit (RawPointerType &type) = 0;
+  virtual void visit (ReferenceType &type) = 0;
+  virtual void visit (ArrayType &type) = 0;
+  virtual void visit (SliceType &type) = 0;
+  virtual void visit (InferredType &type) = 0;
+  virtual void visit (BareFunctionType &type) = 0;
+};
+
+class HIRStmtVisitor
+{
+public:
+  virtual void visit (EnumItemTuple &) = 0;
+  virtual void visit (EnumItemStruct &) = 0;
+  virtual void visit (EnumItem &item) = 0;
+  virtual void visit (TupleStruct &tuple_struct) = 0;
+  virtual void visit (EnumItemDiscriminant &) = 0;
+  virtual void visit (TypePathSegmentFunction &segment) = 0;
+  virtual void visit (TypePath &path) = 0;
+  virtual void visit (QualifiedPathInType &path) = 0;
+  virtual void visit (Module &module) = 0;
+  virtual void visit (ExternCrate &crate) = 0;
+  virtual void visit (UseDeclaration &use_decl) = 0;
+  virtual void visit (Function &function) = 0;
+  virtual void visit (TypeAlias &type_alias) = 0;
+  virtual void visit (StructStruct &struct_item) = 0;
+  virtual void visit (Enum &enum_item) = 0;
+  virtual void visit (Union &union_item) = 0;
+  virtual void visit (ConstantItem &const_item) = 0;
+  virtual void visit (StaticItem &static_item) = 0;
+  virtual void visit (Trait &trait) = 0;
+  virtual void visit (ImplBlock &impl) = 0;
+  virtual void visit (ExternBlock &block) = 0;
+  virtual void visit (EmptyStmt &stmt) = 0;
+  virtual void visit (LetStmt &stmt) = 0;
+  virtual void visit (ExprStmtWithoutBlock &stmt) = 0;
+  virtual void visit (ExprStmtWithBlock &stmt) = 0;
+};
+
+class HIRExpressionVisitor
+{
+public:
+  // These are StructExprField
+  // Added because of CompileStructExprField
+  virtual void visit (StructExprFieldIdentifier &field) = 0;
+  virtual void visit (StructExprFieldIdentifierValue &field) = 0;
+  virtual void visit (StructExprFieldIndexValue &field) = 0;
+
+  virtual void visit (HIR::QualifiedPathInExpression &expr) = 0;
+  virtual void visit (HIR::PathInExpression &expr) = 0;
+  virtual void visit (ClosureExprInnerTyped &) = 0;
+  virtual void visit (ClosureExprInner &expr) = 0;
+  virtual void visit (StructExprStructFields &) = 0;
+  virtual void visit (StructExprStruct &) = 0;
+  virtual void visit (LiteralExpr &expr) = 0;
+  virtual void visit (BorrowExpr &expr) = 0;
+  virtual void visit (DereferenceExpr &expr) = 0;
+  virtual void visit (ErrorPropagationExpr &expr) = 0;
+  virtual void visit (NegationExpr &expr) = 0;
+  virtual void visit (ArithmeticOrLogicalExpr &expr) = 0;
+  virtual void visit (ComparisonExpr &expr) = 0;
+  virtual void visit (LazyBooleanExpr &expr) = 0;
+  virtual void visit (TypeCastExpr &expr) = 0;
+  virtual void visit (AssignmentExpr &expr) = 0;
+  virtual void visit (CompoundAssignmentExpr &expr) = 0;
+  virtual void visit (GroupedExpr &expr) = 0;
+  virtual void visit (ArrayExpr &expr) = 0;
+  virtual void visit (ArrayIndexExpr &expr) = 0;
+  virtual void visit (TupleExpr &expr) = 0;
+  virtual void visit (TupleIndexExpr &expr) = 0;
+  virtual void visit (CallExpr &expr) = 0;
+  virtual void visit (MethodCallExpr &expr) = 0;
+  virtual void visit (FieldAccessExpr &expr) = 0;
+  virtual void visit (BlockExpr &expr) = 0;
+  virtual void visit (ContinueExpr &expr) = 0;
+  virtual void visit (BreakExpr &expr) = 0;
+  virtual void visit (RangeFromToExpr &expr) = 0;
+  virtual void visit (RangeFromExpr &expr) = 0;
+  virtual void visit (RangeToExpr &expr) = 0;
+  virtual void visit (RangeFullExpr &expr) = 0;
+  virtual void visit (RangeFromToInclExpr &expr) = 0;
+  virtual void visit (RangeToInclExpr &expr) = 0;
+  virtual void visit (ReturnExpr &expr) = 0;
+  virtual void visit (UnsafeBlockExpr &expr) = 0;
+  virtual void visit (LoopExpr &expr) = 0;
+  virtual void visit (WhileLoopExpr &expr) = 0;
+  virtual void visit (WhileLetLoopExpr &expr) = 0;
+  virtual void visit (ForLoopExpr &expr) = 0;
+  virtual void visit (IfExpr &expr) = 0;
+  virtual void visit (IfExprConseqElse &expr) = 0;
+  virtual void visit (IfExprConseqIf &expr) = 0;
+  virtual void visit (IfExprConseqIfLet &expr) = 0;
+  virtual void visit (IfLetExpr &expr) = 0;
+  virtual void visit (IfLetExprConseqElse &expr) = 0;
+  virtual void visit (IfLetExprConseqIf &expr) = 0;
+  virtual void visit (IfLetExprConseqIfLet &expr) = 0;
+  virtual void visit (MatchExpr &expr) = 0;
+  virtual void visit (AwaitExpr &expr) = 0;
+  virtual void visit (AsyncBlockExpr &expr) = 0;
+};
+
+class HIRPatternVisitor
+{
+public:
+  virtual void visit (GroupedPattern &) = 0;
+  virtual void visit (IdentifierPattern &) = 0;
+  virtual void visit (LiteralPattern &) = 0;
+  virtual void visit (PathInExpression &) = 0;
+  virtual void visit (QualifiedPathInExpression &) = 0;
+  virtual void visit (RangePattern &) = 0;
+  virtual void visit (ReferencePattern &) = 0;
+  virtual void visit (SlicePattern &) = 0;
+  virtual void visit (StructPattern &) = 0;
+  virtual void visit (TuplePattern &) = 0;
+  virtual void visit (TupleStructPattern &) = 0;
+  virtual void visit (WildcardPattern &) = 0;
+};
+
+} // namespace HIR
+} // namespace Rust
+
+#endif
diff --git a/gcc/rust/hir/tree/rust-hir.h b/gcc/rust/hir/tree/rust-hir.h
new file mode 100644
index 00000000000..927ac06fc4a
--- /dev/null
+++ b/gcc/rust/hir/tree/rust-hir.h
@@ -0,0 +1,921 @@
+// 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_HIR_BASE_H
+#define RUST_HIR_BASE_H
+
+#include "rust-ast.h"
+#include "rust-system.h"
+#include "rust-token.h"
+#include "rust-location.h"
+#include "rust-hir-map.h"
+#include "rust-diagnostics.h"
+
+namespace Rust {
+typedef std::string Identifier;
+typedef int TupleIndex;
+
+namespace HIR {
+// foward decl: ast visitor
+class HIRFullVisitor;
+class HIRStmtVisitor;
+class HIRTraitItemVisitor;
+class HIRExternalItemVisitor;
+class HIRVisItemVisitor;
+class HIRExpressionVisitor;
+class HIRPatternVisitor;
+class HIRImplVisitor;
+class HIRTypeVisitor;
+
+// forward decl for use in token tree method
+class Token;
+
+class Node
+{
+public:
+  // Kind for downcasting various HIR nodes to other base classes when visiting
+  // them
+  enum BaseKind
+  {
+    /* class ExternalItem */
+    EXTERNAL,
+    /* class TraitItem */
+    TRAIT_ITEM,
+    /* class VisItem */
+    VIS_ITEM,
+    /* class Item */
+    ITEM,
+    /* class ImplItem */
+    IMPL,
+    /* class Type */
+    TYPE,
+    /* class Stmt */
+    STMT,
+    /* class Expr */
+    EXPR,
+    /* class Pattern */
+    PATTERN,
+  };
+
+  /**
+   * Get the kind of HIR node we are dealing with. This is useful for
+   * downcasting to more precise types when necessary, i.e going from an `Item*`
+   * to a `VisItem*`
+   */
+  virtual BaseKind get_hir_kind () = 0;
+};
+
+// A literal - value with a type. Used in LiteralExpr and LiteralPattern.
+struct Literal
+{
+public:
+  enum LitType
+  {
+    CHAR,
+    STRING,
+    BYTE,
+    BYTE_STRING,
+    INT,
+    FLOAT,
+    BOOL
+  };
+
+private:
+  std::string value_as_string;
+  LitType type;
+  PrimitiveCoreType type_hint;
+
+public:
+  std::string as_string () const { return value_as_string; }
+
+  LitType get_lit_type () const { return type; }
+
+  PrimitiveCoreType get_type_hint () const { return type_hint; }
+
+  Literal (std::string value_as_string, LitType type,
+	   PrimitiveCoreType type_hint)
+    : value_as_string (std::move (value_as_string)), type (type),
+      type_hint (type_hint)
+  {}
+
+  static Literal create_error ()
+  {
+    return Literal ("", CHAR, PrimitiveCoreType::CORETYPE_UNKNOWN);
+  }
+
+  void set_lit_type (LitType lt) { type = lt; }
+
+  // Returns whether literal is in an invalid state.
+  bool is_error () const { return value_as_string == ""; }
+
+  bool is_equal (Literal &other)
+  {
+    return value_as_string == other.value_as_string && type == other.type
+	   && type_hint == other.type_hint;
+  }
+};
+
+/* Base statement abstract class. Note that most "statements" are not allowed in
+ * top-level module scope - only a subclass of statements called "items" are. */
+class Stmt : public Node
+{
+public:
+  // Unique pointer custom clone function
+  std::unique_ptr<Stmt> clone_stmt () const
+  {
+    return std::unique_ptr<Stmt> (clone_stmt_impl ());
+  }
+
+  BaseKind get_hir_kind () override { return STMT; }
+
+  virtual ~Stmt () {}
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (HIRFullVisitor &vis) = 0;
+  virtual void accept_vis (HIRStmtVisitor &vis) = 0;
+
+  virtual Location get_locus () const = 0;
+
+  virtual bool is_unit_check_needed () const { return false; }
+
+  const Analysis::NodeMapping &get_mappings () const { return mappings; }
+
+  virtual bool is_item () const = 0;
+
+protected:
+  Stmt (Analysis::NodeMapping mappings) : mappings (std::move (mappings)) {}
+
+  // Clone function implementation as pure virtual method
+  virtual Stmt *clone_stmt_impl () const = 0;
+
+  Analysis::NodeMapping mappings;
+};
+
+// Rust "item" HIR node (declaration of top-level/module-level allowed stuff)
+class Item : public Stmt
+{
+  AST::AttrVec outer_attrs;
+
+  // TODO: should outer attrs be defined here or in each derived class?
+
+public:
+  enum class ItemKind
+  {
+    Static,
+    Constant,
+    TypeAlias,
+    Function,
+    UseDeclaration,
+    ExternBlock,
+    ExternCrate,
+    Struct,
+    Union,
+    Enum,
+    EnumItem, // FIXME: ARTHUR: Do we need that?
+    Trait,
+    Impl,
+    Module,
+  };
+
+  virtual ItemKind get_item_kind () const = 0;
+
+  // Unique pointer custom clone function
+  std::unique_ptr<Item> clone_item () const
+  {
+    return std::unique_ptr<Item> (clone_item_impl ());
+  }
+
+  BaseKind get_hir_kind () override { return ITEM; }
+
+  std::string as_string () const override;
+
+  /* Adds crate names to the vector passed by reference, if it can
+   * (polymorphism). */
+  virtual void
+  add_crate_name (std::vector<std::string> &names ATTRIBUTE_UNUSED) const
+  {}
+
+  AST::AttrVec &get_outer_attrs () { return outer_attrs; }
+  const AST::AttrVec &get_outer_attrs () const { return outer_attrs; }
+
+  bool is_item () const override final { return true; }
+
+protected:
+  // Constructor
+  Item (Analysis::NodeMapping mappings,
+	AST::AttrVec outer_attribs = AST::AttrVec ())
+    : Stmt (std::move (mappings)), outer_attrs (std::move (outer_attribs))
+  {}
+
+  // Clone function implementation as pure virtual method
+  virtual Item *clone_item_impl () const = 0;
+
+  /* Save having to specify two clone methods in derived classes by making
+   * statement clone return item clone. Hopefully won't affect performance too
+   * much. */
+  Item *clone_stmt_impl () const override { return clone_item_impl (); }
+};
+
+// forward decl of ExprWithoutBlock
+class ExprWithoutBlock;
+
+// Base expression HIR node - abstract
+class Expr : public Node
+{
+  AST::AttrVec outer_attrs;
+  Analysis::NodeMapping mappings;
+
+public:
+  enum BlockType
+  {
+    WITH_BLOCK,
+    WITHOUT_BLOCK,
+  };
+
+  enum ExprType
+  {
+    Lit,
+    Operator,
+    Grouped,
+    Array,
+    ArrayIndex,
+    Tuple,
+    TupleIdx,
+    Struct,
+    Call,
+    MethodCall,
+    FieldAccess,
+    Closure,
+    Block,
+    Continue,
+    Break,
+    Range,
+    Return,
+    UnsafeBlock,
+    BaseLoop,
+    If,
+    IfLet,
+    Match,
+    Await,
+    AsyncBlock,
+    Path,
+  };
+
+  BaseKind get_hir_kind () override final { return EXPR; }
+
+  const AST::AttrVec &get_outer_attrs () const { return outer_attrs; }
+
+  // Unique pointer custom clone function
+  std::unique_ptr<Expr> clone_expr () const
+  {
+    return std::unique_ptr<Expr> (clone_expr_impl ());
+  }
+
+  /* HACK: downcasting without dynamic_cast (if possible) via polymorphism -
+   * overrided in subclasses of ExprWithoutBlock */
+  virtual ExprWithoutBlock *as_expr_without_block () const { return nullptr; }
+
+  // TODO: make pure virtual if move out outer attributes to derived classes
+  virtual std::string as_string () const;
+
+  virtual ~Expr () {}
+
+  virtual Location get_locus () const = 0;
+
+  const Analysis::NodeMapping &get_mappings () const { return mappings; }
+
+  // Clone function implementation as pure virtual method
+  virtual Expr *clone_expr_impl () const = 0;
+
+  virtual BlockType get_block_expr_type () const = 0;
+
+  virtual ExprType get_expression_type () const = 0;
+
+  virtual void accept_vis (HIRExpressionVisitor &vis) = 0;
+  virtual void accept_vis (HIRFullVisitor &vis) = 0;
+
+protected:
+  // Constructor
+  Expr (Analysis::NodeMapping mappings,
+	AST::AttrVec outer_attribs = AST::AttrVec ())
+    : outer_attrs (std::move (outer_attribs)), mappings (std::move (mappings))
+  {}
+
+  // TODO: think of less hacky way to implement this kind of thing
+  // Sets outer attributes.
+  void set_outer_attrs (AST::AttrVec outer_attrs_to_set)
+  {
+    outer_attrs = std::move (outer_attrs_to_set);
+  }
+};
+
+// HIR node for an expression without an accompanying block - abstract
+class ExprWithoutBlock : public Expr
+{
+protected:
+  // Constructor
+  ExprWithoutBlock (Analysis::NodeMapping mappings,
+		    AST::AttrVec outer_attribs = AST::AttrVec ())
+    : Expr (std::move (mappings), std::move (outer_attribs))
+  {}
+
+  // pure virtual clone implementation
+  virtual ExprWithoutBlock *clone_expr_without_block_impl () const = 0;
+
+  /* Save having to specify two clone methods in derived classes by making expr
+   * clone return exprwithoutblock clone. Hopefully won't affect performance too
+   * much. */
+  ExprWithoutBlock *clone_expr_impl () const override
+  {
+    return clone_expr_without_block_impl ();
+  }
+
+public:
+  // Unique pointer custom clone function
+  std::unique_ptr<ExprWithoutBlock> clone_expr_without_block () const
+  {
+    return std::unique_ptr<ExprWithoutBlock> (clone_expr_without_block_impl ());
+  }
+
+  /* downcasting hack from expr to use pratt parsing with
+   * parse_expr_without_block */
+  ExprWithoutBlock *as_expr_without_block () const override
+  {
+    return clone_expr_without_block_impl ();
+  }
+
+  BlockType get_block_expr_type () const final override
+  {
+    return BlockType::WITHOUT_BLOCK;
+  };
+};
+
+// Pattern base HIR node
+class Pattern : public Node
+{
+public:
+  enum PatternType
+  {
+    PATH,
+    LITERAL,
+    IDENTIFIER,
+    WILDCARD,
+    RANGE,
+    REFERENCE,
+    STRUCT,
+    TUPLE_STRUCT,
+    TUPLE,
+    GROUPED,
+    SLICE,
+  };
+
+  BaseKind get_hir_kind () override final { return PATTERN; }
+
+  // Unique pointer custom clone function
+  std::unique_ptr<Pattern> clone_pattern () const
+  {
+    return std::unique_ptr<Pattern> (clone_pattern_impl ());
+  }
+
+  // possible virtual methods: is_refutable()
+
+  virtual ~Pattern () {}
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (HIRFullVisitor &vis) = 0;
+  virtual void accept_vis (HIRPatternVisitor &vis) = 0;
+
+  virtual Analysis::NodeMapping get_pattern_mappings () const = 0;
+
+  virtual Location get_locus () const = 0;
+
+  virtual PatternType get_pattern_type () const = 0;
+
+protected:
+  // Clone pattern implementation as pure virtual method
+  virtual Pattern *clone_pattern_impl () const = 0;
+};
+
+// forward decl for Type
+class TraitBound;
+
+// Base class for types as represented in HIR - abstract
+class Type : public Node
+{
+public:
+  // Unique pointer custom clone function
+  std::unique_ptr<Type> clone_type () const
+  {
+    return std::unique_ptr<Type> (clone_type_impl ());
+  }
+
+  // virtual destructor
+  virtual ~Type () {}
+
+  BaseKind get_hir_kind () override final { return TYPE; }
+
+  virtual std::string as_string () const = 0;
+
+  /* HACK: convert to trait bound. Virtual method overriden by classes that
+   * enable this. */
+  virtual TraitBound *to_trait_bound (bool in_parens ATTRIBUTE_UNUSED) const
+  {
+    return nullptr;
+  }
+  /* as pointer, shouldn't require definition beforehand, only forward
+   * declaration. */
+
+  virtual void accept_vis (HIRFullVisitor &vis) = 0;
+  virtual void accept_vis (HIRTypeVisitor &vis) = 0;
+
+  virtual Analysis::NodeMapping get_mappings () const { return mappings; }
+  virtual Location get_locus () const { return locus; }
+
+protected:
+  Type (Analysis::NodeMapping mappings, Location locus)
+    : mappings (mappings), locus (locus)
+  {}
+
+  // Clone function implementation as pure virtual method
+  virtual Type *clone_type_impl () const = 0;
+
+  Analysis::NodeMapping mappings;
+  Location locus;
+};
+
+// A type without parentheses? - abstract
+class TypeNoBounds : public Type
+{
+public:
+  // Unique pointer custom clone function
+  std::unique_ptr<TypeNoBounds> clone_type_no_bounds () const
+  {
+    return std::unique_ptr<TypeNoBounds> (clone_type_no_bounds_impl ());
+  }
+
+protected:
+  TypeNoBounds (Analysis::NodeMapping mappings, Location locus)
+    : Type (mappings, locus)
+  {}
+
+  // Clone function implementation as pure virtual method
+  virtual TypeNoBounds *clone_type_no_bounds_impl () const = 0;
+
+  /* Save having to specify two clone methods in derived classes by making type
+   * clone return typenobounds clone. Hopefully won't affect performance too
+   * much. */
+  TypeNoBounds *clone_type_impl () const override
+  {
+    return clone_type_no_bounds_impl ();
+  }
+};
+
+/* Abstract base class representing a type param bound - Lifetime and TraitBound
+ * extends it */
+class TypeParamBound
+{
+public:
+  enum BoundType
+  {
+    LIFETIME,
+    TRAITBOUND
+  };
+
+  virtual ~TypeParamBound () {}
+
+  // Unique pointer custom clone function
+  std::unique_ptr<TypeParamBound> clone_type_param_bound () const
+  {
+    return std::unique_ptr<TypeParamBound> (clone_type_param_bound_impl ());
+  }
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (HIRFullVisitor &vis) = 0;
+
+  virtual Analysis::NodeMapping get_mappings () const = 0;
+
+  virtual Location get_locus () const = 0;
+
+  virtual BoundType get_bound_type () const = 0;
+
+protected:
+  // Clone function implementation as pure virtual method
+  virtual TypeParamBound *clone_type_param_bound_impl () const = 0;
+};
+
+// Represents a lifetime (and is also a kind of type param bound)
+class Lifetime : public TypeParamBound
+{
+private:
+  AST::Lifetime::LifetimeType lifetime_type;
+  std::string lifetime_name;
+  Location locus;
+  Analysis::NodeMapping mappings;
+
+public:
+  // Constructor
+  Lifetime (Analysis::NodeMapping mapping, AST::Lifetime::LifetimeType type,
+	    std::string name, Location locus)
+    : lifetime_type (type), lifetime_name (std::move (name)), locus (locus),
+      mappings (mapping)
+  {}
+
+  // Returns true if the lifetime is in an error state.
+  bool is_error () const
+  {
+    return lifetime_type == AST::Lifetime::LifetimeType::NAMED
+	   && lifetime_name.empty ();
+  }
+
+  static Lifetime error ()
+  {
+    return Lifetime (Analysis::NodeMapping::get_error (),
+		     AST::Lifetime::LifetimeType::NAMED, "", Location ());
+  }
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  std::string get_name () const { return lifetime_name; }
+
+  AST::Lifetime::LifetimeType get_lifetime_type () const
+  {
+    return lifetime_type;
+  }
+
+  Location get_locus () const override final { return locus; }
+
+  Analysis::NodeMapping get_mappings () const override final
+  {
+    return mappings;
+  }
+
+  BoundType get_bound_type () const final override { return LIFETIME; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  Lifetime *clone_type_param_bound_impl () const override
+  {
+    return new Lifetime (*this);
+  }
+};
+
+/* Base generic parameter in HIR. Abstract - can be represented by a Lifetime or
+ * Type param */
+class GenericParam
+{
+public:
+  virtual ~GenericParam () {}
+
+  enum class GenericKind
+  {
+    TYPE,
+    LIFETIME,
+    CONST,
+  };
+
+  // Unique pointer custom clone function
+  std::unique_ptr<GenericParam> clone_generic_param () const
+  {
+    return std::unique_ptr<GenericParam> (clone_generic_param_impl ());
+  }
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (HIRFullVisitor &vis) = 0;
+
+  virtual Location get_locus () const = 0;
+
+  Analysis::NodeMapping get_mappings () const { return mappings; }
+
+  enum GenericKind get_kind () const { return kind; }
+
+protected:
+  // Clone function implementation as pure virtual method
+  virtual GenericParam *clone_generic_param_impl () const = 0;
+
+  Analysis::NodeMapping mappings;
+
+  enum GenericKind kind;
+
+  GenericParam (Analysis::NodeMapping mapping,
+		enum GenericKind kind = GenericKind::TYPE)
+    : mappings (mapping), kind (kind)
+  {}
+};
+
+// A lifetime generic parameter (as opposed to a type generic parameter)
+class LifetimeParam : public GenericParam
+{
+  Lifetime lifetime;
+
+  // bool has_lifetime_bounds;
+  // LifetimeBounds lifetime_bounds;
+  std::vector<Lifetime> lifetime_bounds; // inlined LifetimeBounds
+
+  // bool has_outer_attribute;
+  // std::unique_ptr<Attribute> outer_attr;
+  AST::Attribute outer_attr;
+
+  Location locus;
+
+public:
+  Lifetime get_lifetime () { return lifetime; }
+
+  // Returns whether the lifetime param has any lifetime bounds.
+  bool has_lifetime_bounds () const { return !lifetime_bounds.empty (); }
+
+  // Returns whether the lifetime param has an outer attribute.
+  bool has_outer_attribute () const { return !outer_attr.is_empty (); }
+
+  // Returns whether the lifetime param is in an error state.
+  bool is_error () const { return lifetime.is_error (); }
+
+  // Constructor
+  LifetimeParam (Analysis::NodeMapping mappings, Lifetime lifetime,
+		 Location locus = Location (),
+		 std::vector<Lifetime> lifetime_bounds
+		 = std::vector<Lifetime> (),
+		 AST::Attribute outer_attr = AST::Attribute::create_empty ())
+    : GenericParam (mappings, GenericKind::LIFETIME),
+      lifetime (std::move (lifetime)),
+      lifetime_bounds (std::move (lifetime_bounds)),
+      outer_attr (std::move (outer_attr)), locus (locus)
+  {}
+
+  // TODO: remove copy and assignment operator definitions - not required
+
+  // Copy constructor with clone
+  LifetimeParam (LifetimeParam const &other)
+    : GenericParam (other.mappings, GenericKind::LIFETIME),
+      lifetime (other.lifetime), lifetime_bounds (other.lifetime_bounds),
+      outer_attr (other.outer_attr), locus (other.locus)
+  {}
+
+  // Overloaded assignment operator to clone attribute
+  LifetimeParam &operator= (LifetimeParam const &other)
+  {
+    lifetime = other.lifetime;
+    lifetime_bounds = other.lifetime_bounds;
+    outer_attr = other.outer_attr;
+    locus = other.locus;
+    mappings = other.mappings;
+
+    return *this;
+  }
+
+  // move constructors
+  LifetimeParam (LifetimeParam &&other) = default;
+  LifetimeParam &operator= (LifetimeParam &&other) = default;
+
+  std::string as_string () const override;
+
+  void accept_vis (HIRFullVisitor &vis) override;
+
+  Location get_locus () const override final { return locus; }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  LifetimeParam *clone_generic_param_impl () const override
+  {
+    return new LifetimeParam (*this);
+  }
+};
+
+class ConstGenericParam : public GenericParam
+{
+public:
+  ConstGenericParam (std::string name, std::unique_ptr<Type> type,
+		     std::unique_ptr<Expr> default_expression,
+		     Analysis::NodeMapping mapping, Location locus)
+    : GenericParam (mapping, GenericKind::CONST), name (std::move (name)),
+      type (std::move (type)),
+      default_expression (std::move (default_expression)), locus (locus)
+  {}
+
+  ConstGenericParam (const ConstGenericParam &other) : GenericParam (other)
+  {
+    name = other.name;
+    locus = other.locus;
+
+    if (other.type)
+      type = other.type->clone_type ();
+    if (other.default_expression)
+      default_expression = other.default_expression->clone_expr ();
+  }
+
+  std::string as_string () const override final;
+
+  void accept_vis (HIRFullVisitor &vis) override final;
+
+  Location get_locus () const override final { return locus; };
+
+  bool has_default_expression () { return default_expression != nullptr; }
+
+  std::unique_ptr<Type> &get_type () { return type; }
+  std::unique_ptr<Expr> &get_default_expression ()
+  {
+    rust_assert (has_default_expression ());
+
+    return default_expression;
+  }
+
+protected:
+  /* Use covariance to implement clone function as returning this object rather
+   * than base */
+  ConstGenericParam *clone_generic_param_impl () const override
+  {
+    return new ConstGenericParam (*this);
+  }
+
+private:
+  std::string name;
+  std::unique_ptr<Type> type;
+
+  /* Optional - can be a null pointer if there is no default expression */
+  std::unique_ptr<Expr> default_expression;
+
+  Location locus;
+};
+
+// Item used in trait declarations - abstract base class
+class TraitItem : public Node
+{
+public:
+  enum TraitItemKind
+  {
+    FUNC,
+    CONST,
+    TYPE
+  };
+
+  BaseKind get_hir_kind () override final { return TRAIT_ITEM; }
+
+protected:
+  // Constructor
+  TraitItem (Analysis::NodeMapping mappings) : mappings (mappings) {}
+
+  // Clone function implementation as pure virtual method
+  virtual TraitItem *clone_trait_item_impl () const = 0;
+
+  Analysis::NodeMapping mappings;
+
+public:
+  virtual ~TraitItem () {}
+
+  std::unique_ptr<TraitItem> clone_trait_item () const
+  {
+    return std::unique_ptr<TraitItem> (clone_trait_item_impl ());
+  }
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (HIRTraitItemVisitor &vis) = 0;
+  virtual void accept_vis (HIRFullVisitor &vis) = 0;
+
+  virtual const std::string trait_identifier () const = 0;
+
+  const Analysis::NodeMapping get_mappings () const { return mappings; }
+
+  virtual TraitItemKind get_item_kind () const = 0;
+
+  virtual AST::AttrVec &get_outer_attrs () = 0;
+  virtual const AST::AttrVec &get_outer_attrs () const = 0;
+};
+
+class ImplItem : public Node
+{
+public:
+  enum ImplItemType
+  {
+    FUNCTION,
+    TYPE_ALIAS,
+    CONSTANT
+  };
+
+  virtual ~ImplItem () {}
+
+  BaseKind get_hir_kind () override final { return IMPL; }
+
+  // Unique pointer custom clone function
+  std::unique_ptr<ImplItem> clone_inherent_impl_item () const
+  {
+    return std::unique_ptr<ImplItem> (clone_inherent_impl_item_impl ());
+  }
+
+  virtual std::string as_string () const = 0;
+
+  virtual void accept_vis (HIRImplVisitor &vis) = 0;
+  virtual void accept_vis (HIRFullVisitor &vis) = 0;
+  virtual void accept_vis (HIRStmtVisitor &vis) = 0;
+
+  virtual Analysis::NodeMapping get_impl_mappings () const = 0;
+
+  virtual Location get_locus () const = 0;
+
+  virtual ImplItemType get_impl_item_type () const = 0;
+
+protected:
+  // Clone function implementation as pure virtual method
+  virtual ImplItem *clone_inherent_impl_item_impl () const = 0;
+};
+
+// A crate HIR object - holds all the data for a single compilation unit
+struct Crate
+{
+  AST::AttrVec inner_attrs;
+  // dodgy spacing required here
+  /* TODO: is it better to have a vector of items here or a module (implicit
+   * top-level one)? */
+  std::vector<std::unique_ptr<Item> > items;
+
+  Analysis::NodeMapping mappings;
+
+public:
+  // Constructor
+  Crate (std::vector<std::unique_ptr<Item> > items, AST::AttrVec inner_attrs,
+	 Analysis::NodeMapping mappings)
+    : inner_attrs (std::move (inner_attrs)), items (std::move (items)),
+      mappings (mappings)
+  {}
+
+  // Copy constructor with vector clone
+  Crate (Crate const &other)
+    : inner_attrs (other.inner_attrs), mappings (other.mappings)
+  {
+    items.reserve (other.items.size ());
+    for (const auto &e : other.items)
+      items.push_back (e->clone_item ());
+  }
+
+  ~Crate () = default;
+
+  // Overloaded assignment operator with vector clone
+  Crate &operator= (Crate const &other)
+  {
+    inner_attrs = other.inner_attrs;
+    mappings = other.mappings;
+
+    items.reserve (other.items.size ());
+    for (const auto &e : other.items)
+      items.push_back (e->clone_item ());
+
+    return *this;
+  }
+
+  // Move constructors
+  Crate (Crate &&other) = default;
+  Crate &operator= (Crate &&other) = default;
+
+  // Get crate representation as string (e.g. for debugging).
+  std::string as_string () const;
+
+  const Analysis::NodeMapping &get_mappings () const { return mappings; }
+};
+
+// Base path expression HIR node - abstract
+class PathExpr : public ExprWithoutBlock
+{
+protected:
+  PathExpr (Analysis::NodeMapping mappings, AST::AttrVec outer_attribs)
+    : ExprWithoutBlock (std::move (mappings), std::move (outer_attribs))
+  {}
+
+public:
+  /* Replaces the outer attributes of this path expression with the given outer
+   * attributes. */
+  void replace_outer_attrs (AST::AttrVec outer_attrs)
+  {
+    set_outer_attrs (std::move (outer_attrs));
+  }
+
+  ExprType get_expression_type () const final override
+  {
+    return ExprType::Path;
+  }
+};
+} // namespace HIR
+} // namespace Rust
+
+#endif
-- 
2.25.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [PATCH Rust front-end v2 14/37] gccrs: Add AST to HIR lowering pass
  2022-08-24 11:59 Rust frontend patches v2 herron.philip
                   ` (12 preceding siblings ...)
  2022-08-24 11:59 ` [PATCH Rust front-end v2 13/37] gccrs: Add second intermedite representation called HIR herron.philip
@ 2022-08-24 11:59 ` herron.philip
  2022-08-24 11:59 ` [PATCH Rust front-end v2 15/37] gccrs: Add wrapper for make_unique herron.philip
                   ` (23 subsequent siblings)
  37 siblings, 0 replies; 59+ messages in thread
From: herron.philip @ 2022-08-24 11:59 UTC (permalink / raw)
  To: gcc-patches; +Cc: gcc-rust, Philip Herron

From: Philip Herron <philip.herron@embecosm.com>

This performs the lowering of the AST to HIR the interesting piece here is
that we desugar alot of the AST like we mentioned in the previous pass, but
crucially we strip out all code that is "marked-for-strip" which failed
cfg-expansion from the expansion pass. So now the HIR includes all code
required to compile for this crate.
---
 gcc/rust/hir/rust-ast-lower-base.cc           | 1078 +++++++++++++++++
 gcc/rust/hir/rust-ast-lower-base.h            |  297 +++++
 gcc/rust/hir/rust-ast-lower-block.h           |  230 ++++
 gcc/rust/hir/rust-ast-lower-enumitem.h        |  181 +++
 gcc/rust/hir/rust-ast-lower-expr.h            |  766 ++++++++++++
 gcc/rust/hir/rust-ast-lower-extern.h          |  121 ++
 gcc/rust/hir/rust-ast-lower-implitem.h        |  521 ++++++++
 gcc/rust/hir/rust-ast-lower-item.cc           |  741 +++++++++++
 gcc/rust/hir/rust-ast-lower-item.h            |   78 ++
 gcc/rust/hir/rust-ast-lower-pattern.cc        |  229 ++++
 gcc/rust/hir/rust-ast-lower-pattern.h         |   72 ++
 gcc/rust/hir/rust-ast-lower-stmt.h            |  418 +++++++
 .../hir/rust-ast-lower-struct-field-expr.h    |   63 +
 gcc/rust/hir/rust-ast-lower-type.h            |  532 ++++++++
 gcc/rust/hir/rust-ast-lower.cc                |  477 ++++++++
 gcc/rust/hir/rust-ast-lower.h                 |   59 +
 gcc/rust/hir/rust-hir-dump.cc                 |  521 ++++++++
 gcc/rust/hir/rust-hir-dump.h                  |  193 +++
 18 files changed, 6577 insertions(+)
 create mode 100644 gcc/rust/hir/rust-ast-lower-base.cc
 create mode 100644 gcc/rust/hir/rust-ast-lower-base.h
 create mode 100644 gcc/rust/hir/rust-ast-lower-block.h
 create mode 100644 gcc/rust/hir/rust-ast-lower-enumitem.h
 create mode 100644 gcc/rust/hir/rust-ast-lower-expr.h
 create mode 100644 gcc/rust/hir/rust-ast-lower-extern.h
 create mode 100644 gcc/rust/hir/rust-ast-lower-implitem.h
 create mode 100644 gcc/rust/hir/rust-ast-lower-item.cc
 create mode 100644 gcc/rust/hir/rust-ast-lower-item.h
 create mode 100644 gcc/rust/hir/rust-ast-lower-pattern.cc
 create mode 100644 gcc/rust/hir/rust-ast-lower-pattern.h
 create mode 100644 gcc/rust/hir/rust-ast-lower-stmt.h
 create mode 100644 gcc/rust/hir/rust-ast-lower-struct-field-expr.h
 create mode 100644 gcc/rust/hir/rust-ast-lower-type.h
 create mode 100644 gcc/rust/hir/rust-ast-lower.cc
 create mode 100644 gcc/rust/hir/rust-ast-lower.h
 create mode 100644 gcc/rust/hir/rust-hir-dump.cc
 create mode 100644 gcc/rust/hir/rust-hir-dump.h

diff --git a/gcc/rust/hir/rust-ast-lower-base.cc b/gcc/rust/hir/rust-ast-lower-base.cc
new file mode 100644
index 00000000000..a67461791d7
--- /dev/null
+++ b/gcc/rust/hir/rust-ast-lower-base.cc
@@ -0,0 +1,1078 @@
+// 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/>.
+
+#include "rust-ast-lower-base.h"
+#include "rust-ast-lower-type.h"
+#include "rust-ast-lower-pattern.h"
+#include "rust-ast-lower-extern.h"
+
+namespace Rust {
+namespace HIR {
+
+void
+ASTLoweringBase::visit (AST::Token &tok)
+{}
+void
+ASTLoweringBase::visit (AST::DelimTokenTree &delim_tok_tree)
+{}
+void
+ASTLoweringBase::visit (AST::AttrInputMetaItemContainer &input)
+{}
+//  void ASTLoweringBase::visit(MetaItem& meta_item) {}
+//  void vsit(Stmt& stmt) {}
+//  void ASTLoweringBase::visit(Expr& expr) {}
+void
+ASTLoweringBase::visit (AST::IdentifierExpr &ident_expr)
+{}
+//  void ASTLoweringBase::visit(Pattern& pattern) {}
+//  void ASTLoweringBase::visit(Type& type) {}
+//  void ASTLoweringBase::visit(TypeParamBound& type_param_bound) {}
+void
+ASTLoweringBase::visit (AST::Lifetime &lifetime)
+{}
+//  void ASTLoweringBase::visit(GenericParam& generic_param) {}
+void
+ASTLoweringBase::visit (AST::LifetimeParam &lifetime_param)
+{}
+void
+ASTLoweringBase::visit (AST::ConstGenericParam &const_param)
+{}
+//  void ASTLoweringBase::visit(TraitItem& trait_item) {}
+//  void ASTLoweringBase::visit(InherentImplItem& inherent_impl_item) {}
+//  void ASTLoweringBase::visit(TraitImplItem& trait_impl_item) {}
+
+// rust-path.h
+void
+ASTLoweringBase::visit (AST::PathInExpression &path)
+{}
+void
+ASTLoweringBase::visit (AST::TypePathSegment &segment)
+{}
+void
+ASTLoweringBase::visit (AST::TypePathSegmentGeneric &segment)
+{}
+void
+ASTLoweringBase::visit (AST::TypePathSegmentFunction &segment)
+{}
+void
+ASTLoweringBase::visit (AST::TypePath &path)
+{}
+void
+ASTLoweringBase::visit (AST::QualifiedPathInExpression &path)
+{}
+void
+ASTLoweringBase::visit (AST::QualifiedPathInType &path)
+{}
+
+// rust-expr.h
+void
+ASTLoweringBase::visit (AST::LiteralExpr &expr)
+{}
+void
+ASTLoweringBase::visit (AST::AttrInputLiteral &attr_input)
+{}
+void
+ASTLoweringBase::visit (AST::MetaItemLitExpr &meta_item)
+{}
+void
+ASTLoweringBase::visit (AST::MetaItemPathLit &meta_item)
+{}
+void
+ASTLoweringBase::visit (AST::BorrowExpr &expr)
+{}
+void
+ASTLoweringBase::visit (AST::DereferenceExpr &expr)
+{}
+void
+ASTLoweringBase::visit (AST::ErrorPropagationExpr &expr)
+{}
+void
+ASTLoweringBase::visit (AST::NegationExpr &expr)
+{}
+void
+ASTLoweringBase::visit (AST::ArithmeticOrLogicalExpr &expr)
+{}
+void
+ASTLoweringBase::visit (AST::ComparisonExpr &expr)
+{}
+void
+ASTLoweringBase::visit (AST::LazyBooleanExpr &expr)
+{}
+void
+ASTLoweringBase::visit (AST::TypeCastExpr &expr)
+{}
+void
+ASTLoweringBase::visit (AST::AssignmentExpr &expr)
+{}
+void
+ASTLoweringBase::visit (AST::CompoundAssignmentExpr &expr)
+{}
+void
+ASTLoweringBase::visit (AST::GroupedExpr &expr)
+{}
+//  void ASTLoweringBase::visit(ArrayElems& elems) {}
+void
+ASTLoweringBase::visit (AST::ArrayElemsValues &elems)
+{}
+void
+ASTLoweringBase::visit (AST::ArrayElemsCopied &elems)
+{}
+void
+ASTLoweringBase::visit (AST::ArrayExpr &expr)
+{}
+void
+ASTLoweringBase::visit (AST::ArrayIndexExpr &expr)
+{}
+void
+ASTLoweringBase::visit (AST::TupleExpr &expr)
+{}
+void
+ASTLoweringBase::visit (AST::TupleIndexExpr &expr)
+{}
+void
+ASTLoweringBase::visit (AST::StructExprStruct &expr)
+{}
+//  void ASTLoweringBase::visit(StructExprField& field) {}
+void
+ASTLoweringBase::visit (AST::StructExprFieldIdentifier &field)
+{}
+void
+ASTLoweringBase::visit (AST::StructExprFieldIdentifierValue &field)
+{}
+void
+ASTLoweringBase::visit (AST::StructExprFieldIndexValue &field)
+{}
+void
+ASTLoweringBase::visit (AST::StructExprStructFields &expr)
+{}
+void
+ASTLoweringBase::visit (AST::StructExprStructBase &expr)
+{}
+void
+ASTLoweringBase::visit (AST::CallExpr &expr)
+{}
+void
+ASTLoweringBase::visit (AST::MethodCallExpr &expr)
+{}
+void
+ASTLoweringBase::visit (AST::FieldAccessExpr &expr)
+{}
+void
+ASTLoweringBase::visit (AST::ClosureExprInner &expr)
+{}
+void
+ASTLoweringBase::visit (AST::BlockExpr &expr)
+{}
+void
+ASTLoweringBase::visit (AST::ClosureExprInnerTyped &expr)
+{}
+void
+ASTLoweringBase::visit (AST::ContinueExpr &expr)
+{}
+void
+ASTLoweringBase::visit (AST::BreakExpr &expr)
+{}
+void
+ASTLoweringBase::visit (AST::RangeFromToExpr &expr)
+{}
+void
+ASTLoweringBase::visit (AST::RangeFromExpr &expr)
+{}
+void
+ASTLoweringBase::visit (AST::RangeToExpr &expr)
+{}
+void
+ASTLoweringBase::visit (AST::RangeFullExpr &expr)
+{}
+void
+ASTLoweringBase::visit (AST::RangeFromToInclExpr &expr)
+{}
+void
+ASTLoweringBase::visit (AST::RangeToInclExpr &expr)
+{}
+void
+ASTLoweringBase::visit (AST::ReturnExpr &expr)
+{}
+void
+ASTLoweringBase::visit (AST::UnsafeBlockExpr &expr)
+{}
+void
+ASTLoweringBase::visit (AST::LoopExpr &expr)
+{}
+void
+ASTLoweringBase::visit (AST::WhileLoopExpr &expr)
+{}
+void
+ASTLoweringBase::visit (AST::WhileLetLoopExpr &expr)
+{}
+void
+ASTLoweringBase::visit (AST::ForLoopExpr &expr)
+{}
+void
+ASTLoweringBase::visit (AST::IfExpr &expr)
+{}
+void
+ASTLoweringBase::visit (AST::IfExprConseqElse &expr)
+{}
+void
+ASTLoweringBase::visit (AST::IfExprConseqIf &expr)
+{}
+void
+ASTLoweringBase::visit (AST::IfExprConseqIfLet &expr)
+{}
+void
+ASTLoweringBase::visit (AST::IfLetExpr &expr)
+{}
+void
+ASTLoweringBase::visit (AST::IfLetExprConseqElse &expr)
+{}
+void
+ASTLoweringBase::visit (AST::IfLetExprConseqIf &expr)
+{}
+void
+ASTLoweringBase::visit (AST::IfLetExprConseqIfLet &expr)
+{}
+//  void ASTLoweringBase::visit(MatchCase& match_case) {}
+// void ASTLoweringBase:: (AST::MatchCaseBlockExpr &match_case) {}
+// void ASTLoweringBase:: (AST::MatchCaseExpr &match_case) {}
+void
+ASTLoweringBase::visit (AST::MatchExpr &expr)
+{}
+void
+ASTLoweringBase::visit (AST::AwaitExpr &expr)
+{}
+void
+ASTLoweringBase::visit (AST::AsyncBlockExpr &expr)
+{}
+
+// rust-item.h
+void
+ASTLoweringBase::visit (AST::TypeParam &param)
+{}
+//  void ASTLoweringBase::visit(WhereClauseItem& item) {}
+void
+ASTLoweringBase::visit (AST::LifetimeWhereClauseItem &item)
+{}
+void
+ASTLoweringBase::visit (AST::TypeBoundWhereClauseItem &item)
+{}
+void
+ASTLoweringBase::visit (AST::Method &method)
+{}
+void
+ASTLoweringBase::visit (AST::Module &module)
+{}
+void
+ASTLoweringBase::visit (AST::ExternCrate &crate)
+{}
+//  void ASTLoweringBase::visit(UseTree& use_tree) {}
+void
+ASTLoweringBase::visit (AST::UseTreeGlob &use_tree)
+{}
+void
+ASTLoweringBase::visit (AST::UseTreeList &use_tree)
+{}
+void
+ASTLoweringBase::visit (AST::UseTreeRebind &use_tree)
+{}
+void
+ASTLoweringBase::visit (AST::UseDeclaration &use_decl)
+{}
+void
+ASTLoweringBase::visit (AST::Function &function)
+{}
+void
+ASTLoweringBase::visit (AST::TypeAlias &type_alias)
+{}
+void
+ASTLoweringBase::visit (AST::StructStruct &struct_item)
+{}
+void
+ASTLoweringBase::visit (AST::TupleStruct &tuple_struct)
+{}
+void
+ASTLoweringBase::visit (AST::EnumItem &item)
+{}
+void
+ASTLoweringBase::visit (AST::EnumItemTuple &item)
+{}
+void
+ASTLoweringBase::visit (AST::EnumItemStruct &item)
+{}
+void
+ASTLoweringBase::visit (AST::EnumItemDiscriminant &item)
+{}
+void
+ASTLoweringBase::visit (AST::Enum &enum_item)
+{}
+void
+ASTLoweringBase::visit (AST::Union &union_item)
+{}
+void
+ASTLoweringBase::visit (AST::ConstantItem &const_item)
+{}
+void
+ASTLoweringBase::visit (AST::StaticItem &static_item)
+{}
+void
+ASTLoweringBase::visit (AST::TraitItemFunc &item)
+{}
+void
+ASTLoweringBase::visit (AST::TraitItemMethod &item)
+{}
+void
+ASTLoweringBase::visit (AST::TraitItemConst &item)
+{}
+void
+ASTLoweringBase::visit (AST::TraitItemType &item)
+{}
+void
+ASTLoweringBase::visit (AST::Trait &trait)
+{}
+void
+ASTLoweringBase::visit (AST::InherentImpl &impl)
+{}
+void
+ASTLoweringBase::visit (AST::TraitImpl &impl)
+{}
+//  void ASTLoweringBase::visit(ExternalItem& item) {}
+void
+ASTLoweringBase::visit (AST::ExternalStaticItem &item)
+{}
+void
+ASTLoweringBase::visit (AST::ExternalFunctionItem &item)
+{}
+void
+ASTLoweringBase::visit (AST::ExternBlock &block)
+{}
+
+// rust-macro.h
+void
+ASTLoweringBase::visit (AST::MacroMatchFragment &match)
+{}
+void
+ASTLoweringBase::visit (AST::MacroMatchRepetition &match)
+{}
+void
+ASTLoweringBase::visit (AST::MacroMatcher &matcher)
+{}
+void
+ASTLoweringBase::visit (AST::MacroRulesDefinition &rules_def)
+{}
+void
+ASTLoweringBase::visit (AST::MacroInvocation &macro_invoc)
+{}
+void
+ASTLoweringBase::visit (AST::MetaItemPath &meta_item)
+{}
+void
+ASTLoweringBase::visit (AST::MetaItemSeq &meta_item)
+{}
+void
+ASTLoweringBase::visit (AST::MetaWord &meta_item)
+{}
+void
+ASTLoweringBase::visit (AST::MetaNameValueStr &meta_item)
+{}
+void
+ASTLoweringBase::visit (AST::MetaListPaths &meta_item)
+{}
+void
+ASTLoweringBase::visit (AST::MetaListNameValueStr &meta_item)
+{}
+
+// rust-pattern.h
+void
+ASTLoweringBase::visit (AST::LiteralPattern &pattern)
+{}
+void
+ASTLoweringBase::visit (AST::IdentifierPattern &pattern)
+{}
+void
+ASTLoweringBase::visit (AST::WildcardPattern &pattern)
+{}
+//  void ASTLoweringBase::visit(RangePatternBound& bound) {}
+void
+ASTLoweringBase::visit (AST::RangePatternBoundLiteral &bound)
+{}
+void
+ASTLoweringBase::visit (AST::RangePatternBoundPath &bound)
+{}
+void
+ASTLoweringBase::visit (AST::RangePatternBoundQualPath &bound)
+{}
+void
+ASTLoweringBase::visit (AST::RangePattern &pattern)
+{}
+void
+ASTLoweringBase::visit (AST::ReferencePattern &pattern)
+{}
+//  void ASTLoweringBase::visit(StructPatternField& field) {}
+void
+ASTLoweringBase::visit (AST::StructPatternFieldTuplePat &field)
+{}
+void
+ASTLoweringBase::visit (AST::StructPatternFieldIdentPat &field)
+{}
+void
+ASTLoweringBase::visit (AST::StructPatternFieldIdent &field)
+{}
+void
+ASTLoweringBase::visit (AST::StructPattern &pattern)
+{}
+//  void ASTLoweringBase::visit(TupleStructItems& tuple_items) {}
+void
+ASTLoweringBase::visit (AST::TupleStructItemsNoRange &tuple_items)
+{}
+void
+ASTLoweringBase::visit (AST::TupleStructItemsRange &tuple_items)
+{}
+void
+ASTLoweringBase::visit (AST::TupleStructPattern &pattern)
+{}
+//  void ASTLoweringBase::visit(TuplePatternItems& tuple_items) {}
+void
+ASTLoweringBase::visit (AST::TuplePatternItemsMultiple &tuple_items)
+{}
+void
+ASTLoweringBase::visit (AST::TuplePatternItemsRanged &tuple_items)
+{}
+void
+ASTLoweringBase::visit (AST::TuplePattern &pattern)
+{}
+void
+ASTLoweringBase::visit (AST::GroupedPattern &pattern)
+{}
+void
+ASTLoweringBase::visit (AST::SlicePattern &pattern)
+{}
+
+// rust-stmt.h
+void
+ASTLoweringBase::visit (AST::EmptyStmt &stmt)
+{}
+void
+ASTLoweringBase::visit (AST::LetStmt &stmt)
+{}
+void
+ASTLoweringBase::visit (AST::ExprStmtWithoutBlock &stmt)
+{}
+void
+ASTLoweringBase::visit (AST::ExprStmtWithBlock &stmt)
+{}
+
+// rust-type.h
+void
+ASTLoweringBase::visit (AST::TraitBound &bound)
+{}
+void
+ASTLoweringBase::visit (AST::ImplTraitType &type)
+{}
+void
+ASTLoweringBase::visit (AST::TraitObjectType &type)
+{}
+void
+ASTLoweringBase::visit (AST::ParenthesisedType &type)
+{}
+void
+ASTLoweringBase::visit (AST::ImplTraitTypeOneBound &type)
+{}
+void
+ASTLoweringBase::visit (AST::TraitObjectTypeOneBound &type)
+{}
+void
+ASTLoweringBase::visit (AST::TupleType &type)
+{}
+void
+ASTLoweringBase::visit (AST::NeverType &type)
+{}
+void
+ASTLoweringBase::visit (AST::RawPointerType &type)
+{}
+void
+ASTLoweringBase::visit (AST::ReferenceType &type)
+{}
+void
+ASTLoweringBase::visit (AST::ArrayType &type)
+{}
+void
+ASTLoweringBase::visit (AST::SliceType &type)
+{}
+void
+ASTLoweringBase::visit (AST::InferredType &type)
+{}
+void
+ASTLoweringBase::visit (AST::BareFunctionType &type)
+{}
+
+HIR::Lifetime
+ASTLoweringBase::lower_lifetime (AST::Lifetime &lifetime)
+{
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, lifetime.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+  mappings->insert_node_to_hir (mapping.get_nodeid (), mapping.get_hirid ());
+
+  return HIR::Lifetime (mapping, lifetime.get_lifetime_type (),
+			lifetime.get_lifetime_name (), lifetime.get_locus ());
+}
+
+HIR::LoopLabel
+ASTLoweringBase::lower_loop_label (AST::LoopLabel &loop_label)
+{
+  HIR::Lifetime life = lower_lifetime (loop_label.get_lifetime ());
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, loop_label.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+  mappings->insert_node_to_hir (mapping.get_nodeid (), mapping.get_hirid ());
+
+  return HIR::LoopLabel (mapping, std::move (life), loop_label.get_locus ());
+}
+
+std::vector<std::unique_ptr<HIR::GenericParam>>
+ASTLoweringBase::lower_generic_params (
+  std::vector<std::unique_ptr<AST::GenericParam>> &params)
+{
+  std::vector<std::unique_ptr<HIR::GenericParam>> lowered;
+  for (auto &ast_param : params)
+    {
+      auto hir_param = ASTLowerGenericParam::translate (ast_param.get ());
+      lowered.push_back (std::unique_ptr<HIR::GenericParam> (hir_param));
+    }
+
+  return lowered;
+}
+
+HIR::PathExprSegment
+ASTLoweringBase::lower_path_expr_seg (AST::PathExprSegment &s)
+{
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, s.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  return HIR::PathExprSegment (
+    std::move (mapping),
+    HIR::PathIdentSegment (s.get_ident_segment ().as_string ()), s.get_locus (),
+    s.has_generic_args () ? lower_generic_args (s.get_generic_args ())
+			  : HIR::GenericArgs::create_empty ());
+}
+
+HIR::GenericArgsBinding
+ASTLoweringBase::lower_binding (AST::GenericArgsBinding &binding)
+{
+  HIR::Type *lowered_type
+    = ASTLoweringType::translate (binding.get_type ().get ());
+  return HIR::GenericArgsBinding (binding.get_identifier (),
+				  std::unique_ptr<HIR::Type> (lowered_type),
+				  binding.get_locus ());
+}
+
+HIR::GenericArgs
+ASTLoweringBase::lower_generic_args (AST::GenericArgs &args)
+{
+  std::vector<HIR::GenericArgsBinding> binding_args;
+  for (auto &binding : args.get_binding_args ())
+    {
+      HIR::GenericArgsBinding b = lower_binding (binding);
+      binding_args.push_back (std::move (b));
+    }
+
+  std::vector<HIR::Lifetime> lifetime_args;
+  for (auto &lifetime : args.get_lifetime_args ())
+    {
+      HIR::Lifetime l = lower_lifetime (lifetime);
+      lifetime_args.push_back (std::move (l));
+    }
+
+  std::vector<std::unique_ptr<HIR::Type>> type_args;
+  std::vector<HIR::ConstGenericArg> const_args;
+
+  for (auto &arg : args.get_generic_args ())
+    {
+      switch (arg.get_kind ())
+	{
+	  case AST::GenericArg::Kind::Type: {
+	    auto type = ASTLoweringType::translate (arg.get_type ().get ());
+	    type_args.emplace_back (std::unique_ptr<HIR::Type> (type));
+	    break;
+	  }
+	  case AST::GenericArg::Kind::Const: {
+	    auto expr
+	      = ASTLoweringExpr::translate (arg.get_expression ().get ());
+	    const_args.emplace_back (
+	      HIR::ConstGenericArg (std::unique_ptr<HIR::Expr> (expr),
+				    expr->get_locus ()));
+	    break;
+	  }
+	default:
+	  gcc_unreachable ();
+	}
+    }
+
+  return HIR::GenericArgs (std::move (lifetime_args), std::move (type_args),
+			   std::move (binding_args), std::move (const_args),
+			   args.get_locus ());
+}
+
+HIR::SelfParam
+ASTLoweringBase::lower_self (AST::SelfParam &self)
+{
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, self.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 mappings->get_next_localdef_id (crate_num));
+
+  if (self.has_type ())
+    {
+      HIR::Type *type = ASTLoweringType::translate (self.get_type ().get ());
+      return HIR::SelfParam (mapping, std::unique_ptr<HIR::Type> (type),
+			     self.get_is_mut (), self.get_locus ());
+    }
+  else if (!self.get_has_ref ())
+    {
+      return HIR::SelfParam (mapping, std::unique_ptr<HIR::Type> (nullptr),
+			     self.get_is_mut (), self.get_locus ());
+    }
+
+  AST::Lifetime l = self.get_lifetime ();
+  return HIR::SelfParam (mapping, lower_lifetime (l), self.get_is_mut (),
+			 self.get_locus ());
+}
+
+void
+ASTLowerTypePath::visit (AST::TypePathSegmentGeneric &segment)
+{
+  std::vector<HIR::GenericArgsBinding> binding_args; // TODO
+
+  std::string segment_name = segment.get_ident_segment ().as_string ();
+  bool has_separating_scope_resolution
+    = segment.get_separating_scope_resolution ();
+
+  auto generic_args = lower_generic_args (segment.get_generic_args ());
+
+  auto crate_num = mappings->get_current_crate ();
+  auto hirid = mappings->get_next_hir_id (crate_num);
+  Analysis::NodeMapping mapping (crate_num, segment.get_node_id (), hirid,
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated_segment
+    = new HIR::TypePathSegmentGeneric (std::move (mapping), segment_name,
+				       has_separating_scope_resolution,
+				       generic_args, segment.get_locus ());
+}
+
+void
+ASTLowerQualifiedPathInType::visit (AST::QualifiedPathInType &path)
+{
+  auto crate_num = mappings->get_current_crate ();
+  auto hirid = mappings->get_next_hir_id (crate_num);
+  Analysis::NodeMapping qual_mappings (
+    crate_num, path.get_qualified_path_type ().get_node_id (), hirid,
+    UNKNOWN_LOCAL_DEFID);
+
+  HIR::Type *qual_type = ASTLoweringType::translate (
+    path.get_qualified_path_type ().get_type ().get ());
+  HIR::TypePath *qual_trait = ASTLowerTypePath::translate (
+    path.get_qualified_path_type ().get_as_type_path ());
+
+  HIR::QualifiedPathType qual_path_type (
+    qual_mappings, std::unique_ptr<HIR::Type> (qual_type),
+    std::unique_ptr<HIR::TypePath> (qual_trait),
+    path.get_qualified_path_type ().get_locus ());
+
+  translated_segment = nullptr;
+  path.get_associated_segment ()->accept_vis (*this);
+  if (translated_segment == nullptr)
+    {
+      rust_fatal_error (path.get_associated_segment ()->get_locus (),
+			"failed to translate AST TypePathSegment");
+      return;
+    }
+  std::unique_ptr<HIR::TypePathSegment> associated_segment (translated_segment);
+
+  std::vector<std::unique_ptr<HIR::TypePathSegment>> translated_segments;
+  for (auto &seg : path.get_segments ())
+    {
+      translated_segment = nullptr;
+      seg->accept_vis (*this);
+      if (translated_segment == nullptr)
+	{
+	  rust_fatal_error (seg->get_locus (),
+			    "failed to translte AST TypePathSegment");
+	}
+      translated_segments.push_back (
+	std::unique_ptr<HIR::TypePathSegment> (translated_segment));
+    }
+
+  Analysis::NodeMapping mapping (crate_num, path.get_node_id (), hirid,
+				 mappings->get_next_localdef_id (crate_num));
+  translated = new HIR::QualifiedPathInType (std::move (mapping),
+					     std::move (qual_path_type),
+					     std::move (associated_segment),
+					     std::move (translated_segments),
+					     path.get_locus ());
+}
+
+void
+ASTLoweringType::visit (AST::TraitObjectTypeOneBound &type)
+{
+  std::vector<std::unique_ptr<HIR::TypeParamBound>> bounds;
+  HIR::TypeParamBound *translated_bound
+    = ASTLoweringTypeBounds::translate (&type.get_trait_bound ());
+  bounds.push_back (std::unique_ptr<HIR::TypeParamBound> (translated_bound));
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, type.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 mappings->get_next_localdef_id (crate_num));
+
+  translated = new HIR::TraitObjectType (mapping, std::move (bounds),
+					 type.get_locus (), type.is_dyn ());
+}
+
+void
+ASTLoweringType::visit (AST::TraitObjectType &type)
+{
+  std::vector<std::unique_ptr<HIR::TypeParamBound>> bounds;
+
+  for (auto &bound : type.get_type_param_bounds ())
+    {
+      HIR::TypeParamBound *translated_bound
+	= ASTLoweringTypeBounds::translate (bound.get ());
+      bounds.push_back (
+	std::unique_ptr<HIR::TypeParamBound> (translated_bound));
+    }
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, type.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 mappings->get_next_localdef_id (crate_num));
+
+  translated = new HIR::TraitObjectType (mapping, std::move (bounds),
+					 type.get_locus (), type.is_dyn ());
+}
+
+HIR::Type *
+ASTLoweringBase::lower_type_no_bounds (AST::TypeNoBounds *type)
+{
+  return ASTLoweringType::translate (type);
+}
+
+HIR::TypeParamBound *
+ASTLoweringBase::lower_bound (AST::TypeParamBound *bound)
+{
+  return ASTLoweringTypeBounds::translate (bound);
+}
+
+/* Checks whether the name of a field already exists.  Returns true
+   and produces an error if so.  */
+bool
+struct_field_name_exists (std::vector<HIR::StructField> &fields,
+			  HIR::StructField &new_field)
+{
+  for (auto &field : fields)
+    {
+      if (field.get_field_name ().compare (new_field.get_field_name ()) == 0)
+	{
+	  RichLocation r (new_field.get_locus ());
+	  r.add_range (field.get_locus ());
+	  rust_error_at (r, "duplicate field name %qs",
+			 field.get_field_name ().c_str ());
+	  return true;
+	}
+    }
+  return false;
+}
+
+HIR::FunctionQualifiers
+ASTLoweringBase::lower_qualifiers (const AST::FunctionQualifiers &qualifiers)
+{
+  Unsafety unsafety
+    = qualifiers.is_unsafe () ? Unsafety::Unsafe : Unsafety::Normal;
+  bool has_extern = qualifiers.is_extern ();
+
+  ABI abi = ABI::RUST;
+  if (qualifiers.has_abi ())
+    {
+      const std::string &extern_abi = qualifiers.get_extern_abi ();
+      abi = get_abi_from_string (extern_abi);
+      if (has_extern && abi == ABI::UNKNOWN)
+	rust_error_at (qualifiers.get_locus (), "unknown ABI option");
+    }
+
+  return HIR::FunctionQualifiers (qualifiers.get_const_status (), unsafety,
+				  has_extern, abi);
+}
+
+void
+ASTLoweringBase::handle_outer_attributes (const HIR::Item &item)
+{
+  for (const auto &attr : item.get_outer_attrs ())
+    {
+      const auto &str_path = attr.get_path ().as_string ();
+      if (!is_known_attribute (str_path))
+	{
+	  rust_error_at (attr.get_locus (), "unknown attribute");
+	  continue;
+	}
+
+      bool is_lang_item = str_path.compare ("lang") == 0
+			  && attr.has_attr_input ()
+			  && attr.get_attr_input ().get_attr_input_type ()
+			       == AST::AttrInput::AttrInputType::LITERAL;
+
+      bool is_doc_item = str_path.compare ("doc") == 0;
+
+      if (is_doc_item)
+	handle_doc_item_attribute (item, attr);
+      else if (is_lang_item)
+	handle_lang_item_attribute (item, attr);
+      else if (!attribute_handled_in_another_pass (str_path))
+	{
+	  rust_error_at (attr.get_locus (), "unhandled attribute: [%s]",
+			 attr.get_path ().as_string ().c_str ());
+	}
+    }
+}
+
+void
+ASTLoweringBase::handle_doc_item_attribute (const HIR::Item &item,
+					    const AST::Attribute &attr)
+{
+  auto simple_doc_comment = attr.has_attr_input ()
+			    && attr.get_attr_input ().get_attr_input_type ()
+				 == AST::AttrInput::AttrInputType::LITERAL;
+  if (simple_doc_comment)
+    return;
+
+  const AST::AttrInput &input = attr.get_attr_input ();
+  bool is_token_tree
+    = input.get_attr_input_type () == AST::AttrInput::AttrInputType::TOKEN_TREE;
+  rust_assert (is_token_tree);
+  const auto &option = static_cast<const AST::DelimTokenTree &> (input);
+  AST::AttrInputMetaItemContainer *meta_item = option.parse_to_meta_item ();
+
+  // TODO: add actual and complete checks for the doc attributes
+  //
+  // FIXME: Move this to the AttributeChecker visitor
+  rust_assert (meta_item);
+}
+
+void
+ASTLoweringBase::handle_lang_item_attribute (const HIR::Item &item,
+					     const AST::Attribute &attr)
+{
+  auto &literal = static_cast<AST::AttrInputLiteral &> (attr.get_attr_input ());
+  const auto &lang_item_type_str = literal.get_literal ().as_string ();
+  auto lang_item_type = Analysis::RustLangItem::Parse (lang_item_type_str);
+  if (lang_item_type == Analysis::RustLangItem::ItemType::UNKNOWN)
+    {
+      rust_error_at (attr.get_locus (), "unknown lang item");
+      return;
+    }
+  mappings->insert_lang_item (lang_item_type,
+			      item.get_mappings ().get_defid ());
+}
+
+bool
+ASTLoweringBase::is_known_attribute (const std::string &attribute_path) const
+{
+  const auto &lookup = attr_mappings->lookup_builtin (attribute_path);
+  return !lookup.is_error ();
+}
+
+bool
+ASTLoweringBase::attribute_handled_in_another_pass (
+  const std::string &attribute_path) const
+{
+  const auto &lookup = attr_mappings->lookup_builtin (attribute_path);
+  if (lookup.is_error ())
+    return false;
+
+  if (lookup.handler == Analysis::CompilerPass::UNKNOWN)
+    return false;
+
+  return lookup.handler != Analysis::CompilerPass::HIR_LOWERING;
+}
+
+std::unique_ptr<HIR::TuplePatternItems>
+ASTLoweringBase::lower_tuple_pattern_multiple (
+  AST::TuplePatternItemsMultiple &pattern)
+{
+  std::vector<std::unique_ptr<HIR::Pattern>> patterns;
+  for (auto &p : pattern.get_patterns ())
+    {
+      HIR::Pattern *translated = ASTLoweringPattern::translate (p.get ());
+      patterns.push_back (std::unique_ptr<HIR::Pattern> (translated));
+    }
+
+  return std::unique_ptr<HIR::TuplePatternItems> (
+    new HIR::TuplePatternItemsMultiple (std::move (patterns)));
+}
+
+std::unique_ptr<TuplePatternItems>
+ASTLoweringBase::lower_tuple_pattern_ranged (
+  AST::TuplePatternItemsRanged &pattern)
+{
+  std::vector<std::unique_ptr<HIR::Pattern>> lower_patterns;
+  std::vector<std::unique_ptr<HIR::Pattern>> upper_patterns;
+
+  for (auto &p : pattern.get_lower_patterns ())
+    {
+      HIR::Pattern *translated = ASTLoweringPattern::translate (p.get ());
+      lower_patterns.push_back (std::unique_ptr<HIR::Pattern> (translated));
+    }
+
+  for (auto &p : pattern.get_upper_patterns ())
+    {
+      HIR::Pattern *translated = ASTLoweringPattern::translate (p.get ());
+      upper_patterns.push_back (std::unique_ptr<HIR::Pattern> (translated));
+    }
+
+  return std::unique_ptr<HIR::TuplePatternItems> (
+    new HIR::TuplePatternItemsRanged (std::move (lower_patterns),
+				      std::move (upper_patterns)));
+}
+
+std::unique_ptr<HIR::RangePatternBound>
+ASTLoweringBase::lower_range_pattern_bound (AST::RangePatternBound *bound)
+{
+  std::unique_ptr<HIR::RangePatternBound> hir_bound = nullptr;
+  switch (bound->get_bound_type ())
+    {
+      case AST::RangePatternBound::RangePatternBoundType::LITERAL: {
+	AST::RangePatternBoundLiteral &ref
+	  = *static_cast<AST::RangePatternBoundLiteral *> (bound);
+
+	HIR::Literal literal = lower_literal (ref.get_literal ());
+
+	hir_bound = std::unique_ptr<HIR::RangePatternBound> (
+	  new HIR::RangePatternBoundLiteral (literal, ref.get_locus (),
+					     ref.get_has_minus ()));
+      }
+      break;
+      case AST::RangePatternBound::RangePatternBoundType::PATH: {
+	AST::RangePatternBoundPath &ref
+	  = *static_cast<AST::RangePatternBoundPath *> (bound);
+
+	HIR::PathInExpression *path
+	  = ASTLowerPathInExpression::translate (&ref.get_path ());
+
+	hir_bound = std::unique_ptr<HIR::RangePatternBound> (
+	  new HIR::RangePatternBoundPath (*path));
+      }
+      break;
+      case AST::RangePatternBound::RangePatternBoundType::QUALPATH: {
+	AST::RangePatternBoundQualPath &ref
+	  = *static_cast<AST::RangePatternBoundQualPath *> (bound);
+
+	HIR::QualifiedPathInExpression *qualpath
+	  = ASTLowerQualPathInExpression::translate (
+	    &ref.get_qualified_path ());
+
+	hir_bound = std::unique_ptr<HIR::RangePatternBound> (
+	  new HIR::RangePatternBoundQualPath (*qualpath));
+      }
+      break;
+    }
+
+  return hir_bound;
+}
+
+HIR::Literal
+ASTLoweringBase::lower_literal (const AST::Literal &literal)
+{
+  HIR::Literal::LitType type = HIR::Literal::LitType::CHAR;
+  switch (literal.get_lit_type ())
+    {
+    case AST::Literal::LitType::CHAR:
+      type = HIR::Literal::LitType::CHAR;
+      break;
+    case AST::Literal::LitType::STRING:
+      type = HIR::Literal::LitType::STRING;
+      break;
+    case AST::Literal::LitType::BYTE:
+      type = HIR::Literal::LitType::BYTE;
+      break;
+    case AST::Literal::LitType::BYTE_STRING:
+      type = HIR::Literal::LitType::BYTE_STRING;
+      break;
+    case AST::Literal::LitType::INT:
+      type = HIR::Literal::LitType::INT;
+      break;
+    case AST::Literal::LitType::FLOAT:
+      type = HIR::Literal::LitType::FLOAT;
+      break;
+    case AST::Literal::LitType::BOOL:
+      type = HIR::Literal::LitType::BOOL;
+      break;
+    case AST::Literal::LitType::ERROR:
+      gcc_unreachable ();
+      break;
+    }
+
+  return HIR::Literal (literal.as_string (), type, literal.get_type_hint ());
+}
+
+HIR::ExternBlock *
+ASTLoweringBase::lower_extern_block (AST::ExternBlock &extern_block)
+{
+  HIR::Visibility vis = translate_visibility (extern_block.get_visibility ());
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, extern_block.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 mappings->get_next_localdef_id (crate_num));
+
+  std::vector<std::unique_ptr<HIR::ExternalItem>> extern_items;
+  for (auto &item : extern_block.get_extern_items ())
+    {
+      if (item->is_marked_for_strip ())
+	continue;
+
+      HIR::ExternalItem *lowered
+	= ASTLoweringExternItem::translate (item.get (), mapping.get_hirid ());
+      extern_items.push_back (std::unique_ptr<HIR::ExternalItem> (lowered));
+    }
+
+  ABI abi = ABI::RUST;
+  if (extern_block.has_abi ())
+    {
+      const std::string &extern_abi = extern_block.get_abi ();
+      abi = get_abi_from_string (extern_abi);
+      if (abi == ABI::UNKNOWN)
+	rust_error_at (extern_block.get_locus (), "unknown ABI option");
+    }
+
+  HIR::ExternBlock *hir_extern_block
+    = new HIR::ExternBlock (mapping, abi, std::move (extern_items),
+			    std::move (vis), extern_block.get_inner_attrs (),
+			    extern_block.get_outer_attrs (),
+			    extern_block.get_locus ());
+
+  mappings->insert_hir_extern_block (hir_extern_block);
+
+  return hir_extern_block;
+}
+
+} // namespace HIR
+} // namespace Rust
diff --git a/gcc/rust/hir/rust-ast-lower-base.h b/gcc/rust/hir/rust-ast-lower-base.h
new file mode 100644
index 00000000000..68c57e0c02b
--- /dev/null
+++ b/gcc/rust/hir/rust-ast-lower-base.h
@@ -0,0 +1,297 @@
+// 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_AST_LOWER_BASE
+#define RUST_AST_LOWER_BASE
+
+#include "rust-system.h"
+#include "rust-ast-full.h"
+#include "rust-ast-visitor.h"
+#include "rust-hir-map.h"
+#include "rust-hir-full.h"
+#include "rust-attributes.h"
+
+namespace Rust {
+namespace HIR {
+
+// base class to allow derivatives to overload as needed
+class ASTLoweringBase : public AST::ASTVisitor
+{
+public:
+  virtual ~ASTLoweringBase () {}
+
+  // visitor impl
+  // rust-ast.h
+  //  virtual void visit(AttrInput& attr_input);
+  //  virtual void visit(TokenTree& token_tree);
+  //  virtual void visit(MacroMatch& macro_match);
+  virtual void visit (AST::Token &tok);
+  virtual void visit (AST::DelimTokenTree &delim_tok_tree);
+  virtual void visit (AST::AttrInputMetaItemContainer &input);
+  //  virtual void visit(MetaItem& meta_item);
+  //  void vsit(Stmt& stmt);
+  //  virtual void visit(Expr& expr);
+  virtual void visit (AST::IdentifierExpr &ident_expr);
+  //  virtual void visit(Pattern& pattern);
+  //  virtual void visit(Type& type);
+  //  virtual void visit(TypeParamBound& type_param_bound);
+  virtual void visit (AST::Lifetime &lifetime);
+  //  virtual void visit(GenericParam& generic_param);
+  virtual void visit (AST::LifetimeParam &lifetime_param);
+  virtual void visit (AST::ConstGenericParam &const_param);
+  //  virtual void visit(TraitItem& trait_item);
+  //  virtual void visit(InherentImplItem& inherent_impl_item);
+  //  virtual void visit(TraitImplItem& trait_impl_item);
+
+  // rust-path.h
+  virtual void visit (AST::PathInExpression &path);
+  virtual void visit (AST::TypePathSegment &segment);
+  virtual void visit (AST::TypePathSegmentGeneric &segment);
+  virtual void visit (AST::TypePathSegmentFunction &segment);
+  virtual void visit (AST::TypePath &path);
+  virtual void visit (AST::QualifiedPathInExpression &path);
+  virtual void visit (AST::QualifiedPathInType &path);
+
+  // rust-expr.h
+  virtual void visit (AST::LiteralExpr &expr);
+  virtual void visit (AST::AttrInputLiteral &attr_input);
+  virtual void visit (AST::MetaItemLitExpr &meta_item);
+  virtual void visit (AST::MetaItemPathLit &meta_item);
+  virtual void visit (AST::BorrowExpr &expr);
+  virtual void visit (AST::DereferenceExpr &expr);
+  virtual void visit (AST::ErrorPropagationExpr &expr);
+  virtual void visit (AST::NegationExpr &expr);
+  virtual void visit (AST::ArithmeticOrLogicalExpr &expr);
+  virtual void visit (AST::ComparisonExpr &expr);
+  virtual void visit (AST::LazyBooleanExpr &expr);
+  virtual void visit (AST::TypeCastExpr &expr);
+  virtual void visit (AST::AssignmentExpr &expr);
+  virtual void visit (AST::CompoundAssignmentExpr &expr);
+  virtual void visit (AST::GroupedExpr &expr);
+  //  virtual void visit(ArrayElems& elems);
+  virtual void visit (AST::ArrayElemsValues &elems);
+  virtual void visit (AST::ArrayElemsCopied &elems);
+  virtual void visit (AST::ArrayExpr &expr);
+  virtual void visit (AST::ArrayIndexExpr &expr);
+  virtual void visit (AST::TupleExpr &expr);
+  virtual void visit (AST::TupleIndexExpr &expr);
+  virtual void visit (AST::StructExprStruct &expr);
+  //  virtual void visit(StructExprField& field);
+  virtual void visit (AST::StructExprFieldIdentifier &field);
+  virtual void visit (AST::StructExprFieldIdentifierValue &field);
+  virtual void visit (AST::StructExprFieldIndexValue &field);
+  virtual void visit (AST::StructExprStructFields &expr);
+  virtual void visit (AST::StructExprStructBase &expr);
+  virtual void visit (AST::CallExpr &expr);
+  virtual void visit (AST::MethodCallExpr &expr);
+  virtual void visit (AST::FieldAccessExpr &expr);
+  virtual void visit (AST::ClosureExprInner &expr);
+  virtual void visit (AST::BlockExpr &expr);
+  virtual void visit (AST::ClosureExprInnerTyped &expr);
+  virtual void visit (AST::ContinueExpr &expr);
+  virtual void visit (AST::BreakExpr &expr);
+  virtual void visit (AST::RangeFromToExpr &expr);
+  virtual void visit (AST::RangeFromExpr &expr);
+  virtual void visit (AST::RangeToExpr &expr);
+  virtual void visit (AST::RangeFullExpr &expr);
+  virtual void visit (AST::RangeFromToInclExpr &expr);
+  virtual void visit (AST::RangeToInclExpr &expr);
+  virtual void visit (AST::ReturnExpr &expr);
+  virtual void visit (AST::UnsafeBlockExpr &expr);
+  virtual void visit (AST::LoopExpr &expr);
+  virtual void visit (AST::WhileLoopExpr &expr);
+  virtual void visit (AST::WhileLetLoopExpr &expr);
+  virtual void visit (AST::ForLoopExpr &expr);
+  virtual void visit (AST::IfExpr &expr);
+  virtual void visit (AST::IfExprConseqElse &expr);
+  virtual void visit (AST::IfExprConseqIf &expr);
+  virtual void visit (AST::IfExprConseqIfLet &expr);
+  virtual void visit (AST::IfLetExpr &expr);
+  virtual void visit (AST::IfLetExprConseqElse &expr);
+  virtual void visit (AST::IfLetExprConseqIf &expr);
+  virtual void visit (AST::IfLetExprConseqIfLet &expr);
+  //  virtual void visit(MatchCase& match_case);
+  // virtual void visit (AST::MatchCaseBlockExpr &match_case);
+  // virtual void visit (AST::MatchCaseExpr &match_case);
+  virtual void visit (AST::MatchExpr &expr);
+  virtual void visit (AST::AwaitExpr &expr);
+  virtual void visit (AST::AsyncBlockExpr &expr);
+
+  // rust-item.h
+  virtual void visit (AST::TypeParam &param);
+  //  virtual void visit(WhereClauseItem& item);
+  virtual void visit (AST::LifetimeWhereClauseItem &item);
+  virtual void visit (AST::TypeBoundWhereClauseItem &item);
+  virtual void visit (AST::Method &method);
+  virtual void visit (AST::Module &module);
+  virtual void visit (AST::ExternCrate &crate);
+  //  virtual void visit(UseTree& use_tree);
+  virtual void visit (AST::UseTreeGlob &use_tree);
+  virtual void visit (AST::UseTreeList &use_tree);
+  virtual void visit (AST::UseTreeRebind &use_tree);
+  virtual void visit (AST::UseDeclaration &use_decl);
+  virtual void visit (AST::Function &function);
+  virtual void visit (AST::TypeAlias &type_alias);
+  virtual void visit (AST::StructStruct &struct_item);
+  virtual void visit (AST::TupleStruct &tuple_struct);
+  virtual void visit (AST::EnumItem &item);
+  virtual void visit (AST::EnumItemTuple &item);
+  virtual void visit (AST::EnumItemStruct &item);
+  virtual void visit (AST::EnumItemDiscriminant &item);
+  virtual void visit (AST::Enum &enum_item);
+  virtual void visit (AST::Union &union_item);
+  virtual void visit (AST::ConstantItem &const_item);
+  virtual void visit (AST::StaticItem &static_item);
+  virtual void visit (AST::TraitItemFunc &item);
+  virtual void visit (AST::TraitItemMethod &item);
+  virtual void visit (AST::TraitItemConst &item);
+  virtual void visit (AST::TraitItemType &item);
+  virtual void visit (AST::Trait &trait);
+  virtual void visit (AST::InherentImpl &impl);
+  virtual void visit (AST::TraitImpl &impl);
+  //  virtual void visit(ExternalItem& item);
+  virtual void visit (AST::ExternalStaticItem &item);
+  virtual void visit (AST::ExternalFunctionItem &item);
+  virtual void visit (AST::ExternBlock &block);
+
+  // rust-macro.h
+  virtual void visit (AST::MacroMatchFragment &match);
+  virtual void visit (AST::MacroMatchRepetition &match);
+  virtual void visit (AST::MacroMatcher &matcher);
+  virtual void visit (AST::MacroRulesDefinition &rules_def);
+  virtual void visit (AST::MacroInvocation &macro_invoc);
+  virtual void visit (AST::MetaItemPath &meta_item);
+  virtual void visit (AST::MetaItemSeq &meta_item);
+  virtual void visit (AST::MetaWord &meta_item);
+  virtual void visit (AST::MetaNameValueStr &meta_item);
+  virtual void visit (AST::MetaListPaths &meta_item);
+  virtual void visit (AST::MetaListNameValueStr &meta_item);
+
+  // rust-pattern.h
+  virtual void visit (AST::LiteralPattern &pattern);
+  virtual void visit (AST::IdentifierPattern &pattern);
+  virtual void visit (AST::WildcardPattern &pattern);
+  //  virtual void visit(RangePatternBound& bound);
+  virtual void visit (AST::RangePatternBoundLiteral &bound);
+  virtual void visit (AST::RangePatternBoundPath &bound);
+  virtual void visit (AST::RangePatternBoundQualPath &bound);
+  virtual void visit (AST::RangePattern &pattern);
+  virtual void visit (AST::ReferencePattern &pattern);
+  //  virtual void visit(StructPatternField& field);
+  virtual void visit (AST::StructPatternFieldTuplePat &field);
+  virtual void visit (AST::StructPatternFieldIdentPat &field);
+  virtual void visit (AST::StructPatternFieldIdent &field);
+  virtual void visit (AST::StructPattern &pattern);
+  //  virtual void visit(TupleStructItems& tuple_items);
+  virtual void visit (AST::TupleStructItemsNoRange &tuple_items);
+  virtual void visit (AST::TupleStructItemsRange &tuple_items);
+  virtual void visit (AST::TupleStructPattern &pattern);
+  //  virtual void visit(TuplePatternItems& tuple_items);
+  virtual void visit (AST::TuplePatternItemsMultiple &tuple_items);
+  virtual void visit (AST::TuplePatternItemsRanged &tuple_items);
+  virtual void visit (AST::TuplePattern &pattern);
+  virtual void visit (AST::GroupedPattern &pattern);
+  virtual void visit (AST::SlicePattern &pattern);
+
+  // rust-stmt.h
+  virtual void visit (AST::EmptyStmt &stmt);
+  virtual void visit (AST::LetStmt &stmt);
+  virtual void visit (AST::ExprStmtWithoutBlock &stmt);
+  virtual void visit (AST::ExprStmtWithBlock &stmt);
+
+  // rust-type.h
+  virtual void visit (AST::TraitBound &bound);
+  virtual void visit (AST::ImplTraitType &type);
+  virtual void visit (AST::TraitObjectType &type);
+  virtual void visit (AST::ParenthesisedType &type);
+  virtual void visit (AST::ImplTraitTypeOneBound &type);
+  virtual void visit (AST::TraitObjectTypeOneBound &type);
+  virtual void visit (AST::TupleType &type);
+  virtual void visit (AST::NeverType &type);
+  virtual void visit (AST::RawPointerType &type);
+  virtual void visit (AST::ReferenceType &type);
+  virtual void visit (AST::ArrayType &type);
+  virtual void visit (AST::SliceType &type);
+  virtual void visit (AST::InferredType &type);
+  virtual void visit (AST::BareFunctionType &type);
+
+protected:
+  ASTLoweringBase ()
+    : mappings (Analysis::Mappings::get ()),
+      attr_mappings (Analysis::BuiltinAttributeMappings::get ())
+  {}
+
+  Analysis::Mappings *mappings;
+  Analysis::BuiltinAttributeMappings *attr_mappings;
+
+  HIR::Lifetime lower_lifetime (AST::Lifetime &lifetime);
+
+  HIR::LoopLabel lower_loop_label (AST::LoopLabel &loop_label);
+
+  std::vector<std::unique_ptr<HIR::GenericParam> > lower_generic_params (
+    std::vector<std::unique_ptr<AST::GenericParam> > &params);
+
+  HIR::PathExprSegment lower_path_expr_seg (AST::PathExprSegment &s);
+
+  HIR::GenericArgs lower_generic_args (AST::GenericArgs &args);
+
+  HIR::GenericArgsBinding lower_binding (AST::GenericArgsBinding &binding);
+
+  HIR::SelfParam lower_self (AST::SelfParam &self);
+
+  HIR::Type *lower_type_no_bounds (AST::TypeNoBounds *type);
+
+  HIR::TypeParamBound *lower_bound (AST::TypeParamBound *bound);
+
+  HIR::QualifiedPathType
+  lower_qual_path_type (AST::QualifiedPathType &qual_path_type);
+
+  HIR::FunctionQualifiers
+  lower_qualifiers (const AST::FunctionQualifiers &qualifiers);
+
+  void handle_outer_attributes (const HIR::Item &item);
+
+  void handle_lang_item_attribute (const HIR::Item &item,
+				   const AST::Attribute &attr);
+
+  void handle_doc_item_attribute (const HIR::Item &item,
+				  const AST::Attribute &attr);
+
+  bool is_known_attribute (const std::string &attribute_path) const;
+
+  bool
+  attribute_handled_in_another_pass (const std::string &attribute_path) const;
+
+  std::unique_ptr<TuplePatternItems>
+  lower_tuple_pattern_multiple (AST::TuplePatternItemsMultiple &pattern);
+
+  std::unique_ptr<TuplePatternItems>
+  lower_tuple_pattern_ranged (AST::TuplePatternItemsRanged &pattern);
+
+  std::unique_ptr<HIR::RangePatternBound>
+  lower_range_pattern_bound (AST::RangePatternBound *bound);
+
+  HIR::Literal lower_literal (const AST::Literal &literal);
+
+  HIR::ExternBlock *lower_extern_block (AST::ExternBlock &extern_block);
+};
+
+} // namespace HIR
+} // namespace Rust
+
+#endif // RUST_AST_LOWER_BASE
diff --git a/gcc/rust/hir/rust-ast-lower-block.h b/gcc/rust/hir/rust-ast-lower-block.h
new file mode 100644
index 00000000000..0d3c704c6f1
--- /dev/null
+++ b/gcc/rust/hir/rust-ast-lower-block.h
@@ -0,0 +1,230 @@
+// 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_AST_LOWER_BLOCK
+#define RUST_AST_LOWER_BLOCK
+
+#include "rust-diagnostics.h"
+#include "rust-ast-lower-base.h"
+
+namespace Rust {
+namespace HIR {
+
+class ASTLoweringBlock : public ASTLoweringBase
+{
+  using Rust::HIR::ASTLoweringBase::visit;
+
+public:
+  static HIR::BlockExpr *translate (AST::BlockExpr *expr, bool *terminated)
+  {
+    ASTLoweringBlock resolver;
+    expr->accept_vis (resolver);
+    if (resolver.translated != nullptr)
+      {
+	resolver.mappings->insert_hir_expr (resolver.translated);
+      }
+
+    *terminated = resolver.terminated;
+    return resolver.translated;
+  }
+
+  static HIR::UnsafeBlockExpr *translate (AST::UnsafeBlockExpr *expr,
+					  bool *terminated)
+  {
+    ASTLoweringBlock resolver;
+
+    HIR::BlockExpr *block
+      = ASTLoweringBlock::translate (expr->get_block_expr ().get (),
+				     terminated);
+    auto crate_num = resolver.mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, expr->get_node_id (),
+				   resolver.mappings->get_next_hir_id (
+				     crate_num),
+				   UNKNOWN_LOCAL_DEFID);
+
+    HIR::UnsafeBlockExpr *translated
+      = new HIR::UnsafeBlockExpr (mapping,
+				  std::unique_ptr<HIR::BlockExpr> (block),
+				  expr->get_outer_attrs (), expr->get_locus ());
+
+    resolver.mappings->insert_hir_expr (translated);
+
+    return translated;
+  }
+
+  void visit (AST::BlockExpr &expr) override;
+
+private:
+  ASTLoweringBlock ()
+    : ASTLoweringBase (), translated (nullptr), terminated (false)
+  {}
+
+  HIR::BlockExpr *translated;
+  bool terminated;
+};
+
+class ASTLoweringIfBlock : public ASTLoweringBase
+{
+  using Rust::HIR::ASTLoweringBase::visit;
+
+public:
+  static HIR::IfExpr *translate (AST::IfExpr *expr, bool *terminated)
+  {
+    ASTLoweringIfBlock resolver;
+    expr->accept_vis (resolver);
+    if (resolver.translated != nullptr)
+      {
+	resolver.mappings->insert_hir_expr (resolver.translated);
+      }
+    *terminated = resolver.terminated;
+    return resolver.translated;
+  }
+
+  ~ASTLoweringIfBlock () {}
+
+  void visit (AST::IfExpr &expr) override;
+
+  void visit (AST::IfExprConseqElse &expr) override;
+
+  void visit (AST::IfExprConseqIf &expr) override;
+
+private:
+  ASTLoweringIfBlock ()
+    : ASTLoweringBase (), translated (nullptr), terminated (false)
+  {}
+
+  HIR::IfExpr *translated;
+  bool terminated;
+};
+
+class ASTLoweringIfLetBlock : public ASTLoweringBase
+{
+  using Rust::HIR::ASTLoweringBase::visit;
+
+public:
+  static HIR::IfLetExpr *translate (AST::IfLetExpr *expr)
+  {
+    ASTLoweringIfLetBlock resolver;
+    expr->accept_vis (resolver);
+    if (resolver.translated != nullptr)
+      {
+	resolver.mappings->insert_hir_expr (resolver.translated);
+      }
+    return resolver.translated;
+  }
+
+  ~ASTLoweringIfLetBlock () {}
+
+  void visit (AST::IfLetExpr &expr) override;
+
+private:
+  ASTLoweringIfLetBlock () : ASTLoweringBase (), translated (nullptr) {}
+
+  HIR::IfLetExpr *translated;
+};
+
+class ASTLoweringExprWithBlock : public ASTLoweringBase
+{
+  using Rust::HIR::ASTLoweringBase::visit;
+
+public:
+  static HIR::ExprWithBlock *translate (AST::ExprWithBlock *expr,
+					bool *terminated)
+  {
+    ASTLoweringExprWithBlock resolver;
+    expr->accept_vis (resolver);
+    if (resolver.translated != nullptr)
+      {
+	resolver.mappings->insert_hir_expr (resolver.translated);
+      }
+
+    *terminated = resolver.terminated;
+    return resolver.translated;
+  }
+
+  ~ASTLoweringExprWithBlock () {}
+
+  void visit (AST::IfExpr &expr) override
+  {
+    translated = ASTLoweringIfBlock::translate (&expr, &terminated);
+  }
+
+  void visit (AST::IfExprConseqElse &expr) override
+  {
+    translated = ASTLoweringIfBlock::translate (&expr, &terminated);
+  }
+
+  void visit (AST::IfExprConseqIf &expr) override
+  {
+    translated = ASTLoweringIfBlock::translate (&expr, &terminated);
+  }
+
+  void visit (AST::IfLetExpr &expr) override
+  {
+    translated = ASTLoweringIfLetBlock::translate (&expr);
+  }
+
+  void visit (AST::BlockExpr &expr) override
+  {
+    translated = ASTLoweringBlock::translate (&expr, &terminated);
+  }
+
+  void visit (AST::UnsafeBlockExpr &expr) override
+  {
+    translated = ASTLoweringBlock::translate (&expr, &terminated);
+  }
+
+  void visit (AST::LoopExpr &expr) override
+  {
+    HIR::BlockExpr *loop_block
+      = ASTLoweringBlock::translate (expr.get_loop_block ().get (),
+				     &terminated);
+
+    HIR::LoopLabel loop_label = lower_loop_label (expr.get_loop_label ());
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   UNKNOWN_LOCAL_DEFID);
+
+    translated
+      = new HIR::LoopExpr (mapping,
+			   std::unique_ptr<HIR::BlockExpr> (loop_block),
+			   expr.get_locus (), std::move (loop_label),
+			   expr.get_outer_attrs ());
+  }
+
+  void visit (AST::WhileLoopExpr &expr) override;
+
+  void visit (AST::ForLoopExpr &expr) override;
+
+  void visit (AST::MatchExpr &expr) override;
+
+private:
+  ASTLoweringExprWithBlock ()
+    : ASTLoweringBase (), translated (nullptr), terminated (false)
+  {}
+
+  HIR::ExprWithBlock *translated;
+  bool terminated;
+};
+
+} // namespace HIR
+} // namespace Rust
+
+#endif // RUST_AST_LOWER_BLOCK
diff --git a/gcc/rust/hir/rust-ast-lower-enumitem.h b/gcc/rust/hir/rust-ast-lower-enumitem.h
new file mode 100644
index 00000000000..b76658c78cc
--- /dev/null
+++ b/gcc/rust/hir/rust-ast-lower-enumitem.h
@@ -0,0 +1,181 @@
+// 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_AST_LOWER_ENUMITEM
+#define RUST_AST_LOWER_ENUMITEM
+
+#include "rust-ast-lower.h"
+#include "rust-diagnostics.h"
+
+#include "rust-ast-lower-base.h"
+#include "rust-ast-lower-type.h"
+#include "rust-ast-lower-expr.h"
+#include "rust-hir-full-decls.h"
+
+namespace Rust {
+namespace HIR {
+
+class ASTLoweringEnumItem : public ASTLoweringBase
+{
+  using Rust::HIR::ASTLoweringBase::visit;
+
+public:
+  static HIR::EnumItem *translate (AST::EnumItem *item)
+  {
+    ASTLoweringEnumItem resolver;
+    item->accept_vis (resolver);
+
+    rust_assert (resolver.translated != nullptr);
+
+    auto hirid = resolver.translated->get_mappings ().get_hirid ();
+    auto defid = resolver.translated->get_mappings ().get_defid ();
+
+    resolver.mappings->insert_defid_mapping (defid, resolver.translated);
+    resolver.mappings->insert_hir_item (resolver.translated);
+    resolver.mappings->insert_location (hirid,
+					resolver.translated->get_locus ());
+
+    return resolver.translated;
+  }
+
+  void visit (AST::EnumItem &item) override
+  {
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, item.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   mappings->get_next_localdef_id (crate_num));
+
+    if (item.has_visibility ())
+      rust_error_at (item.get_locus (),
+		     "visibility qualifier %qs not allowed on enum item",
+		     item.get_visibility ().as_string ().c_str ());
+    translated = new HIR::EnumItem (mapping, item.get_identifier (),
+				    item.get_outer_attrs (), item.get_locus ());
+  }
+
+  void visit (AST::EnumItemTuple &item) override
+  {
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, item.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   mappings->get_next_localdef_id (crate_num));
+
+    if (item.has_visibility ())
+      rust_error_at (item.get_locus (),
+		     "visibility qualifier %qs not allowed on enum item",
+		     item.get_visibility ().as_string ().c_str ());
+
+    std::vector<HIR::TupleField> fields;
+    for (auto &field : item.get_tuple_fields ())
+      {
+	HIR::Visibility vis = translate_visibility (field.get_visibility ());
+	HIR::Type *type
+	  = ASTLoweringType::translate (field.get_field_type ().get ());
+
+	auto crate_num = mappings->get_current_crate ();
+	Analysis::NodeMapping field_mapping (
+	  crate_num, field.get_node_id (),
+	  mappings->get_next_hir_id (crate_num),
+	  mappings->get_next_localdef_id (crate_num));
+
+	HIR::TupleField translated_field (field_mapping,
+					  std::unique_ptr<HIR::Type> (type),
+					  vis, field.get_locus (),
+					  field.get_outer_attrs ());
+	fields.push_back (std::move (translated_field));
+      }
+
+    translated
+      = new HIR::EnumItemTuple (mapping, item.get_identifier (),
+				std::move (fields), item.get_outer_attrs (),
+				item.get_locus ());
+  }
+
+  void visit (AST::EnumItemStruct &item) override
+  {
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, item.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   mappings->get_next_localdef_id (crate_num));
+
+    if (item.has_visibility ())
+      rust_error_at (item.get_locus (),
+		     "visibility qualifier %qs not allowed on enum item",
+		     item.get_visibility ().as_string ().c_str ());
+
+    std::vector<HIR::StructField> fields;
+    for (auto &field : item.get_struct_fields ())
+      {
+	HIR::Visibility vis = translate_visibility (field.get_visibility ());
+	HIR::Type *type
+	  = ASTLoweringType::translate (field.get_field_type ().get ());
+
+	auto crate_num = mappings->get_current_crate ();
+	Analysis::NodeMapping field_mapping (
+	  crate_num, field.get_node_id (),
+	  mappings->get_next_hir_id (crate_num),
+	  mappings->get_next_localdef_id (crate_num));
+
+	HIR::StructField translated_field (field_mapping,
+					   field.get_field_name (),
+					   std::unique_ptr<HIR::Type> (type),
+					   vis, field.get_locus (),
+					   field.get_outer_attrs ());
+
+	if (struct_field_name_exists (fields, translated_field))
+	  break;
+
+	fields.push_back (std::move (translated_field));
+      }
+
+    translated
+      = new HIR::EnumItemStruct (mapping, item.get_identifier (),
+				 std::move (fields), item.get_outer_attrs (),
+				 item.get_locus ());
+  }
+
+  void visit (AST::EnumItemDiscriminant &item) override
+  {
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, item.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   mappings->get_next_localdef_id (crate_num));
+
+    if (item.has_visibility ())
+      rust_error_at (item.get_locus (),
+		     "visibility qualifier %qs not allowed on enum item",
+		     item.get_visibility ().as_string ().c_str ());
+
+    HIR::Expr *expr = ASTLoweringExpr::translate (item.get_expr ().get ());
+    translated
+      = new HIR::EnumItemDiscriminant (mapping, item.get_identifier (),
+				       std::unique_ptr<HIR::Expr> (expr),
+				       item.get_outer_attrs (),
+				       item.get_locus ());
+  }
+
+private:
+  ASTLoweringEnumItem () : translated (nullptr) {}
+
+  HIR::EnumItem *translated;
+};
+
+} // namespace HIR
+} // namespace Rust
+
+#endif // RUST_AST_LOWER_ENUMITEM
diff --git a/gcc/rust/hir/rust-ast-lower-expr.h b/gcc/rust/hir/rust-ast-lower-expr.h
new file mode 100644
index 00000000000..4f7f40f27e4
--- /dev/null
+++ b/gcc/rust/hir/rust-ast-lower-expr.h
@@ -0,0 +1,766 @@
+// 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_AST_LOWER_EXPR
+#define RUST_AST_LOWER_EXPR
+
+#include "rust-diagnostics.h"
+#include "rust-ast-lower-base.h"
+#include "rust-ast-lower-block.h"
+#include "rust-ast-lower-struct-field-expr.h"
+#include "rust-ast-lower-pattern.h"
+
+namespace Rust {
+namespace HIR {
+
+class ASTLowerPathInExpression : public ASTLoweringBase
+{
+  using Rust::HIR::ASTLoweringBase::visit;
+
+public:
+  static HIR::PathInExpression *translate (AST::PathInExpression *expr)
+  {
+    ASTLowerPathInExpression compiler;
+    expr->accept_vis (compiler);
+    rust_assert (compiler.translated);
+    return compiler.translated;
+  }
+
+  void visit (AST::PathInExpression &expr) override;
+
+private:
+  ASTLowerPathInExpression () : translated (nullptr) {}
+
+  HIR::PathInExpression *translated;
+};
+
+class ASTLowerQualPathInExpression : public ASTLoweringBase
+{
+  using Rust::HIR::ASTLoweringBase::visit;
+
+public:
+  static HIR::QualifiedPathInExpression *
+  translate (AST::QualifiedPathInExpression *expr)
+  {
+    ASTLowerQualPathInExpression compiler;
+    expr->accept_vis (compiler);
+    rust_assert (compiler.translated);
+    return compiler.translated;
+  }
+
+  void visit (AST::QualifiedPathInExpression &expr) override;
+
+private:
+  ASTLowerQualPathInExpression () : translated (nullptr) {}
+
+  HIR::QualifiedPathInExpression *translated;
+};
+
+class ASTLoweringExpr : public ASTLoweringBase
+{
+  using Rust::HIR::ASTLoweringBase::visit;
+
+public:
+  static HIR::Expr *translate (AST::Expr *expr, bool *terminated = nullptr)
+  {
+    ASTLoweringExpr resolver;
+    expr->accept_vis (resolver);
+    if (resolver.translated == nullptr)
+      {
+	rust_fatal_error (expr->get_locus (), "Failed to lower expr: [%s]",
+			  expr->as_string ().c_str ());
+	return nullptr;
+      }
+
+    resolver.mappings->insert_hir_expr (resolver.translated);
+    resolver.mappings->insert_location (
+      resolver.translated->get_mappings ().get_hirid (), expr->get_locus ());
+
+    if (terminated != nullptr)
+      *terminated = resolver.terminated;
+
+    return resolver.translated;
+  }
+
+  void visit (AST::TupleIndexExpr &expr) override
+  {
+    HIR::Expr *tuple_expr
+      = ASTLoweringExpr::translate (expr.get_tuple_expr ().get (), &terminated);
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   UNKNOWN_LOCAL_DEFID);
+
+    translated
+      = new HIR::TupleIndexExpr (mapping,
+				 std::unique_ptr<HIR::Expr> (tuple_expr),
+				 expr.get_tuple_index (),
+				 expr.get_outer_attrs (), expr.get_locus ());
+  }
+
+  void visit (AST::TupleExpr &expr) override
+  {
+    std::vector<std::unique_ptr<HIR::Expr> > tuple_elements;
+    for (auto &e : expr.get_tuple_elems ())
+      {
+	HIR::Expr *t = ASTLoweringExpr::translate (e.get ());
+	tuple_elements.push_back (std::unique_ptr<HIR::Expr> (t));
+      }
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   UNKNOWN_LOCAL_DEFID);
+
+    translated
+      = new HIR::TupleExpr (std::move (mapping), std::move (tuple_elements),
+			    expr.get_inner_attrs (), expr.get_outer_attrs (),
+			    expr.get_locus ());
+  }
+
+  void visit (AST::IfExpr &expr) override
+  {
+    translated = ASTLoweringIfBlock::translate (&expr, &terminated);
+  }
+
+  void visit (AST::IfExprConseqElse &expr) override
+  {
+    translated = ASTLoweringIfBlock::translate (&expr, &terminated);
+  }
+
+  void visit (AST::IfExprConseqIf &expr) override
+  {
+    translated = ASTLoweringIfBlock::translate (&expr, &terminated);
+  }
+
+  void visit (AST::BlockExpr &expr) override
+  {
+    translated = ASTLoweringBlock::translate (&expr, &terminated);
+  }
+
+  void visit (AST::UnsafeBlockExpr &expr) override
+  {
+    translated = ASTLoweringBlock::translate (&expr, &terminated);
+  }
+
+  void visit (AST::PathInExpression &expr) override
+  {
+    translated = ASTLowerPathInExpression::translate (&expr);
+  }
+
+  void visit (AST::QualifiedPathInExpression &expr) override
+  {
+    translated = ASTLowerQualPathInExpression::translate (&expr);
+  }
+
+  void visit (AST::ReturnExpr &expr) override
+  {
+    terminated = true;
+    HIR::Expr *return_expr
+      = expr.has_returned_expr ()
+	  ? ASTLoweringExpr::translate (expr.get_returned_expr ().get ())
+	  : nullptr;
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   UNKNOWN_LOCAL_DEFID);
+
+    translated = new HIR::ReturnExpr (mapping, expr.get_locus (),
+				      std::unique_ptr<HIR::Expr> (return_expr));
+  }
+
+  void visit (AST::CallExpr &expr) override
+  {
+    HIR::Expr *func
+      = ASTLoweringExpr::translate (expr.get_function_expr ().get ());
+
+    auto const &in_params = expr.get_params ();
+    std::vector<std::unique_ptr<HIR::Expr> > params;
+    for (auto &param : in_params)
+      {
+	auto trans = ASTLoweringExpr::translate (param.get ());
+	params.push_back (std::unique_ptr<HIR::Expr> (trans));
+      }
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (
+      crate_num, UNKNOWN_NODEID /* this can map back to the AST*/,
+      mappings->get_next_hir_id (crate_num), UNKNOWN_LOCAL_DEFID);
+
+    translated = new HIR::CallExpr (mapping, std::unique_ptr<HIR::Expr> (func),
+				    std::move (params), expr.get_outer_attrs (),
+				    expr.get_locus ());
+  }
+
+  void visit (AST::MethodCallExpr &expr) override
+  {
+    HIR::PathExprSegment method_path
+      = lower_path_expr_seg (expr.get_method_name ());
+
+    HIR::Expr *receiver
+      = ASTLoweringExpr::translate (expr.get_receiver_expr ().get ());
+
+    auto const &in_params = expr.get_params ();
+    std::vector<std::unique_ptr<HIR::Expr> > params;
+    for (auto &param : in_params)
+      {
+	auto trans = ASTLoweringExpr::translate (param.get ());
+	params.push_back (std::unique_ptr<HIR::Expr> (trans));
+      }
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   UNKNOWN_LOCAL_DEFID);
+
+    translated
+      = new HIR::MethodCallExpr (mapping, std::unique_ptr<HIR::Expr> (receiver),
+				 method_path, std::move (params),
+				 expr.get_outer_attrs (), expr.get_locus ());
+  }
+
+  void visit (AST::AssignmentExpr &expr) override
+  {
+    HIR::Expr *lhs = ASTLoweringExpr::translate (expr.get_left_expr ().get ());
+    HIR::Expr *rhs = ASTLoweringExpr::translate (expr.get_right_expr ().get ());
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   UNKNOWN_LOCAL_DEFID);
+
+    translated
+      = new HIR::AssignmentExpr (mapping, std::unique_ptr<HIR::Expr> (lhs),
+				 std::unique_ptr<HIR::Expr> (rhs),
+				 expr.get_locus ());
+  }
+
+  void visit (AST::IdentifierExpr &expr) override
+  {
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping1 (crate_num, expr.get_node_id (),
+				    mappings->get_next_hir_id (crate_num),
+				    UNKNOWN_LOCAL_DEFID);
+    Analysis::NodeMapping mapping2 (mapping1);
+
+    HIR::PathIdentSegment ident_seg (expr.get_ident ());
+    HIR::PathExprSegment seg (mapping1, ident_seg, expr.get_locus (),
+			      HIR::GenericArgs::create_empty ());
+    translated = new HIR::PathInExpression (mapping2, {seg}, expr.get_locus (),
+					    false, expr.get_outer_attrs ());
+  }
+
+  void visit (AST::ArrayExpr &expr) override
+  {
+    expr.get_array_elems ()->accept_vis (*this);
+    rust_assert (translated_array_elems != nullptr);
+    HIR::ArrayElems *elems = translated_array_elems;
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   UNKNOWN_LOCAL_DEFID);
+
+    translated
+      = new HIR::ArrayExpr (mapping, std::unique_ptr<HIR::ArrayElems> (elems),
+			    expr.get_inner_attrs (), expr.get_outer_attrs (),
+			    expr.get_locus ());
+  }
+
+  void visit (AST::ArrayIndexExpr &expr) override
+  {
+    HIR::Expr *array_expr
+      = ASTLoweringExpr::translate (expr.get_array_expr ().get ());
+    HIR::Expr *array_index_expr
+      = ASTLoweringExpr::translate (expr.get_index_expr ().get ());
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   UNKNOWN_LOCAL_DEFID);
+
+    translated
+      = new HIR::ArrayIndexExpr (mapping,
+				 std::unique_ptr<HIR::Expr> (array_expr),
+				 std::unique_ptr<HIR::Expr> (array_index_expr),
+				 expr.get_outer_attrs (), expr.get_locus ());
+  }
+
+  void visit (AST::ArrayElemsValues &elems) override
+  {
+    std::vector<std::unique_ptr<HIR::Expr> > elements;
+    for (auto &elem : elems.get_values ())
+      {
+	HIR::Expr *translated_elem = ASTLoweringExpr::translate (elem.get ());
+	elements.push_back (std::unique_ptr<HIR::Expr> (translated_elem));
+      }
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (mappings->get_current_crate (),
+				   elems.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   UNKNOWN_LOCAL_DEFID);
+
+    translated_array_elems
+      = new HIR::ArrayElemsValues (mapping, std::move (elements));
+  }
+
+  void visit (AST::ArrayElemsCopied &elems) override
+  {
+    HIR::Expr *element
+      = ASTLoweringExpr::translate (elems.get_elem_to_copy ().get ());
+    HIR::Expr *num_copies
+      = ASTLoweringExpr::translate (elems.get_num_copies ().get ());
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (mappings->get_current_crate (),
+				   elems.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   UNKNOWN_LOCAL_DEFID);
+
+    translated_array_elems
+      = new HIR::ArrayElemsCopied (mapping,
+				   std::unique_ptr<HIR::Expr> (element),
+				   std::unique_ptr<HIR::Expr> (num_copies));
+  }
+
+  void visit (AST::LiteralExpr &expr) override
+  {
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   UNKNOWN_LOCAL_DEFID);
+
+    HIR::Literal l = lower_literal (expr.get_literal ());
+    translated
+      = new HIR::LiteralExpr (mapping, std::move (l), expr.get_locus (),
+			      expr.get_outer_attrs ());
+  }
+
+  void visit (AST::ArithmeticOrLogicalExpr &expr) override
+  {
+    HIR::Expr *lhs = ASTLoweringExpr::translate (expr.get_left_expr ().get ());
+    rust_assert (lhs != nullptr);
+    HIR::Expr *rhs = ASTLoweringExpr::translate (expr.get_right_expr ().get ());
+    rust_assert (rhs != nullptr);
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   UNKNOWN_LOCAL_DEFID);
+
+    translated
+      = new HIR::ArithmeticOrLogicalExpr (mapping,
+					  std::unique_ptr<HIR::Expr> (lhs),
+					  std::unique_ptr<HIR::Expr> (rhs),
+					  expr.get_expr_type (),
+					  expr.get_locus ());
+  }
+
+  void visit (AST::ComparisonExpr &expr) override
+  {
+    HIR::Expr *lhs = ASTLoweringExpr::translate (expr.get_left_expr ().get ());
+    rust_assert (lhs != nullptr);
+    HIR::Expr *rhs = ASTLoweringExpr::translate (expr.get_right_expr ().get ());
+    rust_assert (rhs != nullptr);
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   UNKNOWN_LOCAL_DEFID);
+
+    translated
+      = new HIR::ComparisonExpr (mapping, std::unique_ptr<HIR::Expr> (lhs),
+				 std::unique_ptr<HIR::Expr> (rhs),
+				 expr.get_expr_type (), expr.get_locus ());
+  }
+
+  void visit (AST::LazyBooleanExpr &expr) override
+  {
+    HIR::Expr *lhs = ASTLoweringExpr::translate (expr.get_left_expr ().get ());
+    rust_assert (lhs != nullptr);
+    HIR::Expr *rhs = ASTLoweringExpr::translate (expr.get_right_expr ().get ());
+    rust_assert (rhs != nullptr);
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   UNKNOWN_LOCAL_DEFID);
+
+    translated
+      = new HIR::LazyBooleanExpr (mapping, std::unique_ptr<HIR::Expr> (lhs),
+				  std::unique_ptr<HIR::Expr> (rhs),
+				  expr.get_expr_type (), expr.get_locus ());
+  }
+
+  void visit (AST::NegationExpr &expr) override
+  {
+    HIR::Expr *negated_value
+      = ASTLoweringExpr::translate (expr.get_negated_expr ().get ());
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   UNKNOWN_LOCAL_DEFID);
+    translated
+      = new HIR::NegationExpr (mapping,
+			       std::unique_ptr<HIR::Expr> (negated_value),
+			       expr.get_expr_type (), expr.get_outer_attrs (),
+			       expr.get_locus ());
+  }
+
+  void visit (AST::TypeCastExpr &expr) override
+  {
+    HIR::Expr *expr_to_cast_to
+      = ASTLoweringExpr::translate (expr.get_casted_expr ().get ());
+    HIR::Type *type_to_cast_to
+      = lower_type_no_bounds (expr.get_type_to_cast_to ().get ());
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   UNKNOWN_LOCAL_DEFID);
+
+    translated
+      = new HIR::TypeCastExpr (mapping,
+			       std::unique_ptr<HIR::Expr> (expr_to_cast_to),
+			       std::unique_ptr<HIR::Type> (type_to_cast_to),
+			       expr.get_locus ());
+  }
+
+  void visit (AST::CompoundAssignmentExpr &expr) override
+  {
+    ArithmeticOrLogicalOperator op;
+    switch (expr.get_expr_type ())
+      {
+      case CompoundAssignmentOperator::ADD:
+	op = ArithmeticOrLogicalOperator::ADD;
+	break;
+      case CompoundAssignmentOperator::SUBTRACT:
+	op = ArithmeticOrLogicalOperator::SUBTRACT;
+	break;
+      case CompoundAssignmentOperator::MULTIPLY:
+	op = ArithmeticOrLogicalOperator::MULTIPLY;
+	break;
+      case CompoundAssignmentOperator::DIVIDE:
+	op = ArithmeticOrLogicalOperator::DIVIDE;
+	break;
+      case CompoundAssignmentOperator::MODULUS:
+	op = ArithmeticOrLogicalOperator::MODULUS;
+	break;
+      case CompoundAssignmentOperator::BITWISE_AND:
+	op = ArithmeticOrLogicalOperator::BITWISE_AND;
+	break;
+      case CompoundAssignmentOperator::BITWISE_OR:
+	op = ArithmeticOrLogicalOperator::BITWISE_OR;
+	break;
+      case CompoundAssignmentOperator::BITWISE_XOR:
+	op = ArithmeticOrLogicalOperator::BITWISE_XOR;
+	break;
+      case CompoundAssignmentOperator::LEFT_SHIFT:
+	op = ArithmeticOrLogicalOperator::LEFT_SHIFT;
+	break;
+      case CompoundAssignmentOperator::RIGHT_SHIFT:
+	op = ArithmeticOrLogicalOperator::RIGHT_SHIFT;
+	break;
+      default:
+	gcc_unreachable ();
+      }
+
+    HIR::Expr *asignee_expr
+      = ASTLoweringExpr::translate (expr.get_left_expr ().get ());
+    HIR::Expr *value
+      = ASTLoweringExpr::translate (expr.get_right_expr ().get ());
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   UNKNOWN_LOCAL_DEFID);
+
+    translated = new HIR::CompoundAssignmentExpr (
+      mapping, std::unique_ptr<HIR::Expr> (asignee_expr),
+      std::unique_ptr<HIR::Expr> (value), op, expr.get_locus ());
+  }
+
+  void visit (AST::StructExprStruct &struct_expr) override
+  {
+    HIR::PathInExpression *path
+      = ASTLowerPathInExpression::translate (&struct_expr.get_struct_name ());
+    HIR::PathInExpression copied_path (*path);
+    delete path;
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, struct_expr.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   UNKNOWN_LOCAL_DEFID);
+
+    translated = new HIR::StructExprStruct (mapping, copied_path,
+					    struct_expr.get_inner_attrs (),
+					    struct_expr.get_outer_attrs (),
+					    struct_expr.get_locus ());
+  }
+
+  void visit (AST::StructExprStructFields &struct_expr) override
+  {
+    // bit of a hack for now
+    HIR::PathInExpression *path
+      = ASTLowerPathInExpression::translate (&struct_expr.get_struct_name ());
+    HIR::PathInExpression copied_path (*path);
+    delete path;
+
+    HIR::StructBase *base = nullptr;
+    if (struct_expr.has_struct_base ())
+      {
+	HIR::Expr *translated_base = ASTLoweringExpr::translate (
+	  struct_expr.get_struct_base ().get_base_struct ().get ());
+	base
+	  = new HIR::StructBase (std::unique_ptr<HIR::Expr> (translated_base));
+      }
+
+    auto const &in_fields = struct_expr.get_fields ();
+    std::vector<std::unique_ptr<HIR::StructExprField> > fields;
+    for (auto &field : in_fields)
+      {
+	HIR::StructExprField *translated
+	  = ASTLowerStructExprField::translate (field.get ());
+	fields.push_back (std::unique_ptr<HIR::StructExprField> (translated));
+      }
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, struct_expr.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   UNKNOWN_LOCAL_DEFID);
+
+    translated = new HIR::StructExprStructFields (
+      mapping, copied_path, std::move (fields), struct_expr.get_locus (), base,
+      struct_expr.get_inner_attrs (), struct_expr.get_outer_attrs ());
+  }
+
+  void visit (AST::GroupedExpr &expr) override
+  {
+    HIR::Expr *paren_expr
+      = ASTLoweringExpr::translate (expr.get_expr_in_parens ().get ());
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   UNKNOWN_LOCAL_DEFID);
+
+    translated
+      = new HIR::GroupedExpr (mapping, std::unique_ptr<HIR::Expr> (paren_expr),
+			      expr.get_inner_attrs (), expr.get_outer_attrs (),
+			      expr.get_locus ());
+  }
+
+  void visit (AST::FieldAccessExpr &expr) override
+  {
+    HIR::Expr *receiver
+      = ASTLoweringExpr::translate (expr.get_receiver_expr ().get ());
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   UNKNOWN_LOCAL_DEFID);
+    translated
+      = new HIR::FieldAccessExpr (mapping,
+				  std::unique_ptr<HIR::Expr> (receiver),
+				  expr.get_field_name (),
+				  expr.get_outer_attrs (), expr.get_locus ());
+  }
+
+  void visit (AST::LoopExpr &expr) override
+  {
+    translated = ASTLoweringExprWithBlock::translate (&expr, &terminated);
+  }
+
+  void visit (AST::WhileLoopExpr &expr) override
+  {
+    translated = ASTLoweringExprWithBlock::translate (&expr, &terminated);
+  }
+
+  void visit (AST::ForLoopExpr &expr) override
+  {
+    translated = ASTLoweringExprWithBlock::translate (&expr, &terminated);
+  }
+
+  void visit (AST::BreakExpr &expr) override
+  {
+    HIR::Lifetime break_label = lower_lifetime (expr.get_label ());
+    HIR::Expr *break_expr
+      = expr.has_break_expr ()
+	  ? ASTLoweringExpr::translate (expr.get_break_expr ().get ())
+	  : nullptr;
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   UNKNOWN_LOCAL_DEFID);
+
+    translated = new HIR::BreakExpr (mapping, expr.get_locus (),
+				     std ::move (break_label),
+				     std::unique_ptr<HIR::Expr> (break_expr),
+				     expr.get_outer_attrs ());
+  }
+
+  void visit (AST::ContinueExpr &expr) override
+  {
+    HIR::Lifetime break_label = lower_lifetime (expr.get_label ());
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   UNKNOWN_LOCAL_DEFID);
+
+    translated = new HIR::ContinueExpr (mapping, expr.get_locus (),
+					std ::move (break_label),
+					expr.get_outer_attrs ());
+  }
+
+  void visit (AST::BorrowExpr &expr) override
+  {
+    HIR::Expr *borrow_lvalue
+      = ASTLoweringExpr::translate (expr.get_borrowed_expr ().get ());
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   UNKNOWN_LOCAL_DEFID);
+
+    translated = new HIR::BorrowExpr (
+      mapping, std::unique_ptr<HIR::Expr> (borrow_lvalue),
+      expr.get_is_mut () ? Mutability::Mut : Mutability::Imm,
+      expr.get_is_double_borrow (), expr.get_outer_attrs (), expr.get_locus ());
+  }
+
+  void visit (AST::DereferenceExpr &expr) override
+  {
+    HIR::Expr *dref_lvalue
+      = ASTLoweringExpr::translate (expr.get_dereferenced_expr ().get ());
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   UNKNOWN_LOCAL_DEFID);
+
+    translated
+      = new HIR::DereferenceExpr (mapping,
+				  std::unique_ptr<HIR::Expr> (dref_lvalue),
+				  expr.get_outer_attrs (), expr.get_locus ());
+  }
+
+  void visit (AST::MatchExpr &expr) override
+  {
+    translated = ASTLoweringExprWithBlock::translate (&expr, &terminated);
+  }
+
+  void visit (AST::RangeFromToExpr &expr) override
+  {
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   UNKNOWN_LOCAL_DEFID);
+
+    HIR::Expr *range_from
+      = ASTLoweringExpr::translate (expr.get_from_expr ().get ());
+    HIR::Expr *range_to
+      = ASTLoweringExpr::translate (expr.get_to_expr ().get ());
+
+    translated
+      = new HIR::RangeFromToExpr (mapping,
+				  std::unique_ptr<HIR::Expr> (range_from),
+				  std::unique_ptr<HIR::Expr> (range_to),
+				  expr.get_locus ());
+  }
+
+  void visit (AST::RangeFromExpr &expr) override
+  {
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   UNKNOWN_LOCAL_DEFID);
+
+    HIR::Expr *range_from
+      = ASTLoweringExpr::translate (expr.get_from_expr ().get ());
+
+    translated
+      = new HIR::RangeFromExpr (mapping,
+				std::unique_ptr<HIR::Expr> (range_from),
+				expr.get_locus ());
+  }
+
+  void visit (AST::RangeToExpr &expr) override
+  {
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   UNKNOWN_LOCAL_DEFID);
+
+    HIR::Expr *range_to
+      = ASTLoweringExpr::translate (expr.get_to_expr ().get ());
+
+    translated
+      = new HIR::RangeToExpr (mapping, std::unique_ptr<HIR::Expr> (range_to),
+			      expr.get_locus ());
+  }
+
+  void visit (AST::RangeFullExpr &expr) override
+  {
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   UNKNOWN_LOCAL_DEFID);
+
+    translated = new HIR::RangeFullExpr (mapping, expr.get_locus ());
+  }
+
+  void visit (AST::RangeFromToInclExpr &expr) override
+  {
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   UNKNOWN_LOCAL_DEFID);
+
+    HIR::Expr *range_from
+      = ASTLoweringExpr::translate (expr.get_from_expr ().get ());
+    HIR::Expr *range_to
+      = ASTLoweringExpr::translate (expr.get_to_expr ().get ());
+
+    translated
+      = new HIR::RangeFromToInclExpr (mapping,
+				      std::unique_ptr<HIR::Expr> (range_from),
+				      std::unique_ptr<HIR::Expr> (range_to),
+				      expr.get_locus ());
+  }
+
+private:
+  ASTLoweringExpr ()
+    : ASTLoweringBase (), translated (nullptr),
+      translated_array_elems (nullptr), terminated (false)
+  {}
+
+  HIR::Expr *translated;
+  HIR::ArrayElems *translated_array_elems;
+  bool terminated;
+};
+
+} // namespace HIR
+} // namespace Rust
+
+#endif // RUST_AST_LOWER_EXPR
diff --git a/gcc/rust/hir/rust-ast-lower-extern.h b/gcc/rust/hir/rust-ast-lower-extern.h
new file mode 100644
index 00000000000..eeb59c9c5d6
--- /dev/null
+++ b/gcc/rust/hir/rust-ast-lower-extern.h
@@ -0,0 +1,121 @@
+// 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_AST_LOWER_EXTERN_ITEM
+#define RUST_AST_LOWER_EXTERN_ITEM
+
+#include "rust-ast-lower-base.h"
+#include "rust-ast-lower-type.h"
+#include "rust-ast-lower.h"
+
+namespace Rust {
+namespace HIR {
+
+class ASTLoweringExternItem : public ASTLoweringBase
+{
+  using Rust::HIR::ASTLoweringBase::visit;
+
+public:
+  static HIR::ExternalItem *translate (AST::ExternalItem *item,
+				       HirId parent_hirid)
+  {
+    ASTLoweringExternItem resolver;
+    item->accept_vis (resolver);
+
+    rust_assert (resolver.translated != nullptr);
+    resolver.mappings->insert_hir_extern_item (resolver.translated,
+					       parent_hirid);
+    resolver.mappings->insert_location (
+      resolver.translated->get_mappings ().get_hirid (),
+      resolver.translated->get_locus ());
+
+    return resolver.translated;
+  }
+
+  void visit (AST::ExternalStaticItem &item) override
+  {
+    HIR::Visibility vis = translate_visibility (item.get_visibility ());
+    HIR::Type *static_type
+      = ASTLoweringType::translate (item.get_type ().get ());
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, item.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   mappings->get_next_localdef_id (crate_num));
+
+    translated = new HIR::ExternalStaticItem (
+      mapping, item.get_identifier (), std::unique_ptr<HIR::Type> (static_type),
+      item.is_mut () ? Mutability::Mut : Mutability::Imm, std::move (vis),
+      item.get_outer_attrs (), item.get_locus ());
+  }
+
+  void visit (AST::ExternalFunctionItem &function) override
+  {
+    std::vector<std::unique_ptr<HIR::WhereClauseItem> > where_clause_items;
+    HIR::WhereClause where_clause (std::move (where_clause_items));
+    HIR::Visibility vis = translate_visibility (function.get_visibility ());
+
+    std::vector<std::unique_ptr<HIR::GenericParam> > generic_params;
+    if (function.has_generics ())
+      generic_params = lower_generic_params (function.get_generic_params ());
+
+    HIR::Type *return_type
+      = function.has_return_type ()
+	  ? ASTLoweringType::translate (function.get_return_type ().get ())
+	  : nullptr;
+
+    std::vector<HIR::NamedFunctionParam> function_params;
+    for (auto &param : function.get_function_params ())
+      {
+	HIR::Type *param_type
+	  = ASTLoweringType::translate (param.get_type ().get ());
+	Identifier param_name = param.get_name ();
+
+	auto crate_num = mappings->get_current_crate ();
+	Analysis::NodeMapping mapping (crate_num, param.get_node_id (),
+				       mappings->get_next_hir_id (crate_num),
+				       mappings->get_next_localdef_id (
+					 crate_num));
+
+	function_params.push_back (
+	  HIR::NamedFunctionParam (mapping, param_name,
+				   std::unique_ptr<HIR::Type> (param_type)));
+      }
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, function.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   mappings->get_next_localdef_id (crate_num));
+
+    translated = new HIR::ExternalFunctionItem (
+      mapping, function.get_identifier (), std::move (generic_params),
+      std::unique_ptr<HIR::Type> (return_type), std::move (where_clause),
+      std::move (function_params), function.is_variadic (), std::move (vis),
+      function.get_outer_attrs (), function.get_locus ());
+  }
+
+private:
+  ASTLoweringExternItem () : translated (nullptr) {}
+
+  HIR::ExternalItem *translated;
+};
+
+} // namespace HIR
+} // namespace Rust
+
+#endif // RUST_AST_LOWER_ITEM
diff --git a/gcc/rust/hir/rust-ast-lower-implitem.h b/gcc/rust/hir/rust-ast-lower-implitem.h
new file mode 100644
index 00000000000..d5ca47587fc
--- /dev/null
+++ b/gcc/rust/hir/rust-ast-lower-implitem.h
@@ -0,0 +1,521 @@
+// 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_AST_LOWER_IMPLITEM_H
+#define RUST_AST_LOWER_IMPLITEM_H
+
+#include "rust-diagnostics.h"
+#include "rust-ast-lower-type.h"
+#include "rust-ast-lower-stmt.h"
+#include "rust-ast-lower-expr.h"
+#include "rust-ast-lower-pattern.h"
+#include "rust-ast-lower-block.h"
+
+namespace Rust {
+namespace HIR {
+
+class ASTLowerImplItem : public ASTLoweringBase
+{
+  using Rust::HIR::ASTLoweringBase::visit;
+
+public:
+  static HIR::ImplItem *translate (AST::InherentImplItem *item,
+				   HirId parent_impl_id)
+  {
+    ASTLowerImplItem resolver;
+    item->accept_vis (resolver);
+
+    if (resolver.translated != nullptr)
+      {
+	rust_assert (resolver.item_cast != nullptr);
+
+	auto id = resolver.translated->get_impl_mappings ().get_hirid ();
+	auto defid = resolver.translated->get_impl_mappings ().get_defid ();
+	auto locus = resolver.translated->get_locus ();
+
+	resolver.handle_outer_attributes (*resolver.item_cast);
+	resolver.mappings->insert_hir_implitem (parent_impl_id,
+						resolver.translated);
+	resolver.mappings->insert_location (id, locus);
+	resolver.mappings->insert_defid_mapping (defid, resolver.item_cast);
+      }
+
+    return resolver.translated;
+  }
+
+  static HIR::ImplItem *translate (AST::TraitImplItem *item,
+				   HirId parent_impl_id)
+  {
+    ASTLowerImplItem resolver;
+    item->accept_vis (resolver);
+
+    if (resolver.translated != nullptr)
+      {
+	rust_assert (resolver.item_cast != nullptr);
+
+	auto id = resolver.translated->get_impl_mappings ().get_hirid ();
+	auto defid = resolver.translated->get_impl_mappings ().get_defid ();
+	auto locus = resolver.translated->get_locus ();
+
+	resolver.handle_outer_attributes (*resolver.item_cast);
+	resolver.mappings->insert_hir_implitem (parent_impl_id,
+						resolver.translated);
+	resolver.mappings->insert_location (id, locus);
+	resolver.mappings->insert_defid_mapping (defid, resolver.item_cast);
+      }
+
+    return resolver.translated;
+  }
+
+  void visit (AST::TypeAlias &alias) override
+  {
+    std::vector<std::unique_ptr<HIR::WhereClauseItem> > where_clause_items;
+    HIR::WhereClause where_clause (std::move (where_clause_items));
+    HIR::Visibility vis = translate_visibility (alias.get_visibility ());
+
+    std::vector<std::unique_ptr<HIR::GenericParam> > generic_params;
+    if (alias.has_generics ())
+      generic_params = lower_generic_params (alias.get_generic_params ());
+
+    HIR::Type *existing_type
+      = ASTLoweringType::translate (alias.get_type_aliased ().get ());
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, alias.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   mappings->get_next_localdef_id (crate_num));
+
+    auto type_alias = new HIR::TypeAlias (
+      mapping, alias.get_new_type_name (), std::move (generic_params),
+      std::move (where_clause), std::unique_ptr<HIR::Type> (existing_type),
+      std::move (vis), alias.get_outer_attrs (), alias.get_locus ());
+
+    translated = type_alias;
+    item_cast = type_alias;
+  }
+
+  void visit (AST::ConstantItem &constant) override
+  {
+    HIR::Visibility vis = translate_visibility (constant.get_visibility ());
+
+    HIR::Type *type = ASTLoweringType::translate (constant.get_type ().get ());
+    HIR::Expr *expr = ASTLoweringExpr::translate (constant.get_expr ().get ());
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, constant.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   mappings->get_next_localdef_id (crate_num));
+
+    auto translated_constant
+      = new HIR::ConstantItem (mapping, constant.get_identifier (), vis,
+			       std::unique_ptr<HIR::Type> (type),
+			       std::unique_ptr<HIR::Expr> (expr),
+			       constant.get_outer_attrs (),
+			       constant.get_locus ());
+    translated = translated_constant;
+    item_cast = translated_constant;
+  }
+
+  void visit (AST::Function &function) override
+  {
+    // ignore for now and leave empty
+    std::vector<std::unique_ptr<HIR::WhereClauseItem> > where_clause_items;
+    HIR::WhereClause where_clause (std::move (where_clause_items));
+    HIR::FunctionQualifiers qualifiers
+      = lower_qualifiers (function.get_qualifiers ());
+    HIR::Visibility vis = translate_visibility (function.get_visibility ());
+
+    // need
+    std::vector<std::unique_ptr<HIR::GenericParam> > generic_params;
+    if (function.has_generics ())
+      {
+	generic_params = lower_generic_params (function.get_generic_params ());
+      }
+    Identifier function_name = function.get_function_name ();
+    Location locus = function.get_locus ();
+
+    std::unique_ptr<HIR::Type> return_type
+      = function.has_return_type () ? std::unique_ptr<HIR::Type> (
+	  ASTLoweringType::translate (function.get_return_type ().get ()))
+				    : nullptr;
+
+    std::vector<HIR::FunctionParam> function_params;
+    for (auto &param : function.get_function_params ())
+      {
+	auto translated_pattern = std::unique_ptr<HIR::Pattern> (
+	  ASTLoweringPattern::translate (param.get_pattern ().get ()));
+	auto translated_type = std::unique_ptr<HIR::Type> (
+	  ASTLoweringType::translate (param.get_type ().get ()));
+
+	auto crate_num = mappings->get_current_crate ();
+	Analysis::NodeMapping mapping (crate_num, param.get_node_id (),
+				       mappings->get_next_hir_id (crate_num),
+				       UNKNOWN_LOCAL_DEFID);
+
+	auto hir_param
+	  = HIR::FunctionParam (mapping, std::move (translated_pattern),
+				std::move (translated_type),
+				param.get_locus ());
+	function_params.push_back (std::move (hir_param));
+      }
+
+    bool terminated = false;
+    std::unique_ptr<HIR::BlockExpr> function_body
+      = std::unique_ptr<HIR::BlockExpr> (
+	ASTLoweringBlock::translate (function.get_definition ().get (),
+				     &terminated));
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, function.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   mappings->get_next_localdef_id (crate_num));
+
+    mappings->insert_location (function_body->get_mappings ().get_hirid (),
+			       function.get_locus ());
+
+    auto fn
+      = new HIR::Function (mapping, std::move (function_name),
+			   std::move (qualifiers), std::move (generic_params),
+			   std::move (function_params), std::move (return_type),
+			   std::move (where_clause), std::move (function_body),
+			   std::move (vis), function.get_outer_attrs (),
+			   HIR::SelfParam::error (), locus);
+
+    // add the mappings for the function params at the end
+    for (auto &param : fn->get_function_params ())
+      {
+	mappings->insert_hir_param (&param);
+	mappings->insert_location (mapping.get_hirid (), param.get_locus ());
+      }
+
+    translated = fn;
+    item_cast = fn;
+  }
+
+  void visit (AST::Method &method) override
+  {
+    // ignore for now and leave empty
+    std::vector<std::unique_ptr<HIR::WhereClauseItem> > where_clause_items;
+    HIR::WhereClause where_clause (std::move (where_clause_items));
+    HIR::FunctionQualifiers qualifiers
+      = lower_qualifiers (method.get_qualifiers ());
+    HIR::Visibility vis = translate_visibility (method.get_visibility ());
+
+    // need
+    std::vector<std::unique_ptr<HIR::GenericParam> > generic_params;
+    if (method.has_generics ())
+      {
+	generic_params = lower_generic_params (method.get_generic_params ());
+      }
+    Identifier method_name = method.get_method_name ();
+    Location locus = method.get_locus ();
+
+    HIR::SelfParam self_param = lower_self (method.get_self_param ());
+
+    std::unique_ptr<HIR::Type> return_type
+      = method.has_return_type () ? std::unique_ptr<HIR::Type> (
+	  ASTLoweringType::translate (method.get_return_type ().get ()))
+				  : nullptr;
+
+    std::vector<HIR::FunctionParam> function_params;
+    for (auto &param : method.get_function_params ())
+      {
+	auto translated_pattern = std::unique_ptr<HIR::Pattern> (
+	  ASTLoweringPattern::translate (param.get_pattern ().get ()));
+	auto translated_type = std::unique_ptr<HIR::Type> (
+	  ASTLoweringType::translate (param.get_type ().get ()));
+
+	auto crate_num = mappings->get_current_crate ();
+	Analysis::NodeMapping mapping (crate_num, param.get_node_id (),
+				       mappings->get_next_hir_id (crate_num),
+				       UNKNOWN_LOCAL_DEFID);
+
+	auto hir_param
+	  = HIR::FunctionParam (mapping, std::move (translated_pattern),
+				std::move (translated_type),
+				param.get_locus ());
+	function_params.push_back (std::move (hir_param));
+      }
+
+    bool terminated = false;
+    std::unique_ptr<HIR::BlockExpr> method_body
+      = std::unique_ptr<HIR::BlockExpr> (
+	ASTLoweringBlock::translate (method.get_definition ().get (),
+				     &terminated));
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, method.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   mappings->get_next_localdef_id (crate_num));
+    auto mth
+      = new HIR::Function (mapping, std::move (method_name),
+			   std::move (qualifiers), std::move (generic_params),
+			   std::move (function_params), std::move (return_type),
+			   std::move (where_clause), std::move (method_body),
+			   std::move (vis), method.get_outer_attrs (),
+			   std::move (self_param), locus);
+
+    // insert mappings for self
+    mappings->insert_hir_self_param (&self_param);
+    mappings->insert_location (self_param.get_mappings ().get_hirid (),
+			       self_param.get_locus ());
+
+    // add the mappings for the function params at the end
+    for (auto &param : mth->get_function_params ())
+      {
+	mappings->insert_hir_param (&param);
+	mappings->insert_location (mapping.get_hirid (), param.get_locus ());
+      }
+
+    translated = mth;
+    item_cast = mth;
+  }
+
+private:
+  ASTLowerImplItem () : translated (nullptr), item_cast (nullptr) {}
+
+  HIR::ImplItem *translated;
+  HIR::Item *item_cast;
+};
+
+class ASTLowerTraitItem : public ASTLoweringBase
+{
+  using Rust::HIR::ASTLoweringBase::visit;
+
+public:
+  static HIR::TraitItem *translate (AST::TraitItem *item)
+  {
+    ASTLowerTraitItem resolver;
+    item->accept_vis (resolver);
+
+    if (resolver.translated != nullptr)
+      {
+	// FIXME
+
+	// auto id = resolver.translated->get_mappings ().get_hirid ();
+	// auto defid = resolver.translated->get_mappings ().get_defid ();
+	// auto locus = resolver.translated->get_locus ();
+
+	// resolver.handle_outer_attributes (*resolver.translated);
+	resolver.mappings->insert_hir_trait_item (resolver.translated);
+	// resolver.mappings->insert_location (id, locus);
+	// resolver.mappings->insert_defid_mapping (defid, resolver.item_cast);
+      }
+
+    return resolver.translated;
+  }
+
+  void visit (AST::TraitItemFunc &func) override
+  {
+    AST::TraitFunctionDecl &ref = func.get_trait_function_decl ();
+
+    std::vector<std::unique_ptr<HIR::WhereClauseItem> > where_clause_items;
+    HIR::WhereClause where_clause (std::move (where_clause_items));
+    HIR::FunctionQualifiers qualifiers
+      = lower_qualifiers (func.get_trait_function_decl ().get_qualifiers ());
+
+    std::vector<std::unique_ptr<HIR::GenericParam> > generic_params;
+    if (ref.has_generics ())
+      {
+	generic_params = lower_generic_params (ref.get_generic_params ());
+      }
+
+    std::unique_ptr<HIR::Type> return_type
+      = ref.has_return_type () ? std::unique_ptr<HIR::Type> (
+	  ASTLoweringType::translate (ref.get_return_type ().get ()))
+			       : nullptr;
+
+    std::vector<HIR::FunctionParam> function_params;
+    for (auto &param : ref.get_function_params ())
+      {
+	auto translated_pattern = std::unique_ptr<HIR::Pattern> (
+	  ASTLoweringPattern::translate (param.get_pattern ().get ()));
+	auto translated_type = std::unique_ptr<HIR::Type> (
+	  ASTLoweringType::translate (param.get_type ().get ()));
+
+	auto crate_num = mappings->get_current_crate ();
+	Analysis::NodeMapping mapping (crate_num, param.get_node_id (),
+				       mappings->get_next_hir_id (crate_num),
+				       UNKNOWN_LOCAL_DEFID);
+
+	auto hir_param
+	  = HIR::FunctionParam (mapping, std::move (translated_pattern),
+				std::move (translated_type),
+				param.get_locus ());
+	function_params.push_back (std::move (hir_param));
+      }
+
+    HIR::TraitFunctionDecl decl (ref.get_identifier (), std::move (qualifiers),
+				 std::move (generic_params),
+				 HIR::SelfParam::error (),
+				 std::move (function_params),
+				 std::move (return_type),
+				 std::move (where_clause));
+    bool terminated = false;
+    std::unique_ptr<HIR::BlockExpr> block_expr
+      = func.has_definition () ? std::unique_ptr<HIR::BlockExpr> (
+	  ASTLoweringBlock::translate (func.get_definition ().get (),
+				       &terminated))
+			       : nullptr;
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, func.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   mappings->get_next_localdef_id (crate_num));
+
+    HIR::TraitItemFunc *trait_item
+      = new HIR::TraitItemFunc (mapping, std::move (decl),
+				std::move (block_expr), func.get_outer_attrs (),
+				func.get_locus ());
+    translated = trait_item;
+
+    // add the mappings for the function params at the end
+    for (auto &param : trait_item->get_decl ().get_function_params ())
+      {
+	mappings->insert_hir_param (&param);
+	mappings->insert_location (mapping.get_hirid (), param.get_locus ());
+      }
+  }
+
+  void visit (AST::TraitItemMethod &method) override
+  {
+    AST::TraitMethodDecl &ref = method.get_trait_method_decl ();
+
+    std::vector<std::unique_ptr<HIR::WhereClauseItem> > where_clause_items;
+    HIR::WhereClause where_clause (std::move (where_clause_items));
+    HIR::FunctionQualifiers qualifiers
+      = lower_qualifiers (method.get_trait_method_decl ().get_qualifiers ());
+
+    std::vector<std::unique_ptr<HIR::GenericParam> > generic_params;
+    if (ref.has_generics ())
+      {
+	generic_params = lower_generic_params (ref.get_generic_params ());
+      }
+
+    std::unique_ptr<HIR::Type> return_type
+      = ref.has_return_type () ? std::unique_ptr<HIR::Type> (
+	  ASTLoweringType::translate (ref.get_return_type ().get ()))
+			       : nullptr;
+
+    HIR::SelfParam self_param = lower_self (ref.get_self_param ());
+
+    std::vector<HIR::FunctionParam> function_params;
+    for (auto &param : ref.get_function_params ())
+      {
+	auto translated_pattern = std::unique_ptr<HIR::Pattern> (
+	  ASTLoweringPattern::translate (param.get_pattern ().get ()));
+	auto translated_type = std::unique_ptr<HIR::Type> (
+	  ASTLoweringType::translate (param.get_type ().get ()));
+
+	auto crate_num = mappings->get_current_crate ();
+	Analysis::NodeMapping mapping (crate_num, param.get_node_id (),
+				       mappings->get_next_hir_id (crate_num),
+				       UNKNOWN_LOCAL_DEFID);
+
+	auto hir_param
+	  = HIR::FunctionParam (mapping, std::move (translated_pattern),
+				std::move (translated_type),
+				param.get_locus ());
+	function_params.push_back (hir_param);
+      }
+
+    HIR::TraitFunctionDecl decl (ref.get_identifier (), std::move (qualifiers),
+				 std::move (generic_params),
+				 std::move (self_param),
+				 std::move (function_params),
+				 std::move (return_type),
+				 std::move (where_clause));
+    bool terminated = false;
+    std::unique_ptr<HIR::BlockExpr> block_expr
+      = method.has_definition () ? std::unique_ptr<HIR::BlockExpr> (
+	  ASTLoweringBlock::translate (method.get_definition ().get (),
+				       &terminated))
+				 : nullptr;
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, method.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   mappings->get_next_localdef_id (crate_num));
+
+    HIR::TraitItemFunc *trait_item
+      = new HIR::TraitItemFunc (mapping, std::move (decl),
+				std::move (block_expr),
+				method.get_outer_attrs (), method.get_locus ());
+    translated = trait_item;
+
+    // insert mappings for self
+    mappings->insert_hir_self_param (&self_param);
+    mappings->insert_location (self_param.get_mappings ().get_hirid (),
+			       self_param.get_locus ());
+
+    // add the mappings for the function params at the end
+    for (auto &param : trait_item->get_decl ().get_function_params ())
+      {
+	mappings->insert_hir_param (&param);
+	mappings->insert_location (mapping.get_hirid (), param.get_locus ());
+      }
+  }
+
+  void visit (AST::TraitItemConst &constant) override
+  {
+    HIR::Type *type = ASTLoweringType::translate (constant.get_type ().get ());
+    HIR::Expr *expr
+      = constant.has_expression ()
+	  ? ASTLoweringExpr::translate (constant.get_expr ().get ())
+	  : nullptr;
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, constant.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   mappings->get_next_localdef_id (crate_num));
+
+    HIR::TraitItemConst *trait_item
+      = new HIR::TraitItemConst (mapping, constant.get_identifier (),
+				 std::unique_ptr<HIR::Type> (type),
+				 std::unique_ptr<HIR::Expr> (expr),
+				 constant.get_outer_attrs (),
+				 constant.get_locus ());
+    translated = trait_item;
+  }
+
+  void visit (AST::TraitItemType &type) override
+  {
+    std::vector<std::unique_ptr<HIR::TypeParamBound> > type_param_bounds;
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, type.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   mappings->get_next_localdef_id (crate_num));
+
+    HIR::TraitItemType *trait_item
+      = new HIR::TraitItemType (mapping, type.get_identifier (),
+				std::move (type_param_bounds),
+				type.get_outer_attrs (), type.get_locus ());
+    translated = trait_item;
+  }
+
+private:
+  ASTLowerTraitItem () : translated (nullptr) {}
+
+  HIR::TraitItem *translated;
+};
+
+} // namespace HIR
+} // namespace Rust
+
+#endif // RUST_AST_LOWER_IMPLITEM_H
diff --git a/gcc/rust/hir/rust-ast-lower-item.cc b/gcc/rust/hir/rust-ast-lower-item.cc
new file mode 100644
index 00000000000..fefc938b8e5
--- /dev/null
+++ b/gcc/rust/hir/rust-ast-lower-item.cc
@@ -0,0 +1,741 @@
+// 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/>.
+
+#include "rust-ast-lower-item.h"
+
+namespace Rust {
+namespace HIR {
+
+HIR::Item *
+ASTLoweringItem::translate (AST::Item *item)
+{
+  ASTLoweringItem resolver;
+  item->accept_vis (resolver);
+
+  if (resolver.translated != nullptr)
+    {
+      auto id = resolver.translated->get_mappings ().get_hirid ();
+      auto defid = resolver.translated->get_mappings ().get_defid ();
+      auto locus = resolver.translated->get_locus ();
+
+      resolver.handle_outer_attributes (*resolver.translated);
+      resolver.mappings->insert_ast_item (item);
+      resolver.mappings->insert_hir_item (resolver.translated);
+      resolver.mappings->insert_location (id, locus);
+      resolver.mappings->insert_defid_mapping (defid, resolver.translated);
+    }
+
+  return resolver.translated;
+}
+
+void
+ASTLoweringItem::visit (AST::Module &module)
+{
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, module.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 mappings->get_next_localdef_id (crate_num));
+
+  // should be lowered from module.get_vis()
+  HIR::Visibility vis = translate_visibility (module.get_visibility ());
+
+  auto items = std::vector<std::unique_ptr<Item>> ();
+
+  for (auto &item : module.get_items ())
+    {
+      auto transitem = translate (item.get ());
+      items.push_back (std::unique_ptr<Item> (transitem));
+    }
+
+  // should be lowered/copied from module.get_in/outer_attrs()
+  AST::AttrVec inner_attrs = module.get_inner_attrs ();
+  AST::AttrVec outer_attrs = module.get_outer_attrs ();
+
+  translated
+    = new HIR::Module (mapping, module.get_name (), module.get_locus (),
+		       std::move (items), std::move (vis),
+		       std::move (inner_attrs), std::move (outer_attrs));
+  mappings->insert_module (static_cast<Module *> (translated));
+}
+
+void
+ASTLoweringItem::visit (AST::TypeAlias &alias)
+{
+  std::vector<std::unique_ptr<HIR::WhereClauseItem>> where_clause_items;
+  for (auto &item : alias.get_where_clause ().get_items ())
+    {
+      HIR::WhereClauseItem *i
+	= ASTLowerWhereClauseItem::translate (*item.get ());
+      where_clause_items.push_back (std::unique_ptr<HIR::WhereClauseItem> (i));
+    }
+
+  HIR::WhereClause where_clause (std::move (where_clause_items));
+  HIR::Visibility vis = translate_visibility (alias.get_visibility ());
+
+  std::vector<std::unique_ptr<HIR::GenericParam>> generic_params;
+  if (alias.has_generics ())
+    generic_params = lower_generic_params (alias.get_generic_params ());
+
+  HIR::Type *existing_type
+    = ASTLoweringType::translate (alias.get_type_aliased ().get ());
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, alias.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 mappings->get_next_localdef_id (crate_num));
+
+  translated
+    = new HIR::TypeAlias (mapping, alias.get_new_type_name (),
+			  std::move (generic_params), std::move (where_clause),
+			  std::unique_ptr<HIR::Type> (existing_type),
+			  std::move (vis), alias.get_outer_attrs (),
+			  alias.get_locus ());
+}
+
+void
+ASTLoweringItem::visit (AST::TupleStruct &struct_decl)
+{
+  std::vector<std::unique_ptr<HIR::GenericParam>> generic_params;
+  if (struct_decl.has_generics ())
+    {
+      generic_params = lower_generic_params (struct_decl.get_generic_params ());
+    }
+
+  std::vector<std::unique_ptr<HIR::WhereClauseItem>> where_clause_items;
+  for (auto &item : struct_decl.get_where_clause ().get_items ())
+    {
+      HIR::WhereClauseItem *i
+	= ASTLowerWhereClauseItem::translate (*item.get ());
+      where_clause_items.push_back (std::unique_ptr<HIR::WhereClauseItem> (i));
+    }
+
+  HIR::WhereClause where_clause (std::move (where_clause_items));
+  HIR::Visibility vis = translate_visibility (struct_decl.get_visibility ());
+
+  std::vector<HIR::TupleField> fields;
+  for (AST::TupleField &field : struct_decl.get_fields ())
+    {
+      if (field.get_field_type ()->is_marked_for_strip ())
+	continue;
+
+      // FIXME: How do we get the visibility from here?
+      HIR::Visibility vis = translate_visibility (field.get_visibility ());
+      HIR::Type *type
+	= ASTLoweringType::translate (field.get_field_type ().get ());
+
+      auto crate_num = mappings->get_current_crate ();
+      Analysis::NodeMapping mapping (crate_num, field.get_node_id (),
+				     mappings->get_next_hir_id (crate_num),
+				     mappings->get_next_localdef_id (
+				       crate_num));
+
+      HIR::TupleField translated_field (mapping,
+					std::unique_ptr<HIR::Type> (type), vis,
+					field.get_locus (),
+					field.get_outer_attrs ());
+      fields.push_back (std::move (translated_field));
+    }
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, struct_decl.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 mappings->get_next_localdef_id (crate_num));
+
+  translated = new HIR::TupleStruct (mapping, std::move (fields),
+				     struct_decl.get_identifier (),
+				     std::move (generic_params),
+				     std::move (where_clause), vis,
+				     struct_decl.get_outer_attrs (),
+				     struct_decl.get_locus ());
+}
+
+void
+ASTLoweringItem::visit (AST::StructStruct &struct_decl)
+{
+  std::vector<std::unique_ptr<HIR::GenericParam>> generic_params;
+  if (struct_decl.has_generics ())
+    {
+      generic_params = lower_generic_params (struct_decl.get_generic_params ());
+    }
+
+  std::vector<std::unique_ptr<HIR::WhereClauseItem>> where_clause_items;
+  for (auto &item : struct_decl.get_where_clause ().get_items ())
+    {
+      HIR::WhereClauseItem *i
+	= ASTLowerWhereClauseItem::translate (*item.get ());
+      where_clause_items.push_back (std::unique_ptr<HIR::WhereClauseItem> (i));
+    }
+
+  HIR::WhereClause where_clause (std::move (where_clause_items));
+
+  HIR::Visibility vis = translate_visibility (struct_decl.get_visibility ());
+
+  bool is_unit = struct_decl.is_unit_struct ();
+  std::vector<HIR::StructField> fields;
+  for (AST::StructField &field : struct_decl.get_fields ())
+    {
+      if (field.get_field_type ()->is_marked_for_strip ())
+	continue;
+
+      HIR::Visibility vis = translate_visibility (field.get_visibility ());
+      HIR::Type *type
+	= ASTLoweringType::translate (field.get_field_type ().get ());
+
+      auto crate_num = mappings->get_current_crate ();
+      Analysis::NodeMapping mapping (crate_num, field.get_node_id (),
+				     mappings->get_next_hir_id (crate_num),
+				     mappings->get_next_localdef_id (
+				       crate_num));
+
+      HIR::StructField translated_field (mapping, field.get_field_name (),
+					 std::unique_ptr<HIR::Type> (type), vis,
+					 field.get_locus (),
+					 field.get_outer_attrs ());
+
+      if (struct_field_name_exists (fields, translated_field))
+	break;
+
+      fields.push_back (std::move (translated_field));
+    }
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, struct_decl.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 mappings->get_next_localdef_id (crate_num));
+
+  translated = new HIR::StructStruct (mapping, std::move (fields),
+				      struct_decl.get_identifier (),
+				      std::move (generic_params),
+				      std::move (where_clause), is_unit, vis,
+				      struct_decl.get_outer_attrs (),
+				      struct_decl.get_locus ());
+}
+
+void
+ASTLoweringItem::visit (AST::Enum &enum_decl)
+{
+  std::vector<std::unique_ptr<HIR::GenericParam>> generic_params;
+  if (enum_decl.has_generics ())
+    {
+      generic_params = lower_generic_params (enum_decl.get_generic_params ());
+    }
+
+  std::vector<std::unique_ptr<HIR::WhereClauseItem>> where_clause_items;
+  for (auto &item : enum_decl.get_where_clause ().get_items ())
+    {
+      HIR::WhereClauseItem *i
+	= ASTLowerWhereClauseItem::translate (*item.get ());
+      where_clause_items.push_back (std::unique_ptr<HIR::WhereClauseItem> (i));
+    }
+
+  HIR::WhereClause where_clause (std::move (where_clause_items));
+  HIR::Visibility vis = translate_visibility (enum_decl.get_visibility ());
+
+  // bool is_unit = enum_decl.is_zero_variant ();
+  std::vector<std::unique_ptr<HIR::EnumItem>> items;
+  for (auto &variant : enum_decl.get_variants ())
+    {
+      if (variant->is_marked_for_strip ())
+	continue;
+
+      HIR::EnumItem *hir_item = ASTLoweringEnumItem::translate (variant.get ());
+      items.push_back (std::unique_ptr<HIR::EnumItem> (hir_item));
+    }
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, enum_decl.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 mappings->get_next_localdef_id (crate_num));
+
+  translated = new HIR::Enum (mapping, enum_decl.get_identifier (), vis,
+			      std::move (generic_params),
+			      std::move (where_clause), /* is_unit, */
+			      std::move (items), enum_decl.get_outer_attrs (),
+			      enum_decl.get_locus ());
+}
+
+void
+ASTLoweringItem::visit (AST::Union &union_decl)
+{
+  std::vector<std::unique_ptr<HIR::GenericParam>> generic_params;
+  if (union_decl.has_generics ())
+    {
+      generic_params = lower_generic_params (union_decl.get_generic_params ());
+    }
+
+  std::vector<std::unique_ptr<HIR::WhereClauseItem>> where_clause_items;
+  for (auto &item : union_decl.get_where_clause ().get_items ())
+    {
+      HIR::WhereClauseItem *i
+	= ASTLowerWhereClauseItem::translate (*item.get ());
+      where_clause_items.push_back (std::unique_ptr<HIR::WhereClauseItem> (i));
+    }
+  HIR::WhereClause where_clause (std::move (where_clause_items));
+  HIR::Visibility vis = translate_visibility (union_decl.get_visibility ());
+
+  std::vector<HIR::StructField> variants;
+  for (AST::StructField &variant : union_decl.get_variants ())
+    {
+      if (variant.get_field_type ()->is_marked_for_strip ())
+	continue;
+
+      // FIXME: Does visibility apply here?
+      HIR::Visibility vis = translate_visibility (variant.get_visibility ());
+      HIR::Type *type
+	= ASTLoweringType::translate (variant.get_field_type ().get ());
+
+      auto crate_num = mappings->get_current_crate ();
+      Analysis::NodeMapping mapping (crate_num, variant.get_node_id (),
+				     mappings->get_next_hir_id (crate_num),
+				     mappings->get_next_localdef_id (
+				       crate_num));
+
+      HIR::StructField translated_variant (mapping, variant.get_field_name (),
+					   std::unique_ptr<HIR::Type> (type),
+					   vis, variant.get_locus (),
+					   variant.get_outer_attrs ());
+
+      if (struct_field_name_exists (variants, translated_variant))
+	break;
+
+      variants.push_back (std::move (translated_variant));
+    }
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, union_decl.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 mappings->get_next_localdef_id (crate_num));
+
+  translated
+    = new HIR::Union (mapping, union_decl.get_identifier (), vis,
+		      std::move (generic_params), std::move (where_clause),
+		      std::move (variants), union_decl.get_outer_attrs (),
+		      union_decl.get_locus ());
+}
+
+void
+ASTLoweringItem::visit (AST::StaticItem &var)
+{
+  HIR::Visibility vis = translate_visibility (var.get_visibility ());
+
+  HIR::Type *type = ASTLoweringType::translate (var.get_type ().get ());
+  HIR::Expr *expr = ASTLoweringExpr::translate (var.get_expr ().get ());
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, var.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 mappings->get_next_localdef_id (crate_num));
+
+  translated = new HIR::StaticItem (mapping, var.get_identifier (),
+				    var.is_mutable () ? Mutability::Mut
+						      : Mutability::Imm,
+				    std::unique_ptr<HIR::Type> (type),
+				    std::unique_ptr<HIR::Expr> (expr), vis,
+				    var.get_outer_attrs (), var.get_locus ());
+}
+
+void
+ASTLoweringItem::visit (AST::ConstantItem &constant)
+{
+  HIR::Visibility vis = translate_visibility (constant.get_visibility ());
+
+  HIR::Type *type = ASTLoweringType::translate (constant.get_type ().get ());
+  HIR::Expr *expr = ASTLoweringExpr::translate (constant.get_expr ().get ());
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, constant.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 mappings->get_next_localdef_id (crate_num));
+
+  translated = new HIR::ConstantItem (mapping, constant.get_identifier (), vis,
+				      std::unique_ptr<HIR::Type> (type),
+				      std::unique_ptr<HIR::Expr> (expr),
+				      constant.get_outer_attrs (),
+				      constant.get_locus ());
+}
+
+void
+ASTLoweringItem::visit (AST::Function &function)
+{
+  if (function.is_marked_for_strip ())
+    return;
+
+  std::vector<std::unique_ptr<HIR::WhereClauseItem>> where_clause_items;
+  for (auto &item : function.get_where_clause ().get_items ())
+    {
+      HIR::WhereClauseItem *i
+	= ASTLowerWhereClauseItem::translate (*item.get ());
+      where_clause_items.push_back (std::unique_ptr<HIR::WhereClauseItem> (i));
+    }
+
+  HIR::WhereClause where_clause (std::move (where_clause_items));
+  HIR::FunctionQualifiers qualifiers
+    = lower_qualifiers (function.get_qualifiers ());
+  HIR::Visibility vis = translate_visibility (function.get_visibility ());
+
+  // need
+  std::vector<std::unique_ptr<HIR::GenericParam>> generic_params;
+  if (function.has_generics ())
+    {
+      generic_params = lower_generic_params (function.get_generic_params ());
+    }
+  Identifier function_name = function.get_function_name ();
+  Location locus = function.get_locus ();
+
+  std::unique_ptr<HIR::Type> return_type
+    = function.has_return_type () ? std::unique_ptr<HIR::Type> (
+	ASTLoweringType::translate (function.get_return_type ().get ()))
+				  : nullptr;
+
+  std::vector<HIR::FunctionParam> function_params;
+  for (auto &param : function.get_function_params ())
+    {
+      auto translated_pattern = std::unique_ptr<HIR::Pattern> (
+	ASTLoweringPattern::translate (param.get_pattern ().get ()));
+      auto translated_type = std::unique_ptr<HIR::Type> (
+	ASTLoweringType::translate (param.get_type ().get ()));
+
+      auto crate_num = mappings->get_current_crate ();
+      Analysis::NodeMapping mapping (crate_num, param.get_node_id (),
+				     mappings->get_next_hir_id (crate_num),
+				     UNKNOWN_LOCAL_DEFID);
+
+      auto hir_param
+	= HIR::FunctionParam (mapping, std::move (translated_pattern),
+			      std::move (translated_type), param.get_locus ());
+      function_params.push_back (std::move (hir_param));
+    }
+
+  bool terminated = false;
+  std::unique_ptr<HIR::BlockExpr> function_body
+    = std::unique_ptr<HIR::BlockExpr> (
+      ASTLoweringBlock::translate (function.get_definition ().get (),
+				   &terminated));
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, function.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 mappings->get_next_localdef_id (crate_num));
+
+  mappings->insert_location (function_body->get_mappings ().get_hirid (),
+			     function.get_locus ());
+
+  auto fn
+    = new HIR::Function (mapping, std::move (function_name),
+			 std::move (qualifiers), std::move (generic_params),
+			 std::move (function_params), std::move (return_type),
+			 std::move (where_clause), std::move (function_body),
+			 std::move (vis), function.get_outer_attrs (),
+			 HIR::SelfParam::error (), locus);
+
+  // add the mappings for the function params at the end
+  for (auto &param : fn->get_function_params ())
+    {
+      mappings->insert_hir_param (&param);
+      mappings->insert_location (mapping.get_hirid (), param.get_locus ());
+    }
+
+  translated = fn;
+}
+
+void
+ASTLoweringItem::visit (AST::InherentImpl &impl_block)
+{
+  std::vector<std::unique_ptr<HIR::WhereClauseItem>> where_clause_items;
+  for (auto &item : impl_block.get_where_clause ().get_items ())
+    {
+      HIR::WhereClauseItem *i
+	= ASTLowerWhereClauseItem::translate (*item.get ());
+      where_clause_items.push_back (std::unique_ptr<HIR::WhereClauseItem> (i));
+    }
+
+  HIR::WhereClause where_clause (std::move (where_clause_items));
+  HIR::Visibility vis = translate_visibility (impl_block.get_visibility ());
+
+  std::vector<std::unique_ptr<HIR::GenericParam>> generic_params;
+  if (impl_block.has_generics ())
+    {
+      generic_params = lower_generic_params (impl_block.get_generic_params ());
+
+      for (auto &generic_param : generic_params)
+	{
+	  switch (generic_param->get_kind ())
+	    {
+	      case HIR::GenericParam::GenericKind::TYPE: {
+		const HIR::TypeParam &t
+		  = static_cast<const HIR::TypeParam &> (*generic_param);
+
+		if (t.has_type ())
+		  {
+		    // see https://github.com/rust-lang/rust/issues/36887
+		    rust_error_at (
+		      t.get_locus (),
+		      "defaults for type parameters are not allowed here");
+		  }
+	      }
+	      break;
+
+	    default:
+	      break;
+	    }
+	}
+    }
+
+  HIR::Type *impl_type
+    = ASTLoweringType::translate (impl_block.get_type ().get ());
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, impl_block.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 mappings->get_next_localdef_id (crate_num));
+
+  std::vector<std::unique_ptr<HIR::ImplItem>> impl_items;
+  std::vector<HirId> impl_item_ids;
+  for (auto &impl_item : impl_block.get_impl_items ())
+    {
+      if (impl_item->is_marked_for_strip ())
+	continue;
+
+      HIR::ImplItem *lowered
+	= ASTLowerImplItem::translate (impl_item.get (), mapping.get_hirid ());
+      rust_assert (lowered != nullptr);
+      impl_items.push_back (std::unique_ptr<HIR::ImplItem> (lowered));
+      impl_item_ids.push_back (lowered->get_impl_mappings ().get_hirid ());
+    }
+
+  Polarity polarity = Positive;
+  HIR::ImplBlock *hir_impl_block = new HIR::ImplBlock (
+    mapping, std::move (impl_items), std::move (generic_params),
+    std::unique_ptr<HIR::Type> (impl_type), nullptr, where_clause, polarity,
+    vis, impl_block.get_inner_attrs (), impl_block.get_outer_attrs (),
+    impl_block.get_locus ());
+  translated = hir_impl_block;
+
+  mappings->insert_hir_impl_block (hir_impl_block);
+  for (auto &impl_item_id : impl_item_ids)
+    {
+      mappings->insert_impl_item_mapping (impl_item_id, hir_impl_block);
+    }
+}
+
+void
+ASTLoweringItem::visit (AST::Trait &trait)
+{
+  std::vector<std::unique_ptr<HIR::WhereClauseItem>> where_clause_items;
+  for (auto &item : trait.get_where_clause ().get_items ())
+    {
+      HIR::WhereClauseItem *i
+	= ASTLowerWhereClauseItem::translate (*item.get ());
+      where_clause_items.push_back (std::unique_ptr<HIR::WhereClauseItem> (i));
+    }
+  HIR::WhereClause where_clause (std::move (where_clause_items));
+
+  HIR::Visibility vis = translate_visibility (trait.get_visibility ());
+
+  std::vector<std::unique_ptr<HIR::GenericParam>> generic_params;
+  if (trait.has_generics ())
+    {
+      generic_params = lower_generic_params (trait.get_generic_params ());
+    }
+
+  std::vector<std::unique_ptr<HIR::TypeParamBound>> type_param_bounds;
+  if (trait.has_type_param_bounds ())
+    {
+      for (auto &bound : trait.get_type_param_bounds ())
+	{
+	  HIR::TypeParamBound *b = lower_bound (bound.get ());
+	  type_param_bounds.push_back (
+	    std::unique_ptr<HIR::TypeParamBound> (b));
+	}
+    }
+
+  std::vector<std::unique_ptr<HIR::TraitItem>> trait_items;
+  std::vector<HirId> trait_item_ids;
+  for (auto &item : trait.get_trait_items ())
+    {
+      if (item->is_marked_for_strip ())
+	continue;
+
+      HIR::TraitItem *lowered = ASTLowerTraitItem::translate (item.get ());
+      trait_items.push_back (std::unique_ptr<HIR::TraitItem> (lowered));
+      trait_item_ids.push_back (lowered->get_mappings ().get_hirid ());
+    }
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, trait.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 mappings->get_next_localdef_id (crate_num));
+
+  auto trait_unsafety = Unsafety::Normal;
+  if (trait.is_unsafe ())
+    {
+      trait_unsafety = Unsafety::Unsafe;
+    }
+
+  HIR::Trait *hir_trait
+    = new HIR::Trait (mapping, trait.get_identifier (), trait_unsafety,
+		      std::move (generic_params), std::move (type_param_bounds),
+		      where_clause, std::move (trait_items), vis,
+		      trait.get_outer_attrs (), trait.get_locus ());
+  translated = hir_trait;
+
+  for (auto trait_item_id : trait_item_ids)
+    {
+      mappings->insert_trait_item_mapping (trait_item_id, hir_trait);
+    }
+}
+
+void
+ASTLoweringItem::visit (AST::TraitImpl &impl_block)
+{
+  std::vector<std::unique_ptr<HIR::WhereClauseItem>> where_clause_items;
+  for (auto &item : impl_block.get_where_clause ().get_items ())
+    {
+      HIR::WhereClauseItem *i
+	= ASTLowerWhereClauseItem::translate (*item.get ());
+      where_clause_items.push_back (std::unique_ptr<HIR::WhereClauseItem> (i));
+    }
+  HIR::WhereClause where_clause (std::move (where_clause_items));
+  HIR::Visibility vis = translate_visibility (impl_block.get_visibility ());
+
+  std::vector<std::unique_ptr<HIR::GenericParam>> generic_params;
+  if (impl_block.has_generics ())
+    {
+      generic_params = lower_generic_params (impl_block.get_generic_params ());
+
+      for (auto &generic_param : generic_params)
+	{
+	  switch (generic_param->get_kind ())
+	    {
+	      case HIR::GenericParam::GenericKind::TYPE: {
+		const HIR::TypeParam &t
+		  = static_cast<const HIR::TypeParam &> (*generic_param);
+
+		if (t.has_type ())
+		  {
+		    // see https://github.com/rust-lang/rust/issues/36887
+		    rust_error_at (
+		      t.get_locus (),
+		      "defaults for type parameters are not allowed here");
+		  }
+	      }
+	      break;
+
+	    default:
+	      break;
+	    }
+	}
+    }
+
+  HIR::Type *impl_type
+    = ASTLoweringType::translate (impl_block.get_type ().get ());
+  HIR::TypePath *trait_ref
+    = ASTLowerTypePath::translate (impl_block.get_trait_path ());
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, impl_block.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 mappings->get_next_localdef_id (crate_num));
+
+  std::vector<std::unique_ptr<HIR::ImplItem>> impl_items;
+  std::vector<HirId> impl_item_ids;
+  for (auto &impl_item : impl_block.get_impl_items ())
+    {
+      if (impl_item->is_marked_for_strip ())
+	continue;
+
+      HIR::ImplItem *lowered
+	= ASTLowerImplItem::translate (impl_item.get (), mapping.get_hirid ());
+      rust_assert (lowered != nullptr);
+      impl_items.push_back (std::unique_ptr<HIR::ImplItem> (lowered));
+      impl_item_ids.push_back (lowered->get_impl_mappings ().get_hirid ());
+    }
+
+  Polarity polarity = impl_block.is_exclam () ? Positive : Negative;
+  HIR::ImplBlock *hir_impl_block = new HIR::ImplBlock (
+    mapping, std::move (impl_items), std::move (generic_params),
+    std::unique_ptr<HIR::Type> (impl_type),
+    std::unique_ptr<HIR::TypePath> (trait_ref), where_clause, polarity, vis,
+    impl_block.get_inner_attrs (), impl_block.get_outer_attrs (),
+    impl_block.get_locus ());
+  translated = hir_impl_block;
+
+  mappings->insert_hir_impl_block (hir_impl_block);
+  for (auto &impl_item_id : impl_item_ids)
+    {
+      mappings->insert_impl_item_mapping (impl_item_id, hir_impl_block);
+    }
+}
+
+void
+ASTLoweringItem::visit (AST::ExternBlock &extern_block)
+{
+  translated = lower_extern_block (extern_block);
+}
+
+HIR::SimplePath
+ASTLoweringSimplePath::translate (const AST::SimplePath &path)
+{
+  ASTLoweringSimplePath resolver;
+
+  return resolver.lower (path);
+}
+
+HIR::SimplePathSegment
+ASTLoweringSimplePath::lower (const AST::SimplePathSegment &segment)
+{
+  auto crate_num = mappings->get_current_crate ();
+  auto node_id = segment.get_node_id ();
+
+  auto mapping = Analysis::NodeMapping (crate_num, node_id,
+					mappings->get_next_hir_id (crate_num),
+					UNKNOWN_LOCAL_DEFID);
+
+  auto hir_seg = HIR::SimplePathSegment (mapping);
+
+  mappings->insert_node_to_hir (node_id, mapping.get_hirid ());
+  // mappings->insert_simple_path_segment (crate_num, node_id, &segment);
+
+  return hir_seg;
+}
+
+HIR::SimplePath
+ASTLoweringSimplePath::lower (const AST::SimplePath &path)
+{
+  auto segments = std::vector<HIR::SimplePathSegment> ();
+  for (auto &segment : path.get_segments ())
+    segments.emplace_back (lower (segment));
+
+  auto crate_num = mappings->get_current_crate ();
+  auto node_id = path.get_node_id ();
+
+  auto mapping = Analysis::NodeMapping (crate_num, node_id,
+					mappings->get_next_hir_id (crate_num),
+					UNKNOWN_LOCAL_DEFID);
+
+  auto lowered
+    = HIR::SimplePath (std::move (segments), mapping, path.get_locus ());
+
+  mappings->insert_node_to_hir (node_id, mapping.get_hirid ());
+  // mappings->insert_simple_path (crate_num, node_id, &path);
+
+  return lowered;
+}
+
+} // namespace HIR
+} // namespace Rust
diff --git a/gcc/rust/hir/rust-ast-lower-item.h b/gcc/rust/hir/rust-ast-lower-item.h
new file mode 100644
index 00000000000..5d4ee18f517
--- /dev/null
+++ b/gcc/rust/hir/rust-ast-lower-item.h
@@ -0,0 +1,78 @@
+// 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_AST_LOWER_ITEM
+#define RUST_AST_LOWER_ITEM
+
+#include "rust-diagnostics.h"
+
+#include "rust-ast-lower.h"
+#include "rust-ast-lower-base.h"
+#include "rust-ast-lower-enumitem.h"
+#include "rust-ast-lower-type.h"
+#include "rust-ast-lower-implitem.h"
+#include "rust-ast-lower-stmt.h"
+#include "rust-ast-lower-expr.h"
+#include "rust-ast-lower-pattern.h"
+#include "rust-ast-lower-block.h"
+#include "rust-ast-lower-extern.h"
+#include "rust-hir-full-decls.h"
+
+namespace Rust {
+namespace HIR {
+
+class ASTLoweringItem : public ASTLoweringBase
+{
+  using Rust::HIR::ASTLoweringBase::visit;
+
+public:
+  static HIR::Item *translate (AST::Item *item);
+
+  void visit (AST::Module &module) override;
+  void visit (AST::TypeAlias &alias) override;
+  void visit (AST::TupleStruct &struct_decl) override;
+  void visit (AST::StructStruct &struct_decl) override;
+  void visit (AST::Enum &enum_decl) override;
+  void visit (AST::Union &union_decl) override;
+  void visit (AST::StaticItem &var) override;
+  void visit (AST::ConstantItem &constant) override;
+  void visit (AST::Function &function) override;
+  void visit (AST::InherentImpl &impl_block) override;
+  void visit (AST::Trait &trait) override;
+  void visit (AST::TraitImpl &impl_block) override;
+  void visit (AST::ExternBlock &extern_block) override;
+
+private:
+  ASTLoweringItem () : translated (nullptr) {}
+
+  HIR::Item *translated;
+};
+
+class ASTLoweringSimplePath : public ASTLoweringBase
+{
+public:
+  static HIR::SimplePath translate (const AST::SimplePath &path);
+
+  HIR::SimplePathSegment lower (const AST::SimplePathSegment &segment);
+  HIR::SimplePath lower (const AST::SimplePath &path);
+};
+
+} // namespace HIR
+} // namespace Rust
+
+#endif // RUST_AST_LOWER_ITEM
diff --git a/gcc/rust/hir/rust-ast-lower-pattern.cc b/gcc/rust/hir/rust-ast-lower-pattern.cc
new file mode 100644
index 00000000000..2421ca84651
--- /dev/null
+++ b/gcc/rust/hir/rust-ast-lower-pattern.cc
@@ -0,0 +1,229 @@
+// 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/>.
+
+#include "rust-ast-lower-pattern.h"
+#include "rust-ast-lower-expr.h"
+
+namespace Rust {
+namespace HIR {
+
+void
+ASTLoweringPattern::visit (AST::IdentifierPattern &pattern)
+{
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, pattern.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  std::unique_ptr<Pattern> to_bind;
+  translated
+    = new HIR::IdentifierPattern (mapping, pattern.get_ident (),
+				  pattern.get_locus (), pattern.get_is_ref (),
+				  pattern.get_is_mut () ? Mutability::Mut
+							: Mutability::Imm,
+				  std::move (to_bind));
+}
+
+void
+ASTLoweringPattern::visit (AST::PathInExpression &pattern)
+{
+  translated = ASTLowerPathInExpression::translate (&pattern);
+}
+
+void
+ASTLoweringPattern::visit (AST::TupleStructPattern &pattern)
+{
+  HIR::PathInExpression *path
+    = ASTLowerPathInExpression::translate (&pattern.get_path ());
+
+  TupleStructItems *lowered = nullptr;
+  auto &items = pattern.get_items ();
+  switch (items->get_item_type ())
+    {
+      case AST::TupleStructItems::RANGE: {
+	// TODO
+	gcc_unreachable ();
+      }
+      break;
+
+      case AST::TupleStructItems::NO_RANGE: {
+	AST::TupleStructItemsNoRange &items_no_range
+	  = static_cast<AST::TupleStructItemsNoRange &> (*items.get ());
+
+	std::vector<std::unique_ptr<HIR::Pattern> > patterns;
+	for (auto &inner_pattern : items_no_range.get_patterns ())
+	  {
+	    HIR::Pattern *p
+	      = ASTLoweringPattern::translate (inner_pattern.get ());
+	    patterns.push_back (std::unique_ptr<HIR::Pattern> (p));
+	  }
+
+	lowered = new HIR::TupleStructItemsNoRange (std::move (patterns));
+      }
+      break;
+    }
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, pattern.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated = new HIR::TupleStructPattern (
+    mapping, *path, std::unique_ptr<HIR::TupleStructItems> (lowered));
+}
+
+void
+ASTLoweringPattern::visit (AST::StructPattern &pattern)
+{
+  HIR::PathInExpression *path
+    = ASTLowerPathInExpression::translate (&pattern.get_path ());
+
+  auto &raw_elems = pattern.get_struct_pattern_elems ();
+  rust_assert (!raw_elems.has_etc ());
+
+  std::vector<std::unique_ptr<HIR::StructPatternField> > fields;
+  for (auto &field : raw_elems.get_struct_pattern_fields ())
+    {
+      HIR::StructPatternField *f = nullptr;
+      switch (field->get_item_type ())
+	{
+	  case AST::StructPatternField::ItemType::TUPLE_PAT: {
+	    // TODO
+	    gcc_unreachable ();
+	  }
+	  break;
+
+	  case AST::StructPatternField::ItemType::IDENT_PAT: {
+	    // TODO
+	    gcc_unreachable ();
+	  }
+	  break;
+
+	  case AST::StructPatternField::ItemType::IDENT: {
+	    AST::StructPatternFieldIdent &ident
+	      = static_cast<AST::StructPatternFieldIdent &> (*field.get ());
+
+	    auto crate_num = mappings->get_current_crate ();
+	    Analysis::NodeMapping mapping (crate_num, ident.get_node_id (),
+					   mappings->get_next_hir_id (
+					     crate_num),
+					   UNKNOWN_LOCAL_DEFID);
+
+	    f = new HIR::StructPatternFieldIdent (
+	      mapping, ident.get_identifier (), ident.is_ref (),
+	      ident.is_mut () ? Mutability::Mut : Mutability::Imm,
+	      ident.get_outer_attrs (), ident.get_locus ());
+	  }
+	  break;
+	}
+
+      // insert the reverse mappings and locations
+      auto field_id = f->get_mappings ().get_hirid ();
+      auto field_node_id = f->get_mappings ().get_nodeid ();
+      mappings->insert_location (field_id, f->get_locus ());
+      mappings->insert_node_to_hir (field_node_id, field_id);
+
+      // add it to the lowered fields list
+      fields.push_back (std::unique_ptr<HIR::StructPatternField> (f));
+    }
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, pattern.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  HIR::StructPatternElements elems (std::move (fields));
+  translated = new HIR::StructPattern (mapping, *path, std::move (elems));
+}
+
+void
+ASTLoweringPattern::visit (AST::WildcardPattern &pattern)
+{
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, pattern.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated = new HIR::WildcardPattern (mapping, pattern.get_locus ());
+}
+
+void
+ASTLoweringPattern::visit (AST::TuplePattern &pattern)
+{
+  std::unique_ptr<HIR::TuplePatternItems> items;
+  switch (pattern.get_items ()->get_pattern_type ())
+    {
+      case AST::TuplePatternItems::TuplePatternItemType::MULTIPLE: {
+	AST::TuplePatternItemsMultiple &ref
+	  = *static_cast<AST::TuplePatternItemsMultiple *> (
+	    pattern.get_items ().get ());
+	items = lower_tuple_pattern_multiple (ref);
+      }
+      break;
+
+      case AST::TuplePatternItems::TuplePatternItemType::RANGED: {
+	AST::TuplePatternItemsRanged &ref
+	  = *static_cast<AST::TuplePatternItemsRanged *> (
+	    pattern.get_items ().get ());
+	items = lower_tuple_pattern_ranged (ref);
+      }
+      break;
+    }
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, pattern.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated
+    = new HIR::TuplePattern (mapping, std::move (items), pattern.get_locus ());
+}
+
+void
+ASTLoweringPattern::visit (AST::LiteralPattern &pattern)
+{
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, pattern.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  HIR::Literal l = lower_literal (pattern.get_literal ());
+  translated
+    = new HIR::LiteralPattern (mapping, std::move (l), pattern.get_locus ());
+}
+
+void
+ASTLoweringPattern::visit (AST::RangePattern &pattern)
+{
+  auto upper_bound
+    = lower_range_pattern_bound (pattern.get_upper_bound ().get ());
+  auto lower_bound
+    = lower_range_pattern_bound (pattern.get_lower_bound ().get ());
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, pattern.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated
+    = new HIR::RangePattern (mapping, std::move (lower_bound),
+			     std::move (upper_bound), pattern.get_locus ());
+}
+
+} // namespace HIR
+} // namespace Rust
diff --git a/gcc/rust/hir/rust-ast-lower-pattern.h b/gcc/rust/hir/rust-ast-lower-pattern.h
new file mode 100644
index 00000000000..aab99f602d5
--- /dev/null
+++ b/gcc/rust/hir/rust-ast-lower-pattern.h
@@ -0,0 +1,72 @@
+// 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_AST_LOWER_PATTERN
+#define RUST_AST_LOWER_PATTERN
+
+#include "rust-ast-lower-base.h"
+
+namespace Rust {
+namespace HIR {
+
+class ASTLoweringPattern : public ASTLoweringBase
+{
+  using Rust::HIR::ASTLoweringBase::visit;
+
+public:
+  static HIR::Pattern *translate (AST::Pattern *pattern)
+  {
+    ASTLoweringPattern resolver;
+    pattern->accept_vis (resolver);
+
+    rust_assert (resolver.translated != nullptr);
+
+    resolver.mappings->insert_hir_pattern (resolver.translated);
+    resolver.mappings->insert_location (
+      resolver.translated->get_pattern_mappings ().get_hirid (),
+      pattern->get_locus ());
+
+    return resolver.translated;
+  }
+
+  void visit (AST::IdentifierPattern &pattern) override;
+
+  void visit (AST::PathInExpression &pattern) override;
+
+  void visit (AST::StructPattern &pattern) override;
+
+  void visit (AST::TupleStructPattern &pattern) override;
+
+  void visit (AST::WildcardPattern &pattern) override;
+
+  void visit (AST::TuplePattern &pattern) override;
+
+  void visit (AST::LiteralPattern &pattern) override;
+
+  void visit (AST::RangePattern &pattern) override;
+
+private:
+  ASTLoweringPattern () : translated (nullptr) {}
+
+  HIR::Pattern *translated;
+};
+
+} // namespace HIR
+} // namespace Rust
+
+#endif // RUST_AST_LOWER_PATTERN
diff --git a/gcc/rust/hir/rust-ast-lower-stmt.h b/gcc/rust/hir/rust-ast-lower-stmt.h
new file mode 100644
index 00000000000..2b26ae3835c
--- /dev/null
+++ b/gcc/rust/hir/rust-ast-lower-stmt.h
@@ -0,0 +1,418 @@
+// 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_AST_LOWER_STMT
+#define RUST_AST_LOWER_STMT
+
+#include "rust-diagnostics.h"
+
+#include "rust-ast-lower-base.h"
+#include "rust-ast-lower-enumitem.h"
+#include "rust-ast-lower-type.h"
+#include "rust-ast-lower-block.h"
+#include "rust-ast-lower-expr.h"
+#include "rust-ast-lower-pattern.h"
+
+namespace Rust {
+namespace HIR {
+
+class ASTLoweringStmt : public ASTLoweringBase
+{
+  using Rust::HIR::ASTLoweringBase::visit;
+
+public:
+  static HIR::Stmt *translate (AST::Stmt *stmt, bool *terminated)
+  {
+    ASTLoweringStmt resolver;
+    stmt->accept_vis (resolver);
+
+    rust_assert (resolver.translated != nullptr);
+    *terminated = resolver.terminated;
+    resolver.mappings->insert_location (
+      resolver.translated->get_mappings ().get_hirid (),
+      resolver.translated->get_locus ());
+    resolver.mappings->insert_hir_stmt (resolver.translated);
+    if (resolver.translated->is_item ())
+      {
+	HIR::Item *i = static_cast<HIR::Item *> (resolver.translated);
+
+	auto defid = resolver.translated->get_mappings ().get_defid ();
+
+	resolver.handle_outer_attributes (*i);
+	resolver.mappings->insert_hir_item (i);
+	resolver.mappings->insert_defid_mapping (defid, i);
+      }
+
+    return resolver.translated;
+  }
+
+  void visit (AST::ExprStmtWithBlock &stmt) override
+  {
+    HIR::ExprWithBlock *expr
+      = ASTLoweringExprWithBlock::translate (stmt.get_expr ().get (),
+					     &terminated);
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, stmt.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   UNKNOWN_LOCAL_DEFID);
+    translated
+      = new HIR::ExprStmtWithBlock (mapping,
+				    std::unique_ptr<HIR::ExprWithBlock> (expr),
+				    stmt.get_locus (),
+				    !stmt.is_semicolon_followed ());
+  }
+
+  void visit (AST::ExprStmtWithoutBlock &stmt) override
+  {
+    HIR::Expr *expr
+      = ASTLoweringExpr::translate (stmt.get_expr ().get (), &terminated);
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, stmt.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   UNKNOWN_LOCAL_DEFID);
+    translated
+      = new HIR::ExprStmtWithoutBlock (mapping,
+				       std::unique_ptr<HIR::Expr> (expr),
+				       stmt.get_locus ());
+  }
+
+  void visit (AST::ConstantItem &constant) override
+  {
+    HIR::Visibility vis = translate_visibility (constant.get_visibility ());
+
+    HIR::Type *type = ASTLoweringType::translate (constant.get_type ().get ());
+    HIR::Expr *expr = ASTLoweringExpr::translate (constant.get_expr ().get ());
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, constant.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   mappings->get_next_localdef_id (crate_num));
+
+    translated = new HIR::ConstantItem (mapping, constant.get_identifier (),
+					vis, std::unique_ptr<HIR::Type> (type),
+					std::unique_ptr<HIR::Expr> (expr),
+					constant.get_outer_attrs (),
+					constant.get_locus ());
+  }
+
+  void visit (AST::LetStmt &stmt) override
+  {
+    HIR::Pattern *variables
+      = ASTLoweringPattern::translate (stmt.get_pattern ().get ());
+    HIR::Type *type = stmt.has_type ()
+			? ASTLoweringType::translate (stmt.get_type ().get ())
+			: nullptr;
+    HIR::Expr *init_expression
+      = stmt.has_init_expr ()
+	  ? ASTLoweringExpr::translate (stmt.get_init_expr ().get ())
+	  : nullptr;
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, stmt.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   UNKNOWN_LOCAL_DEFID);
+    translated
+      = new HIR::LetStmt (mapping, std::unique_ptr<HIR::Pattern> (variables),
+			  std::unique_ptr<HIR::Expr> (init_expression),
+			  std::unique_ptr<HIR::Type> (type),
+			  stmt.get_outer_attrs (), stmt.get_locus ());
+  }
+
+  void visit (AST::TupleStruct &struct_decl) override
+  {
+    std::vector<std::unique_ptr<HIR::GenericParam>> generic_params;
+    if (struct_decl.has_generics ())
+      {
+	generic_params
+	  = lower_generic_params (struct_decl.get_generic_params ());
+      }
+
+    std::vector<std::unique_ptr<HIR::WhereClauseItem>> where_clause_items;
+    HIR::WhereClause where_clause (std::move (where_clause_items));
+    HIR::Visibility vis = translate_visibility (struct_decl.get_visibility ());
+
+    std::vector<HIR::TupleField> fields;
+    for (AST::TupleField &field : struct_decl.get_fields ())
+      {
+	HIR::Visibility vis = translate_visibility (field.get_visibility ());
+	HIR::Type *type
+	  = ASTLoweringType::translate (field.get_field_type ().get ());
+
+	auto crate_num = mappings->get_current_crate ();
+	Analysis::NodeMapping mapping (crate_num, field.get_node_id (),
+				       mappings->get_next_hir_id (crate_num),
+				       mappings->get_next_localdef_id (
+					 crate_num));
+
+	HIR::TupleField translated_field (mapping,
+					  std::unique_ptr<HIR::Type> (type),
+					  vis, field.get_locus (),
+					  field.get_outer_attrs ());
+	fields.push_back (std::move (translated_field));
+      }
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, struct_decl.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   mappings->get_next_localdef_id (crate_num));
+
+    translated = new HIR::TupleStruct (mapping, std::move (fields),
+				       struct_decl.get_identifier (),
+				       std::move (generic_params),
+				       std::move (where_clause), vis,
+				       struct_decl.get_outer_attrs (),
+				       struct_decl.get_locus ());
+  }
+
+  void visit (AST::StructStruct &struct_decl) override
+  {
+    std::vector<std::unique_ptr<HIR::GenericParam>> generic_params;
+    if (struct_decl.has_generics ())
+      {
+	generic_params
+	  = lower_generic_params (struct_decl.get_generic_params ());
+      }
+
+    std::vector<std::unique_ptr<HIR::WhereClauseItem>> where_clause_items;
+    HIR::WhereClause where_clause (std::move (where_clause_items));
+    HIR::Visibility vis = translate_visibility (struct_decl.get_visibility ());
+
+    bool is_unit = struct_decl.is_unit_struct ();
+    std::vector<HIR::StructField> fields;
+    for (AST::StructField &field : struct_decl.get_fields ())
+      {
+	HIR::Visibility vis = translate_visibility (field.get_visibility ());
+	HIR::Type *type
+	  = ASTLoweringType::translate (field.get_field_type ().get ());
+
+	auto crate_num = mappings->get_current_crate ();
+	Analysis::NodeMapping mapping (crate_num, field.get_node_id (),
+				       mappings->get_next_hir_id (crate_num),
+				       mappings->get_next_localdef_id (
+					 crate_num));
+
+	HIR::StructField translated_field (mapping, field.get_field_name (),
+					   std::unique_ptr<HIR::Type> (type),
+					   vis, field.get_locus (),
+					   field.get_outer_attrs ());
+
+	if (struct_field_name_exists (fields, translated_field))
+	  break;
+
+	fields.push_back (std::move (translated_field));
+      }
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, struct_decl.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   mappings->get_next_localdef_id (crate_num));
+
+    translated = new HIR::StructStruct (mapping, std::move (fields),
+					struct_decl.get_identifier (),
+					std::move (generic_params),
+					std::move (where_clause), is_unit, vis,
+					struct_decl.get_outer_attrs (),
+					struct_decl.get_locus ());
+  }
+
+  void visit (AST::Union &union_decl) override
+  {
+    std::vector<std::unique_ptr<HIR::GenericParam>> generic_params;
+    if (union_decl.has_generics ())
+      {
+	generic_params
+	  = lower_generic_params (union_decl.get_generic_params ());
+      }
+
+    std::vector<std::unique_ptr<HIR::WhereClauseItem>> where_clause_items;
+    HIR::WhereClause where_clause (std::move (where_clause_items));
+    HIR::Visibility vis = translate_visibility (union_decl.get_visibility ());
+
+    std::vector<HIR::StructField> variants;
+    for (AST::StructField &variant : union_decl.get_variants ())
+      {
+	HIR::Visibility vis = translate_visibility (variant.get_visibility ());
+	HIR::Type *type
+	  = ASTLoweringType::translate (variant.get_field_type ().get ());
+
+	auto crate_num = mappings->get_current_crate ();
+	Analysis::NodeMapping mapping (crate_num, variant.get_node_id (),
+				       mappings->get_next_hir_id (crate_num),
+				       mappings->get_next_localdef_id (
+					 crate_num));
+
+	HIR::StructField translated_variant (mapping, variant.get_field_name (),
+					     std::unique_ptr<HIR::Type> (type),
+					     vis, variant.get_locus (),
+					     variant.get_outer_attrs ());
+
+	if (struct_field_name_exists (variants, translated_variant))
+	  break;
+
+	variants.push_back (std::move (translated_variant));
+      }
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, union_decl.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   mappings->get_next_localdef_id (crate_num));
+
+    translated
+      = new HIR::Union (mapping, union_decl.get_identifier (), vis,
+			std::move (generic_params), std::move (where_clause),
+			std::move (variants), union_decl.get_outer_attrs (),
+			union_decl.get_locus ());
+  }
+
+  void visit (AST::Enum &enum_decl) override
+  {
+    std::vector<std::unique_ptr<HIR::GenericParam>> generic_params;
+    if (enum_decl.has_generics ())
+      {
+	generic_params = lower_generic_params (enum_decl.get_generic_params ());
+      }
+
+    std::vector<std::unique_ptr<HIR::WhereClauseItem>> where_clause_items;
+    HIR::WhereClause where_clause (std::move (where_clause_items));
+    HIR::Visibility vis = translate_visibility (enum_decl.get_visibility ());
+
+    // bool is_unit = enum_decl.is_zero_variant ();
+    std::vector<std::unique_ptr<HIR::EnumItem>> items;
+    for (auto &variant : enum_decl.get_variants ())
+      {
+	HIR::EnumItem *hir_item
+	  = ASTLoweringEnumItem::translate (variant.get ());
+	items.push_back (std::unique_ptr<HIR::EnumItem> (hir_item));
+      }
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, enum_decl.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   mappings->get_next_localdef_id (crate_num));
+
+    translated = new HIR::Enum (mapping, enum_decl.get_identifier (), vis,
+				std::move (generic_params),
+				std::move (where_clause), /* is_unit, */
+				std::move (items), enum_decl.get_outer_attrs (),
+				enum_decl.get_locus ());
+  }
+
+  void visit (AST::EmptyStmt &empty) override
+  {
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, empty.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   mappings->get_next_localdef_id (crate_num));
+
+    translated = new HIR::EmptyStmt (mapping, empty.get_locus ());
+  }
+
+  void visit (AST::Function &function) override
+  {
+    // ignore for now and leave empty
+    std::vector<std::unique_ptr<HIR::WhereClauseItem>> where_clause_items;
+    HIR::WhereClause where_clause (std::move (where_clause_items));
+    HIR::FunctionQualifiers qualifiers
+      = lower_qualifiers (function.get_qualifiers ());
+    HIR::Visibility vis = translate_visibility (function.get_visibility ());
+
+    // need
+    std::vector<std::unique_ptr<HIR::GenericParam>> generic_params;
+    if (function.has_generics ())
+      {
+	generic_params = lower_generic_params (function.get_generic_params ());
+      }
+
+    Identifier function_name = function.get_function_name ();
+    Location locus = function.get_locus ();
+
+    std::unique_ptr<HIR::Type> return_type
+      = function.has_return_type () ? std::unique_ptr<HIR::Type> (
+	  ASTLoweringType::translate (function.get_return_type ().get ()))
+				    : nullptr;
+
+    std::vector<HIR::FunctionParam> function_params;
+    for (auto &param : function.get_function_params ())
+      {
+	auto translated_pattern = std::unique_ptr<HIR::Pattern> (
+	  ASTLoweringPattern::translate (param.get_pattern ().get ()));
+	auto translated_type = std::unique_ptr<HIR::Type> (
+	  ASTLoweringType::translate (param.get_type ().get ()));
+
+	auto crate_num = mappings->get_current_crate ();
+	Analysis::NodeMapping mapping (crate_num, param.get_node_id (),
+				       mappings->get_next_hir_id (crate_num),
+				       UNKNOWN_LOCAL_DEFID);
+
+	auto hir_param
+	  = HIR::FunctionParam (mapping, std::move (translated_pattern),
+				std::move (translated_type),
+				param.get_locus ());
+	function_params.push_back (hir_param);
+      }
+
+    bool terminated = false;
+    std::unique_ptr<HIR::BlockExpr> function_body
+      = std::unique_ptr<HIR::BlockExpr> (
+	ASTLoweringBlock::translate (function.get_definition ().get (),
+				     &terminated));
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, function.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   mappings->get_next_localdef_id (crate_num));
+
+    mappings->insert_location (function_body->get_mappings ().get_hirid (),
+			       function.get_locus ());
+
+    auto fn
+      = new HIR::Function (mapping, std::move (function_name),
+			   std::move (qualifiers), std::move (generic_params),
+			   std::move (function_params), std::move (return_type),
+			   std::move (where_clause), std::move (function_body),
+			   std::move (vis), function.get_outer_attrs (),
+			   HIR::SelfParam::error (), locus);
+
+    // add the mappings for the function params at the end
+    for (auto &param : fn->get_function_params ())
+      {
+	mappings->insert_hir_param (&param);
+	mappings->insert_location (mapping.get_hirid (), param.get_locus ());
+      }
+
+    translated = fn;
+  }
+
+  void visit (AST::ExternBlock &extern_block) override
+  {
+    translated = lower_extern_block (extern_block);
+  }
+
+private:
+  ASTLoweringStmt () : translated (nullptr), terminated (false) {}
+
+  HIR::Stmt *translated;
+  bool terminated;
+};
+
+} // namespace HIR
+} // namespace Rust
+
+#endif // RUST_AST_LOWER_PATTERN
diff --git a/gcc/rust/hir/rust-ast-lower-struct-field-expr.h b/gcc/rust/hir/rust-ast-lower-struct-field-expr.h
new file mode 100644
index 00000000000..dadf3594904
--- /dev/null
+++ b/gcc/rust/hir/rust-ast-lower-struct-field-expr.h
@@ -0,0 +1,63 @@
+// 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_AST_LOWER_STRUCT_FIELD_EXPR
+#define RUST_AST_LOWER_STRUCT_FIELD_EXPR
+
+#include "rust-diagnostics.h"
+#include "rust-ast-lower-base.h"
+
+namespace Rust {
+namespace HIR {
+
+class ASTLowerStructExprField : public ASTLoweringBase
+{
+  using Rust::HIR::ASTLoweringBase::visit;
+
+public:
+  static HIR::StructExprField *translate (AST::StructExprField *field)
+  {
+    ASTLowerStructExprField compiler;
+    field->accept_vis (compiler);
+    rust_assert (compiler.translated != nullptr);
+
+    compiler.mappings->insert_hir_struct_field (compiler.translated);
+    compiler.mappings->insert_location (
+      compiler.translated->get_mappings ().get_hirid (), field->get_locus ());
+
+    return compiler.translated;
+  }
+
+  ~ASTLowerStructExprField () {}
+
+  void visit (AST::StructExprFieldIdentifierValue &field) override;
+
+  void visit (AST::StructExprFieldIndexValue &field) override;
+
+  void visit (AST::StructExprFieldIdentifier &field) override;
+
+private:
+  ASTLowerStructExprField () : translated (nullptr) {}
+
+  HIR::StructExprField *translated;
+};
+
+} // namespace HIR
+} // namespace Rust
+
+#endif // RUST_AST_LOWER_STRUCT_FIELD_EXPR
diff --git a/gcc/rust/hir/rust-ast-lower-type.h b/gcc/rust/hir/rust-ast-lower-type.h
new file mode 100644
index 00000000000..46f765a3ea2
--- /dev/null
+++ b/gcc/rust/hir/rust-ast-lower-type.h
@@ -0,0 +1,532 @@
+// 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_AST_LOWER_TYPE
+#define RUST_AST_LOWER_TYPE
+
+#include "rust-ast-lower-base.h"
+#include "rust-diagnostics.h"
+#include "rust-ast-lower-expr.h"
+
+namespace Rust {
+namespace HIR {
+
+class ASTLowerTypePath : public ASTLoweringBase
+{
+protected:
+  using Rust::HIR::ASTLoweringBase::visit;
+
+public:
+  static HIR::TypePath *translate (AST::TypePath &type)
+  {
+    ASTLowerTypePath resolver;
+    type.accept_vis (resolver);
+    rust_assert (resolver.translated != nullptr);
+    return resolver.translated;
+  }
+
+  void visit (AST::TypePathSegmentFunction &) override { gcc_unreachable (); }
+
+  void visit (AST::TypePathSegment &segment) override
+  {
+    auto crate_num = mappings->get_current_crate ();
+    auto hirid = mappings->get_next_hir_id (crate_num);
+    Analysis::NodeMapping mapping (crate_num, segment.get_node_id (), hirid,
+				   UNKNOWN_LOCAL_DEFID);
+
+    HIR::PathIdentSegment ident (segment.get_ident_segment ().as_string ());
+    translated_segment
+      = new HIR::TypePathSegment (std::move (mapping), ident,
+				  segment.get_separating_scope_resolution (),
+				  segment.get_locus ());
+  }
+
+  void visit (AST::TypePathSegmentGeneric &segment) override;
+
+  void visit (AST::TypePath &path) override
+  {
+    std::vector<std::unique_ptr<HIR::TypePathSegment>> translated_segments;
+
+    for (auto &seg : path.get_segments ())
+      {
+	translated_segment = nullptr;
+	seg->accept_vis (*this);
+	if (translated_segment == nullptr)
+	  {
+	    rust_fatal_error (seg->get_locus (),
+			      "failed to translate AST TypePathSegment");
+	  }
+	translated_segments.push_back (
+	  std::unique_ptr<HIR::TypePathSegment> (translated_segment));
+      }
+
+    auto crate_num = mappings->get_current_crate ();
+    auto hirid = mappings->get_next_hir_id (crate_num);
+    Analysis::NodeMapping mapping (crate_num, path.get_node_id (), hirid,
+				   mappings->get_next_localdef_id (crate_num));
+
+    translated
+      = new HIR::TypePath (std::move (mapping), std::move (translated_segments),
+			   path.get_locus (),
+			   path.has_opening_scope_resolution_op ());
+  }
+
+protected:
+  HIR::TypePathSegment *translated_segment;
+
+private:
+  HIR::TypePath *translated;
+};
+
+class ASTLowerQualifiedPathInType : public ASTLowerTypePath
+{
+  using ASTLowerTypePath::visit;
+
+public:
+  static HIR::QualifiedPathInType *translate (AST::QualifiedPathInType &type)
+  {
+    ASTLowerQualifiedPathInType resolver;
+    type.accept_vis (resolver);
+    rust_assert (resolver.translated != nullptr);
+    return resolver.translated;
+  }
+
+  void visit (AST::QualifiedPathInType &path) override;
+
+private:
+  HIR::QualifiedPathInType *translated;
+};
+
+class ASTLoweringType : public ASTLoweringBase
+{
+  using Rust::HIR::ASTLoweringBase::visit;
+
+public:
+  static HIR::Type *translate (AST::Type *type)
+  {
+    ASTLoweringType resolver;
+    type->accept_vis (resolver);
+
+    rust_assert (resolver.translated != nullptr);
+    resolver.mappings->insert_hir_type (resolver.translated);
+    resolver.mappings->insert_location (
+      resolver.translated->get_mappings ().get_hirid (),
+      resolver.translated->get_locus ());
+
+    return resolver.translated;
+  }
+
+  void visit (AST::BareFunctionType &fntype) override
+  {
+    bool is_variadic = false;
+    std::vector<HIR::LifetimeParam> lifetime_params;
+    HIR::FunctionQualifiers qualifiers
+      = lower_qualifiers (fntype.get_function_qualifiers ());
+
+    std::vector<HIR::MaybeNamedParam> named_params;
+    for (auto &param : fntype.get_function_params ())
+      {
+	HIR::MaybeNamedParam::ParamKind kind;
+	switch (param.get_param_kind ())
+	  {
+	  case AST::MaybeNamedParam::ParamKind::UNNAMED:
+	    kind = HIR::MaybeNamedParam::ParamKind::UNNAMED;
+	    break;
+	  case AST::MaybeNamedParam::ParamKind::IDENTIFIER:
+	    kind = HIR::MaybeNamedParam::ParamKind::IDENTIFIER;
+	    break;
+	  case AST::MaybeNamedParam::ParamKind::WILDCARD:
+	    kind = HIR::MaybeNamedParam::ParamKind::WILDCARD;
+	    break;
+	  default:
+	    gcc_unreachable ();
+	  }
+
+	HIR::Type *param_type
+	  = ASTLoweringType::translate (param.get_type ().get ());
+
+	HIR::MaybeNamedParam p (param.get_name (), kind,
+				std::unique_ptr<HIR::Type> (param_type),
+				param.get_locus ());
+	named_params.push_back (std::move (p));
+      }
+
+    HIR::Type *return_type = nullptr;
+    if (fntype.has_return_type ())
+      {
+	return_type
+	  = ASTLoweringType::translate (fntype.get_return_type ().get ());
+      }
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, fntype.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   mappings->get_next_localdef_id (crate_num));
+
+    translated = new HIR::BareFunctionType (
+      std::move (mapping), std::move (lifetime_params), std::move (qualifiers),
+      std::move (named_params), is_variadic,
+      std::unique_ptr<HIR::Type> (return_type), fntype.get_locus ());
+  }
+
+  void visit (AST::TupleType &tuple) override
+  {
+    std::vector<std::unique_ptr<HIR::Type>> elems;
+    for (auto &e : tuple.get_elems ())
+      {
+	HIR::Type *t = ASTLoweringType::translate (e.get ());
+	elems.push_back (std::unique_ptr<HIR::Type> (t));
+      }
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, tuple.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   mappings->get_next_localdef_id (crate_num));
+
+    translated = new HIR::TupleType (std::move (mapping), std::move (elems),
+				     tuple.get_locus ());
+  }
+
+  void visit (AST::TypePath &path) override
+  {
+    translated = ASTLowerTypePath::translate (path);
+  }
+
+  void visit (AST::QualifiedPathInType &path) override
+  {
+    translated = ASTLowerQualifiedPathInType::translate (path);
+  }
+
+  void visit (AST::ArrayType &type) override
+  {
+    HIR::Type *translated_type
+      = ASTLoweringType::translate (type.get_elem_type ().get ());
+    HIR::Expr *array_size
+      = ASTLoweringExpr::translate (type.get_size_expr ().get ());
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, type.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   mappings->get_next_localdef_id (crate_num));
+
+    translated
+      = new HIR::ArrayType (mapping,
+			    std::unique_ptr<HIR::Type> (translated_type),
+			    std::unique_ptr<HIR::Expr> (array_size),
+			    type.get_locus ());
+  }
+
+  void visit (AST::ReferenceType &type) override
+  {
+    HIR::Lifetime lifetime = lower_lifetime (type.get_lifetime ());
+
+    HIR::Type *base_type
+      = ASTLoweringType::translate (type.get_base_type ().get ());
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, type.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   mappings->get_next_localdef_id (crate_num));
+
+    translated = new HIR::ReferenceType (mapping,
+					 type.get_has_mut () ? Mutability::Mut
+							     : Mutability::Imm,
+					 std::unique_ptr<HIR::Type> (base_type),
+					 type.get_locus (), lifetime);
+  }
+
+  void visit (AST::RawPointerType &type) override
+  {
+    HIR::Type *base_type
+      = ASTLoweringType::translate (type.get_type_pointed_to ().get ());
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, type.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   mappings->get_next_localdef_id (crate_num));
+
+    translated
+      = new HIR::RawPointerType (mapping,
+				 type.get_pointer_type ()
+				     == AST::RawPointerType::PointerType::MUT
+				   ? Mutability::Mut
+				   : Mutability::Imm,
+				 std::unique_ptr<HIR::Type> (base_type),
+				 type.get_locus ());
+  }
+
+  void visit (AST::SliceType &type) override
+  {
+    HIR::Type *base_type
+      = ASTLoweringType::translate (type.get_elem_type ().get ());
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, type.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   mappings->get_next_localdef_id (crate_num));
+
+    translated
+      = new HIR::SliceType (mapping, std::unique_ptr<HIR::Type> (base_type),
+			    type.get_locus ());
+  }
+
+  void visit (AST::InferredType &type) override
+  {
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, type.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   mappings->get_next_localdef_id (crate_num));
+
+    translated = new HIR::InferredType (mapping, type.get_locus ());
+  }
+
+  void visit (AST::NeverType &type) override
+  {
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, type.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   mappings->get_next_localdef_id (crate_num));
+
+    translated = new HIR::NeverType (mapping, type.get_locus ());
+  }
+
+  void visit (AST::TraitObjectTypeOneBound &type) override;
+
+  void visit (AST::TraitObjectType &type) override;
+
+private:
+  ASTLoweringType () : ASTLoweringBase (), translated (nullptr) {}
+
+  HIR::Type *translated;
+};
+
+class ASTLowerGenericParam : public ASTLoweringBase
+{
+  using Rust::HIR::ASTLoweringBase::visit;
+
+public:
+  static HIR::GenericParam *translate (AST::GenericParam *param)
+  {
+    ASTLowerGenericParam resolver;
+    param->accept_vis (resolver);
+
+    rust_assert (resolver.translated != nullptr);
+    resolver.mappings->insert_location (
+      resolver.translated->get_mappings ().get_hirid (), param->get_locus ());
+    resolver.mappings->insert_hir_generic_param (resolver.translated);
+
+    return resolver.translated;
+  }
+
+  void visit (AST::LifetimeParam &param) override
+  {
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, param.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   mappings->get_next_localdef_id (crate_num));
+
+    HIR::Lifetime lt (mapping, param.get_lifetime ().get_lifetime_type (),
+		      param.get_lifetime ().get_lifetime_name (),
+		      param.get_lifetime ().get_locus ());
+
+    translated = new HIR::LifetimeParam (mapping, lt, param.get_locus (),
+					 std::vector<Lifetime> ());
+  }
+
+  void visit (AST::ConstGenericParam &param) override
+  {
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, param.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   mappings->get_next_localdef_id (crate_num));
+
+    auto type = ASTLoweringType::translate (param.get_type ().get ());
+
+    HIR::Expr *default_expr = nullptr;
+    if (param.has_default_value ())
+      default_expr = ASTLoweringExpr::translate (
+	param.get_default_value ().get_expression ().get ());
+
+    translated
+      = new HIR::ConstGenericParam (param.get_name (),
+				    std::unique_ptr<Type> (type),
+				    std::unique_ptr<Expr> (default_expr),
+				    mapping, param.get_locus ());
+  }
+
+  void visit (AST::TypeParam &param) override
+  {
+    AST::Attribute outer_attr = AST::Attribute::create_empty ();
+    std::vector<std::unique_ptr<HIR::TypeParamBound>> type_param_bounds;
+    if (param.has_type_param_bounds ())
+      {
+	for (auto &bound : param.get_type_param_bounds ())
+	  {
+	    HIR::TypeParamBound *lowered_bound = lower_bound (bound.get ());
+	    type_param_bounds.push_back (
+	      std::unique_ptr<HIR::TypeParamBound> (lowered_bound));
+	  }
+      }
+
+    HIR::Type *type = param.has_type ()
+			? ASTLoweringType::translate (param.get_type ().get ())
+			: nullptr;
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, param.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   mappings->get_next_localdef_id (crate_num));
+
+    translated
+      = new HIR::TypeParam (mapping, param.get_type_representation (),
+			    param.get_locus (), std::move (type_param_bounds),
+			    std::unique_ptr<Type> (type),
+			    std::move (outer_attr));
+  }
+
+private:
+  ASTLowerGenericParam () : ASTLoweringBase (), translated (nullptr) {}
+
+  HIR::GenericParam *translated;
+};
+
+class ASTLoweringTypeBounds : public ASTLoweringBase
+{
+  using Rust::HIR::ASTLoweringBase::visit;
+
+public:
+  static HIR::TypeParamBound *translate (AST::TypeParamBound *type)
+  {
+    ASTLoweringTypeBounds resolver;
+    type->accept_vis (resolver);
+
+    rust_assert (resolver.translated != nullptr);
+    resolver.mappings->insert_location (
+      resolver.translated->get_mappings ().get_hirid (),
+      resolver.translated->get_locus ());
+
+    return resolver.translated;
+  }
+
+  void visit (AST::TraitBound &bound) override
+  {
+    // FIXME
+    std::vector<HIR::LifetimeParam> lifetimes;
+
+    AST::TypePath &ast_trait_path = bound.get_type_path ();
+    HIR::TypePath *trait_path = ASTLowerTypePath::translate (ast_trait_path);
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, bound.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   UNKNOWN_LOCAL_DEFID);
+
+    translated = new HIR::TraitBound (mapping, *trait_path, bound.get_locus (),
+				      bound.is_in_parens (),
+				      bound.has_opening_question_mark ());
+  }
+
+  void visit (AST::Lifetime &bound) override
+  {
+    HIR::Lifetime lifetime = lower_lifetime (bound);
+    translated = new HIR::Lifetime (lifetime);
+  }
+
+private:
+  ASTLoweringTypeBounds () : ASTLoweringBase (), translated (nullptr) {}
+
+  HIR::TypeParamBound *translated;
+};
+
+class ASTLowerWhereClauseItem : public ASTLoweringBase
+{
+  using Rust::HIR::ASTLoweringBase::visit;
+
+public:
+  static HIR::WhereClauseItem *translate (AST::WhereClauseItem &item)
+  {
+    ASTLowerWhereClauseItem compiler;
+    item.accept_vis (compiler);
+
+    rust_assert (compiler.translated != nullptr);
+    // FIXME
+    // compiler.mappings->insert_location (
+    //   compiler.translated->get_mappings ().get_hirid (),
+    //   compiler.translated->get_locus ());
+
+    return compiler.translated;
+  }
+
+  void visit (AST::LifetimeWhereClauseItem &item) override
+  {
+    HIR::Lifetime l = lower_lifetime (item.get_lifetime ());
+    std::vector<HIR::Lifetime> lifetime_bounds;
+    for (auto &lifetime_bound : item.get_lifetime_bounds ())
+      {
+	HIR::Lifetime ll = lower_lifetime (lifetime_bound);
+	lifetime_bounds.push_back (std::move (ll));
+      }
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, item.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   UNKNOWN_LOCAL_DEFID);
+
+    translated = new HIR::LifetimeWhereClauseItem (mapping, std::move (l),
+						   std::move (lifetime_bounds),
+						   item.get_locus ());
+  }
+
+  void visit (AST::TypeBoundWhereClauseItem &item) override
+  {
+    // FIXME
+    std::vector<HIR::LifetimeParam> for_lifetimes;
+
+    std::unique_ptr<HIR::Type> bound_type = std::unique_ptr<HIR::Type> (
+      ASTLoweringType::translate (item.get_type ().get ()));
+
+    std::vector<std::unique_ptr<HIR::TypeParamBound>> type_param_bounds;
+    for (auto &bound : item.get_type_param_bounds ())
+      {
+	HIR::TypeParamBound *b
+	  = ASTLoweringTypeBounds::translate (bound.get ());
+	type_param_bounds.push_back (std::unique_ptr<HIR::TypeParamBound> (b));
+      }
+
+    auto crate_num = mappings->get_current_crate ();
+    Analysis::NodeMapping mapping (crate_num, item.get_node_id (),
+				   mappings->get_next_hir_id (crate_num),
+				   UNKNOWN_LOCAL_DEFID);
+
+    translated
+      = new HIR::TypeBoundWhereClauseItem (mapping, std::move (for_lifetimes),
+					   std::move (bound_type),
+					   std::move (type_param_bounds),
+					   item.get_locus ());
+  }
+
+private:
+  ASTLowerWhereClauseItem () : ASTLoweringBase (), translated (nullptr) {}
+
+  HIR::WhereClauseItem *translated;
+};
+
+} // namespace HIR
+} // namespace Rust
+
+#endif // RUST_AST_LOWER_TYPE
diff --git a/gcc/rust/hir/rust-ast-lower.cc b/gcc/rust/hir/rust-ast-lower.cc
new file mode 100644
index 00000000000..0bec8b088af
--- /dev/null
+++ b/gcc/rust/hir/rust-ast-lower.cc
@@ -0,0 +1,477 @@
+// 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/>.
+
+#include "rust-ast-lower.h"
+#include "rust-ast-lower-item.h"
+#include "rust-ast-lower-implitem.h"
+#include "rust-ast-lower-expr.h"
+#include "rust-ast-lower-block.h"
+#include "rust-ast-lower-type.h"
+
+namespace Rust {
+namespace HIR {
+
+Visibility
+translate_visibility (const AST::Visibility &vis)
+{
+  // FIXME: How do we create a private visibility here? Is it always private if
+  // the AST vis is an error?
+  // FIXME: We need to add a `create_private()` static function to the
+  // AST::Visibility class and use it when the vis is empty in the parser...
+  if (vis.is_error ())
+    return Visibility::create_error ();
+
+  switch (vis.get_public_vis_type ())
+    {
+    case AST::Visibility::PUB:
+      return Visibility (Visibility::VisType::PUBLIC);
+    case AST::Visibility::PRIV:
+    case AST::Visibility::PUB_SELF:
+      return Visibility (Visibility::VisType::PRIVATE);
+    case AST::Visibility::PUB_CRATE:
+    case AST::Visibility::PUB_SUPER:
+    case AST::Visibility::PUB_IN_PATH:
+      return Visibility (Visibility::VisType::RESTRICTED,
+			 ASTLoweringSimplePath::translate (vis.get_path ()));
+      break;
+    }
+
+  return Visibility::create_error ();
+}
+
+ASTLowering::ASTLowering (AST::Crate &astCrate) : astCrate (astCrate) {}
+
+ASTLowering::~ASTLowering () {}
+
+std::unique_ptr<HIR::Crate>
+ASTLowering::Resolve (AST::Crate &astCrate)
+{
+  ASTLowering resolver (astCrate);
+  return resolver.go ();
+}
+
+std::unique_ptr<HIR::Crate>
+ASTLowering::go ()
+{
+  std::vector<std::unique_ptr<HIR::Item> > items;
+
+  for (auto it = astCrate.items.begin (); it != astCrate.items.end (); it++)
+    {
+      auto translated = ASTLoweringItem::translate (it->get ());
+      if (translated != nullptr)
+	items.push_back (std::unique_ptr<HIR::Item> (translated));
+    }
+
+  auto mappings = Analysis::Mappings::get ();
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, astCrate.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  return std::unique_ptr<HIR::Crate> (
+    new HIR::Crate (std::move (items), astCrate.get_inner_attrs (), mapping));
+}
+
+// rust-ast-lower-block.h
+void
+ASTLoweringBlock::visit (AST::BlockExpr &expr)
+{
+  std::vector<std::unique_ptr<HIR::Stmt> > block_stmts;
+  bool block_did_terminate = false;
+
+  for (auto &s : expr.get_statements ())
+    {
+      if (s->get_ast_kind () == AST::Kind::MACRO_RULES_DEFINITION)
+	continue;
+
+      if (s->get_ast_kind () == AST::Kind::MACRO_INVOCATION)
+	rust_fatal_error (
+	  s->get_locus (),
+	  "macro invocations should not get lowered to HIR - At "
+	  "this point in "
+	  "the pipeline, they should all have been expanded");
+
+      if (block_did_terminate)
+	rust_warning_at (s->get_locus (), 0, "unreachable statement");
+
+      bool terminated = false;
+      auto translated_stmt = ASTLoweringStmt::translate (s.get (), &terminated);
+      block_stmts.push_back (std::unique_ptr<HIR::Stmt> (translated_stmt));
+      block_did_terminate |= terminated;
+    }
+
+  if (expr.has_tail_expr () && block_did_terminate)
+    {
+      // warning unreachable tail expressions
+      rust_warning_at (expr.get_tail_expr ()->get_locus (), 0,
+		       "unreachable expression");
+    }
+
+  HIR::ExprWithoutBlock *tail_expr = nullptr;
+  if (expr.has_tail_expr ())
+    {
+      bool terminated = false;
+      tail_expr = (HIR::ExprWithoutBlock *)
+	ASTLoweringExpr::translate (expr.get_tail_expr ().get (), &terminated);
+      block_did_terminate |= terminated;
+    }
+
+  bool tail_reachable = !block_did_terminate;
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+  translated
+    = new HIR::BlockExpr (mapping, std::move (block_stmts),
+			  std::unique_ptr<HIR::ExprWithoutBlock> (tail_expr),
+			  tail_reachable, expr.get_inner_attrs (),
+			  expr.get_outer_attrs (), expr.get_start_locus (),
+			  expr.get_end_locus ());
+
+  terminated = block_did_terminate;
+}
+
+void
+ASTLoweringIfBlock::visit (AST::IfExpr &expr)
+{
+  bool ignored_terminated = false;
+  HIR::Expr *condition
+    = ASTLoweringExpr::translate (expr.get_condition_expr ().get (),
+				  &ignored_terminated);
+  HIR::BlockExpr *block
+    = ASTLoweringBlock::translate (expr.get_if_block ().get (),
+				   &ignored_terminated);
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated = new HIR::IfExpr (mapping, std::unique_ptr<HIR::Expr> (condition),
+				std::unique_ptr<HIR::BlockExpr> (block),
+				expr.get_locus ());
+}
+
+void
+ASTLoweringIfBlock::visit (AST::IfExprConseqElse &expr)
+{
+  HIR::Expr *condition
+    = ASTLoweringExpr::translate (expr.get_condition_expr ().get ());
+
+  bool if_block_terminated = false;
+  bool else_block_termianted = false;
+
+  HIR::BlockExpr *if_block
+    = ASTLoweringBlock::translate (expr.get_if_block ().get (),
+				   &if_block_terminated);
+  HIR::BlockExpr *else_block
+    = ASTLoweringBlock::translate (expr.get_else_block ().get (),
+				   &else_block_termianted);
+
+  terminated = if_block_terminated && else_block_termianted;
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated
+    = new HIR::IfExprConseqElse (mapping,
+				 std::unique_ptr<HIR::Expr> (condition),
+				 std::unique_ptr<HIR::BlockExpr> (if_block),
+				 std::unique_ptr<HIR::BlockExpr> (else_block),
+				 expr.get_locus ());
+}
+
+void
+ASTLoweringIfBlock::visit (AST::IfExprConseqIf &expr)
+{
+  HIR::Expr *condition
+    = ASTLoweringExpr::translate (expr.get_condition_expr ().get ());
+
+  bool ignored_terminated = false;
+  HIR::BlockExpr *block
+    = ASTLoweringBlock::translate (expr.get_if_block ().get (),
+				   &ignored_terminated);
+  HIR::IfExpr *conseq_if_expr
+    = ASTLoweringIfBlock::translate (expr.get_conseq_if_expr ().get (),
+				     &ignored_terminated);
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated
+    = new HIR::IfExprConseqIf (mapping, std::unique_ptr<HIR::Expr> (condition),
+			       std::unique_ptr<HIR::BlockExpr> (block),
+			       std::unique_ptr<HIR::IfExpr> (conseq_if_expr),
+			       expr.get_locus ());
+}
+
+void
+ASTLoweringIfLetBlock::visit (AST::IfLetExpr &expr)
+{
+  std::vector<std::unique_ptr<HIR::Pattern> > patterns;
+  for (auto &pattern : expr.get_patterns ())
+    {
+      HIR::Pattern *ptrn = ASTLoweringPattern::translate (pattern.get ());
+      patterns.push_back (std::unique_ptr<HIR::Pattern> (ptrn));
+    }
+  HIR::Expr *value_ptr
+    = ASTLoweringExpr::translate (expr.get_value_expr ().get ());
+
+  bool ignored_terminated = false;
+  HIR::BlockExpr *block
+    = ASTLoweringBlock::translate (expr.get_if_block ().get (),
+				   &ignored_terminated);
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated = new HIR::IfLetExpr (mapping, std::move (patterns),
+				   std::unique_ptr<HIR::Expr> (value_ptr),
+				   std::unique_ptr<HIR::BlockExpr> (block),
+				   expr.get_locus ());
+}
+
+// rust-ast-lower-struct-field-expr.h
+
+void
+ASTLowerStructExprField::visit (AST::StructExprFieldIdentifierValue &field)
+{
+  HIR::Expr *value = ASTLoweringExpr::translate (field.get_value ().get ());
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, field.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated = new HIR::StructExprFieldIdentifierValue (
+    mapping, field.get_field_name (), std::unique_ptr<HIR::Expr> (value),
+    field.get_locus ());
+}
+
+void
+ASTLowerStructExprField::visit (AST::StructExprFieldIndexValue &field)
+{
+  HIR::Expr *value = ASTLoweringExpr::translate (field.get_value ().get ());
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, field.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated
+    = new HIR::StructExprFieldIndexValue (mapping, field.get_index (),
+					  std::unique_ptr<HIR::Expr> (value),
+					  field.get_locus ());
+}
+
+void
+ASTLowerStructExprField::visit (AST::StructExprFieldIdentifier &field)
+{
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, field.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated
+    = new HIR::StructExprFieldIdentifier (mapping, field.get_field_name (),
+					  field.get_locus ());
+}
+
+// rust-ast-lower-block.h
+
+void
+ASTLoweringExprWithBlock::visit (AST::WhileLoopExpr &expr)
+{
+  HIR::BlockExpr *loop_block
+    = ASTLoweringBlock::translate (expr.get_loop_block ().get (), &terminated);
+
+  HIR::LoopLabel loop_label = lower_loop_label (expr.get_loop_label ());
+  HIR::Expr *loop_condition
+    = ASTLoweringExpr::translate (expr.get_predicate_expr ().get (),
+				  &terminated);
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated
+    = new HIR::WhileLoopExpr (mapping,
+			      std::unique_ptr<HIR::Expr> (loop_condition),
+			      std::unique_ptr<HIR::BlockExpr> (loop_block),
+			      expr.get_locus (), std::move (loop_label),
+			      expr.get_outer_attrs ());
+}
+
+void
+ASTLoweringExprWithBlock::visit (AST::ForLoopExpr &expr)
+{
+  HIR::BlockExpr *loop_block
+    = ASTLoweringBlock::translate (expr.get_loop_block ().get (), &terminated);
+  HIR::LoopLabel loop_label = lower_loop_label (expr.get_loop_label ());
+  HIR::Expr *iterator_expr
+    = ASTLoweringExpr::translate (expr.get_iterator_expr ().get (),
+				  &terminated);
+  HIR::Pattern *loop_pattern
+    = ASTLoweringPattern::translate (expr.get_pattern ().get ());
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated
+    = new HIR::ForLoopExpr (mapping,
+			    std::unique_ptr<HIR::Pattern> (loop_pattern),
+			    std::unique_ptr<HIR::Expr> (iterator_expr),
+			    std::unique_ptr<HIR::BlockExpr> (loop_block),
+			    expr.get_locus (), std::move (loop_label),
+			    expr.get_outer_attrs ());
+}
+
+void
+ASTLoweringExprWithBlock::visit (AST::MatchExpr &expr)
+{
+  HIR::Expr *branch_value
+    = ASTLoweringExpr::translate (expr.get_scrutinee_expr ().get ());
+
+  std::vector<HIR::MatchCase> match_arms;
+  for (auto &match_case : expr.get_match_cases ())
+    {
+      HIR::Expr *kase_expr
+	= ASTLoweringExpr::translate (match_case.get_expr ().get ());
+
+      HIR::Expr *kase_guard_expr = nullptr;
+      if (match_case.get_arm ().has_match_arm_guard ())
+	{
+	  kase_guard_expr = ASTLoweringExpr::translate (
+	    match_case.get_arm ().get_guard_expr ().get ());
+	}
+
+      std::vector<std::unique_ptr<HIR::Pattern> > match_arm_patterns;
+      for (auto &pattern : match_case.get_arm ().get_patterns ())
+	{
+	  HIR::Pattern *ptrn = ASTLoweringPattern::translate (pattern.get ());
+	  match_arm_patterns.push_back (std::unique_ptr<HIR::Pattern> (ptrn));
+	}
+
+      HIR::MatchArm arm (std::move (match_arm_patterns), expr.get_locus (),
+			 std::unique_ptr<HIR::Expr> (kase_guard_expr),
+			 match_case.get_arm ().get_outer_attrs ());
+
+      auto crate_num = mappings->get_current_crate ();
+      Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				     mappings->get_next_hir_id (crate_num),
+				     UNKNOWN_LOCAL_DEFID);
+
+      HIR::MatchCase kase (std::move (mapping), std::move (arm),
+			   std::unique_ptr<HIR::Expr> (kase_expr));
+      match_arms.push_back (std::move (kase));
+    }
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated
+    = new HIR::MatchExpr (mapping, std::unique_ptr<HIR::Expr> (branch_value),
+			  std::move (match_arms), expr.get_inner_attrs (),
+			  expr.get_outer_attrs (), expr.get_locus ());
+}
+
+// rust-ast-lower-expr.h
+
+void
+ASTLowerPathInExpression::visit (AST::PathInExpression &expr)
+{
+  std::vector<HIR::PathExprSegment> path_segments;
+  auto &segments = expr.get_segments ();
+  for (auto &s : segments)
+    {
+      path_segments.push_back (lower_path_expr_seg ((s)));
+
+      // insert the mappings for the segment
+      HIR::PathExprSegment *lowered_seg = &path_segments.back ();
+      mappings->insert_hir_path_expr_seg (lowered_seg);
+    }
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated = new HIR::PathInExpression (mapping, std::move (path_segments),
+					  expr.get_locus (),
+					  expr.opening_scope_resolution ());
+}
+
+HIR::QualifiedPathType
+ASTLoweringBase::lower_qual_path_type (AST::QualifiedPathType &qualified_type)
+{
+  HIR::Type *type
+    = ASTLoweringType::translate (qualified_type.get_type ().get ());
+  HIR::TypePath *trait
+    = qualified_type.has_as_clause ()
+	? ASTLowerTypePath::translate (qualified_type.get_as_type_path ())
+	: nullptr;
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, qualified_type.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  return HIR::QualifiedPathType (mapping, std::unique_ptr<HIR::Type> (type),
+				 std::unique_ptr<HIR::TypePath> (trait),
+				 qualified_type.get_locus ());
+}
+
+void
+ASTLowerQualPathInExpression::visit (AST::QualifiedPathInExpression &expr)
+{
+  HIR::QualifiedPathType qual_path_type
+    = lower_qual_path_type (expr.get_qualified_path_type ());
+
+  std::vector<HIR::PathExprSegment> path_segments;
+  auto &segments = expr.get_segments ();
+  for (auto &s : segments)
+    {
+      path_segments.push_back (lower_path_expr_seg ((s)));
+
+      // insert the mappings for the segment
+      HIR::PathExprSegment *lowered_seg = &path_segments.back ();
+      mappings->insert_hir_path_expr_seg (lowered_seg);
+    }
+
+  auto crate_num = mappings->get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+				 mappings->get_next_hir_id (crate_num),
+				 UNKNOWN_LOCAL_DEFID);
+
+  translated = new HIR::QualifiedPathInExpression (mapping, qual_path_type,
+						   std::move (path_segments),
+						   expr.get_locus (),
+						   expr.get_outer_attrs ());
+}
+} // namespace HIR
+} // namespace Rust
diff --git a/gcc/rust/hir/rust-ast-lower.h b/gcc/rust/hir/rust-ast-lower.h
new file mode 100644
index 00000000000..e726b4b8282
--- /dev/null
+++ b/gcc/rust/hir/rust-ast-lower.h
@@ -0,0 +1,59 @@
+// 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_HIR_LOWER
+#define RUST_HIR_LOWER
+
+#include "rust-system.h"
+#include "rust-ast-full.h"
+#include "rust-ast-visitor.h"
+#include "rust-hir-full.h"
+
+namespace Rust {
+namespace HIR {
+
+/* Checks whether the name of a field already exists.  Returns true
+   and produces an error if so.  */
+bool
+struct_field_name_exists (std::vector<HIR::StructField> &fields,
+			  HIR::StructField &new_field);
+
+/**
+ * Lowers a Visibility from the AST into an HIR Visibility, desugaring it in
+ * the process
+ */
+Visibility
+translate_visibility (const AST::Visibility &vis);
+
+class ASTLowering
+{
+public:
+  static std::unique_ptr<HIR::Crate> Resolve (AST::Crate &astCrate);
+  ~ASTLowering ();
+
+private:
+  ASTLowering (AST::Crate &astCrate);
+  std::unique_ptr<HIR::Crate> go ();
+
+  AST::Crate &astCrate;
+};
+
+} // namespace HIR
+} // namespace Rust
+
+#endif // RUST_HIR_LOWER
diff --git a/gcc/rust/hir/rust-hir-dump.cc b/gcc/rust/hir/rust-hir-dump.cc
new file mode 100644
index 00000000000..bb139a7c1b7
--- /dev/null
+++ b/gcc/rust/hir/rust-hir-dump.cc
@@ -0,0 +1,521 @@
+// 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/>.
+
+#include "rust-hir-dump.h"
+
+namespace Rust {
+namespace HIR {
+
+Dump::Dump (std::ostream &stream) : stream (stream), indent (0) {}
+
+void
+Dump::go (HIR::Crate &crate)
+{
+  stream << "Crate"
+	 << " "
+	 << "{" << std::endl;
+  //
+
+  indent++;
+  stream << std::string (indent, indent_char);
+  stream << "inner_attrs"
+	 << ":"
+	 << " "
+	 << "[";
+  for (auto &attr : crate.inner_attrs)
+    stream << attr.as_string ();
+  stream << "]"
+	 << "," << std::endl;
+  indent--;
+
+  indent++;
+  stream << std::string (indent, indent_char);
+  //
+
+  stream << "items"
+	 << ":"
+	 << " "
+	 << "[";
+
+  stream << std::string (indent, indent_char);
+  for (const auto &item : crate.items)
+    {
+      stream << std::endl;
+      item->accept_vis (*this);
+    }
+  stream << std::string (indent, indent_char);
+  stream << "]"
+	 << "," << std::endl;
+  indent--;
+  //
+
+  indent++;
+  stream << std::string (indent, indent_char);
+  stream << "node_mappings"
+	 << ":"
+	 << " "
+	 << "[";
+  // TODO: print crate mapping attrs
+  stream << "]" << std::endl;
+  indent--;
+
+  stream << "}" << std::endl;
+}
+
+void
+Dump::visit (Lifetime &)
+{}
+void
+Dump::visit (LifetimeParam &)
+{}
+void
+Dump::visit (PathInExpression &)
+{}
+void
+Dump::visit (TypePathSegment &)
+{}
+void
+Dump::visit (TypePathSegmentGeneric &)
+{}
+void
+Dump::visit (TypePathSegmentFunction &)
+{}
+void
+Dump::visit (TypePath &)
+{}
+void
+Dump::visit (QualifiedPathInExpression &)
+{}
+void
+Dump::visit (QualifiedPathInType &)
+{}
+
+void
+Dump::visit (LiteralExpr &literal_expr)
+{
+  indent++;
+  stream << std::string (indent, indent_char);
+  stream << "( " + literal_expr.get_literal ().as_string () + " ("
+	      + literal_expr.get_mappings ().as_string () + "))";
+  stream << "\n";
+}
+void
+Dump::visit (BorrowExpr &)
+{}
+void
+Dump::visit (DereferenceExpr &)
+{}
+void
+Dump::visit (ErrorPropagationExpr &)
+{}
+void
+Dump::visit (NegationExpr &)
+{}
+void
+Dump::visit (ArithmeticOrLogicalExpr &)
+{}
+void
+Dump::visit (ComparisonExpr &)
+{}
+void
+Dump::visit (LazyBooleanExpr &)
+{}
+void
+Dump::visit (TypeCastExpr &)
+{}
+void
+Dump::visit (AssignmentExpr &)
+{}
+void
+Dump::visit (CompoundAssignmentExpr &)
+{}
+void
+Dump::visit (GroupedExpr &)
+{}
+
+void
+Dump::visit (ArrayElemsValues &)
+{}
+void
+Dump::visit (ArrayElemsCopied &)
+{}
+void
+Dump::visit (ArrayExpr &)
+{}
+void
+Dump::visit (ArrayIndexExpr &)
+{}
+void
+Dump::visit (TupleExpr &)
+{}
+void
+Dump::visit (TupleIndexExpr &)
+{}
+void
+Dump::visit (StructExprStruct &)
+{}
+
+void
+Dump::visit (StructExprFieldIdentifier &)
+{}
+void
+Dump::visit (StructExprFieldIdentifierValue &)
+{}
+
+void
+Dump::visit (StructExprFieldIndexValue &)
+{}
+void
+Dump::visit (StructExprStructFields &)
+{}
+void
+Dump::visit (StructExprStructBase &)
+{}
+
+void
+Dump::visit (CallExpr &)
+{}
+void
+Dump::visit (MethodCallExpr &)
+{}
+void
+Dump::visit (FieldAccessExpr &)
+{}
+void
+Dump::visit (ClosureExprInner &)
+{}
+void
+Dump::visit (BlockExpr &block_expr)
+{
+  stream << "BlockExpr"
+	 << ":"
+	 << " "
+	 << "[";
+  indent++;
+  // TODO: print statements
+  // TODO: print tail expression if exists
+  stream << "]";
+  indent--;
+}
+void
+Dump::visit (ClosureExprInnerTyped &)
+{}
+void
+Dump::visit (ContinueExpr &)
+{}
+void
+Dump::visit (BreakExpr &)
+{}
+void
+Dump::visit (RangeFromToExpr &)
+{}
+void
+Dump::visit (RangeFromExpr &)
+{}
+void
+Dump::visit (RangeToExpr &)
+{}
+void
+Dump::visit (RangeFullExpr &)
+{}
+void
+Dump::visit (RangeFromToInclExpr &)
+{}
+void
+Dump::visit (RangeToInclExpr &)
+{}
+void
+Dump::visit (ReturnExpr &)
+{}
+void
+Dump::visit (UnsafeBlockExpr &)
+{}
+void
+Dump::visit (LoopExpr &)
+{}
+void
+Dump::visit (WhileLoopExpr &)
+{}
+void
+Dump::visit (WhileLetLoopExpr &)
+{}
+void
+Dump::visit (ForLoopExpr &)
+{}
+void
+Dump::visit (IfExpr &)
+{}
+void
+Dump::visit (IfExprConseqElse &)
+{}
+void
+Dump::visit (IfExprConseqIf &)
+{}
+void
+Dump::visit (IfExprConseqIfLet &)
+{}
+void
+Dump::visit (IfLetExpr &)
+{}
+void
+Dump::visit (IfLetExprConseqElse &)
+{}
+void
+Dump::visit (IfLetExprConseqIf &)
+{}
+void
+Dump::visit (IfLetExprConseqIfLet &)
+{}
+
+void
+Dump::visit (MatchExpr &)
+{}
+void
+Dump::visit (AwaitExpr &)
+{}
+void
+Dump::visit (AsyncBlockExpr &)
+{}
+
+void
+Dump::visit (TypeParam &)
+{}
+
+void
+Dump::visit (ConstGenericParam &)
+{}
+
+void
+Dump::visit (LifetimeWhereClauseItem &)
+{}
+void
+Dump::visit (TypeBoundWhereClauseItem &)
+{}
+void
+Dump::visit (Module &)
+{}
+void
+Dump::visit (ExternCrate &)
+{}
+
+void
+Dump::visit (UseTreeGlob &)
+{}
+void
+Dump::visit (UseTreeList &)
+{}
+void
+Dump::visit (UseTreeRebind &)
+{}
+void
+Dump::visit (UseDeclaration &)
+{}
+void
+Dump::visit (Function &function)
+{
+  indent++;
+  stream << std::string (indent, indent_char);
+  stream << "Function"
+	 << " ";
+  stream << "{" << std::endl;
+  // TODO: print function params
+  stream << std::string (indent, indent_char);
+  stream << "}" << std::endl;
+  // TODO: get function definition and visit block
+
+  stream << std::endl;
+  indent--;
+}
+void
+Dump::visit (TypeAlias &)
+{}
+void
+Dump::visit (StructStruct &)
+{}
+void
+Dump::visit (TupleStruct &)
+{}
+void
+Dump::visit (EnumItem &)
+{}
+void
+Dump::visit (EnumItemTuple &)
+{}
+void
+Dump::visit (EnumItemStruct &)
+{}
+void
+Dump::visit (EnumItemDiscriminant &)
+{}
+void
+Dump::visit (Enum &)
+{}
+void
+Dump::visit (Union &)
+{}
+void
+Dump::visit (ConstantItem &)
+{}
+void
+Dump::visit (StaticItem &)
+{}
+void
+Dump::visit (TraitItemFunc &)
+{}
+void
+Dump::visit (TraitItemConst &)
+{}
+void
+Dump::visit (TraitItemType &)
+{}
+void
+Dump::visit (Trait &)
+{}
+void
+Dump::visit (ImplBlock &)
+{}
+
+void
+Dump::visit (ExternalStaticItem &)
+{}
+void
+Dump::visit (ExternalFunctionItem &)
+{}
+void
+Dump::visit (ExternBlock &)
+{}
+
+void
+Dump::visit (LiteralPattern &)
+{}
+void
+Dump::visit (IdentifierPattern &)
+{}
+void
+Dump::visit (WildcardPattern &)
+{}
+
+void
+Dump::visit (RangePatternBoundLiteral &)
+{}
+void
+Dump::visit (RangePatternBoundPath &)
+{}
+void
+Dump::visit (RangePatternBoundQualPath &)
+{}
+void
+Dump::visit (RangePattern &)
+{}
+void
+Dump::visit (ReferencePattern &)
+{}
+
+void
+Dump::visit (StructPatternFieldTuplePat &)
+{}
+void
+Dump::visit (StructPatternFieldIdentPat &)
+{}
+void
+Dump::visit (StructPatternFieldIdent &)
+{}
+void
+Dump::visit (StructPattern &)
+{}
+
+void
+Dump::visit (TupleStructItemsNoRange &)
+{}
+void
+Dump::visit (TupleStructItemsRange &)
+{}
+void
+Dump::visit (TupleStructPattern &)
+{}
+
+void
+Dump::visit (TuplePatternItemsMultiple &)
+{}
+void
+Dump::visit (TuplePatternItemsRanged &)
+{}
+void
+Dump::visit (TuplePattern &)
+{}
+void
+Dump::visit (GroupedPattern &)
+{}
+void
+Dump::visit (SlicePattern &)
+{}
+
+void
+Dump::visit (EmptyStmt &)
+{}
+void
+Dump::visit (LetStmt &)
+{}
+void
+Dump::visit (ExprStmtWithoutBlock &)
+{}
+void
+Dump::visit (ExprStmtWithBlock &)
+{}
+
+void
+Dump::visit (TraitBound &)
+{}
+void
+Dump::visit (ImplTraitType &)
+{}
+void
+Dump::visit (TraitObjectType &)
+{}
+void
+Dump::visit (ParenthesisedType &)
+{}
+void
+Dump::visit (ImplTraitTypeOneBound &)
+{}
+void
+Dump::visit (TupleType &)
+{}
+void
+Dump::visit (NeverType &)
+{}
+void
+Dump::visit (RawPointerType &)
+{}
+void
+Dump::visit (ReferenceType &)
+{}
+void
+Dump::visit (ArrayType &)
+{}
+void
+Dump::visit (SliceType &)
+{}
+void
+Dump::visit (InferredType &)
+{}
+void
+Dump::visit (BareFunctionType &)
+{}
+} // namespace HIR
+} // namespace Rust
diff --git a/gcc/rust/hir/rust-hir-dump.h b/gcc/rust/hir/rust-hir-dump.h
new file mode 100644
index 00000000000..8b9e8939a28
--- /dev/null
+++ b/gcc/rust/hir/rust-hir-dump.h
@@ -0,0 +1,193 @@
+// 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_HIR_DUMP_H
+#define RUST_HIR_DUMP_H
+
+#include "rust-hir-visitor.h"
+#include "rust-hir.h"
+#include "rust-hir-full.h"
+
+namespace Rust {
+namespace HIR {
+
+class Dump : public HIRFullVisitor
+{
+public:
+  Dump (std::ostream &stream);
+  void go (HIR::Crate &crate);
+
+private:
+  std::ostream &stream;
+  std::size_t indent; // current indentation level
+  char indent_char = '\t';
+
+  virtual void visit (Lifetime &) override;
+  virtual void visit (LifetimeParam &) override;
+  virtual void visit (PathInExpression &) override;
+  virtual void visit (TypePathSegment &) override;
+  virtual void visit (TypePathSegmentGeneric &) override;
+  virtual void visit (TypePathSegmentFunction &) override;
+  virtual void visit (TypePath &) override;
+  virtual void visit (QualifiedPathInExpression &) override;
+  virtual void visit (QualifiedPathInType &) override;
+
+  virtual void visit (LiteralExpr &) override;
+  virtual void visit (BorrowExpr &) override;
+  virtual void visit (DereferenceExpr &) override;
+  virtual void visit (ErrorPropagationExpr &) override;
+  virtual void visit (NegationExpr &) override;
+  virtual void visit (ArithmeticOrLogicalExpr &) override;
+  virtual void visit (ComparisonExpr &) override;
+  virtual void visit (LazyBooleanExpr &) override;
+  virtual void visit (TypeCastExpr &) override;
+  virtual void visit (AssignmentExpr &) override;
+  virtual void visit (CompoundAssignmentExpr &) override;
+  virtual void visit (GroupedExpr &) override;
+
+  virtual void visit (ArrayElemsValues &) override;
+  virtual void visit (ArrayElemsCopied &) override;
+  virtual void visit (ArrayExpr &) override;
+  virtual void visit (ArrayIndexExpr &) override;
+  virtual void visit (TupleExpr &) override;
+  virtual void visit (TupleIndexExpr &) override;
+  virtual void visit (StructExprStruct &) override;
+
+  virtual void visit (StructExprFieldIdentifier &) override;
+  virtual void visit (StructExprFieldIdentifierValue &) override;
+
+  virtual void visit (StructExprFieldIndexValue &) override;
+  virtual void visit (StructExprStructFields &) override;
+  virtual void visit (StructExprStructBase &) override;
+
+  virtual void visit (CallExpr &) override;
+  virtual void visit (MethodCallExpr &) override;
+  virtual void visit (FieldAccessExpr &) override;
+  virtual void visit (ClosureExprInner &) override;
+  virtual void visit (BlockExpr &) override;
+  virtual void visit (ClosureExprInnerTyped &) override;
+  virtual void visit (ContinueExpr &) override;
+  virtual void visit (BreakExpr &) override;
+  virtual void visit (RangeFromToExpr &) override;
+  virtual void visit (RangeFromExpr &) override;
+  virtual void visit (RangeToExpr &) override;
+  virtual void visit (RangeFullExpr &) override;
+  virtual void visit (RangeFromToInclExpr &) override;
+  virtual void visit (RangeToInclExpr &) override;
+  virtual void visit (ReturnExpr &) override;
+  virtual void visit (UnsafeBlockExpr &) override;
+  virtual void visit (LoopExpr &) override;
+  virtual void visit (WhileLoopExpr &) override;
+  virtual void visit (WhileLetLoopExpr &) override;
+  virtual void visit (ForLoopExpr &) override;
+  virtual void visit (IfExpr &) override;
+  virtual void visit (IfExprConseqElse &) override;
+  virtual void visit (IfExprConseqIf &) override;
+  virtual void visit (IfExprConseqIfLet &) override;
+  virtual void visit (IfLetExpr &) override;
+  virtual void visit (IfLetExprConseqElse &) override;
+  virtual void visit (IfLetExprConseqIf &) override;
+  virtual void visit (IfLetExprConseqIfLet &) override;
+
+  virtual void visit (MatchExpr &) override;
+  virtual void visit (AwaitExpr &) override;
+  virtual void visit (AsyncBlockExpr &) override;
+
+  virtual void visit (TypeParam &) override;
+  virtual void visit (ConstGenericParam &) override;
+
+  virtual void visit (LifetimeWhereClauseItem &) override;
+  virtual void visit (TypeBoundWhereClauseItem &) override;
+  virtual void visit (Module &) override;
+  virtual void visit (ExternCrate &) override;
+
+  virtual void visit (UseTreeGlob &) override;
+  virtual void visit (UseTreeList &) override;
+  virtual void visit (UseTreeRebind &) override;
+  virtual void visit (UseDeclaration &) override;
+  virtual void visit (Function &) override;
+  virtual void visit (TypeAlias &) override;
+  virtual void visit (StructStruct &) override;
+  virtual void visit (TupleStruct &) override;
+  virtual void visit (EnumItem &) override;
+  virtual void visit (EnumItemTuple &) override;
+  virtual void visit (EnumItemStruct &) override;
+  virtual void visit (EnumItemDiscriminant &) override;
+  virtual void visit (Enum &) override;
+  virtual void visit (Union &) override;
+  virtual void visit (ConstantItem &) override;
+  virtual void visit (StaticItem &) override;
+  virtual void visit (TraitItemFunc &) override;
+  virtual void visit (TraitItemConst &) override;
+  virtual void visit (TraitItemType &) override;
+  virtual void visit (Trait &) override;
+  virtual void visit (ImplBlock &) override;
+
+  virtual void visit (ExternalStaticItem &) override;
+  virtual void visit (ExternalFunctionItem &) override;
+  virtual void visit (ExternBlock &) override;
+
+  virtual void visit (LiteralPattern &) override;
+  virtual void visit (IdentifierPattern &) override;
+  virtual void visit (WildcardPattern &) override;
+
+  virtual void visit (RangePatternBoundLiteral &) override;
+  virtual void visit (RangePatternBoundPath &) override;
+  virtual void visit (RangePatternBoundQualPath &) override;
+  virtual void visit (RangePattern &) override;
+  virtual void visit (ReferencePattern &) override;
+
+  virtual void visit (StructPatternFieldTuplePat &) override;
+  virtual void visit (StructPatternFieldIdentPat &) override;
+  virtual void visit (StructPatternFieldIdent &) override;
+  virtual void visit (StructPattern &) override;
+
+  virtual void visit (TupleStructItemsNoRange &) override;
+  virtual void visit (TupleStructItemsRange &) override;
+  virtual void visit (TupleStructPattern &) override;
+
+  virtual void visit (TuplePatternItemsMultiple &) override;
+  virtual void visit (TuplePatternItemsRanged &) override;
+  virtual void visit (TuplePattern &) override;
+  virtual void visit (GroupedPattern &) override;
+  virtual void visit (SlicePattern &) override;
+
+  virtual void visit (EmptyStmt &) override;
+  virtual void visit (LetStmt &) override;
+  virtual void visit (ExprStmtWithoutBlock &) override;
+  virtual void visit (ExprStmtWithBlock &) override;
+
+  virtual void visit (TraitBound &) override;
+  virtual void visit (ImplTraitType &) override;
+  virtual void visit (TraitObjectType &) override;
+  virtual void visit (ParenthesisedType &) override;
+  virtual void visit (ImplTraitTypeOneBound &) override;
+  virtual void visit (TupleType &) override;
+  virtual void visit (NeverType &) override;
+  virtual void visit (RawPointerType &) override;
+  virtual void visit (ReferenceType &) override;
+  virtual void visit (ArrayType &) override;
+  virtual void visit (SliceType &) override;
+  virtual void visit (InferredType &) override;
+  virtual void visit (BareFunctionType &) override;
+};
+
+} // namespace HIR
+} // namespace Rust
+
+#endif // !RUST_HIR_DUMP_H
-- 
2.25.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [PATCH Rust front-end v2 15/37] gccrs: Add wrapper for make_unique
  2022-08-24 11:59 Rust frontend patches v2 herron.philip
                   ` (13 preceding siblings ...)
  2022-08-24 11:59 ` [PATCH Rust front-end v2 14/37] gccrs: Add AST to HIR lowering pass herron.philip
@ 2022-08-24 11:59 ` herron.philip
  2022-08-24 11:59 ` [PATCH Rust front-end v2 16/37] gccrs: Add port of FNV hash used during legacy symbol mangling herron.philip
                   ` (22 subsequent siblings)
  37 siblings, 0 replies; 59+ messages in thread
From: herron.philip @ 2022-08-24 11:59 UTC (permalink / raw)
  To: gcc-patches; +Cc: gcc-rust, Philip Herron

From: Philip Herron <philip.herron@embecosm.com>

This is a wrapper for make_unique we can likely get rid of this as there
are other implementations available or simply keep using the unique_ptr
constructor.
---
 gcc/rust/util/rust-make-unique.h | 35 ++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)
 create mode 100644 gcc/rust/util/rust-make-unique.h

diff --git a/gcc/rust/util/rust-make-unique.h b/gcc/rust/util/rust-make-unique.h
new file mode 100644
index 00000000000..f33f9125f89
--- /dev/null
+++ b/gcc/rust/util/rust-make-unique.h
@@ -0,0 +1,35 @@
+// 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_MAKE_UNIQUE_H
+#define RUST_MAKE_UNIQUE_H
+
+#include <memory>
+
+namespace Rust {
+
+template <typename T, typename... Ts>
+std::unique_ptr<T>
+make_unique (Ts &&... params)
+{
+  return std::unique_ptr<T> (new T (std::forward<Ts> (params)...));
+}
+
+} // namespace Rust
+
+#endif // RUST_MAKE_UNIQUE_H
-- 
2.25.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [PATCH Rust front-end v2 16/37] gccrs: Add port of FNV hash used during legacy symbol mangling
  2022-08-24 11:59 Rust frontend patches v2 herron.philip
                   ` (14 preceding siblings ...)
  2022-08-24 11:59 ` [PATCH Rust front-end v2 15/37] gccrs: Add wrapper for make_unique herron.philip
@ 2022-08-24 11:59 ` herron.philip
  2022-08-24 11:59 ` [PATCH Rust front-end v2 17/37] gccrs: Add Rust ABI enum helpers herron.philip
                   ` (21 subsequent siblings)
  37 siblings, 0 replies; 59+ messages in thread
From: herron.philip @ 2022-08-24 11:59 UTC (permalink / raw)
  To: gcc-patches; +Cc: gcc-rust, Philip Herron

From: Philip Herron <philip.herron@embecosm.com>

This hash was ported from the go runime as we needed a hash for the legacy
symbol mangling system. Which means all symbols in Rust contain a hash of
some metadata for uniqueness on generic functions.
---
 gcc/rust/util/fnv-hash.h | 95 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 95 insertions(+)
 create mode 100644 gcc/rust/util/fnv-hash.h

diff --git a/gcc/rust/util/fnv-hash.h b/gcc/rust/util/fnv-hash.h
new file mode 100644
index 00000000000..78e54c99411
--- /dev/null
+++ b/gcc/rust/util/fnv-hash.h
@@ -0,0 +1,95 @@
+// 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_FNV_HASH_H
+#define RUST_FNV_HASH_H
+
+namespace Rust {
+namespace Hash {
+
+const uint64_t offset128Lower = 0x62b821756295c58d;
+const uint64_t offset128Higher = 0x6c62272e07bb0142;
+const uint64_t prime128Lower = 0x13b;
+const uint64_t prime128Shift = 24;
+
+// ported from https://github.com/golang/go/blob/master/src/hash/fnv/fnv.go
+class FNV128
+{
+public:
+  FNV128 () { reset (); }
+
+  void reset ()
+  {
+    buf[0] = offset128Higher;
+    buf[1] = offset128Lower;
+  }
+
+  void write (const unsigned char *in, size_t len)
+  {
+    for (size_t i = 0; i < len; i++)
+      {
+	unsigned char c = in[i];
+
+	// https://stackoverflow.com/questions/28868367/getting-the-high-part-of-64-bit-integer-multiplication
+	uint64_t a = prime128Lower;
+	uint64_t b = buf[1];
+
+	uint64_t a_lo = (uint32_t) a;
+	uint64_t a_hi = a >> 32;
+	uint64_t b_lo = (uint32_t) b;
+	uint64_t b_hi = b >> 32;
+
+	uint64_t a_x_b_hi = a_hi * b_hi;
+	uint64_t a_x_b_mid = a_hi * b_lo;
+	uint64_t b_x_a_mid = b_hi * a_lo;
+	uint64_t a_x_b_lo = a_lo * b_lo;
+
+	uint64_t carry_bit
+	  = ((uint64_t) (uint32_t) a_x_b_mid + (uint64_t) (uint32_t) b_x_a_mid
+	     + (a_x_b_lo >> 32))
+	    >> 32;
+
+	uint64_t multhi
+	  = a_x_b_hi + (a_x_b_mid >> 32) + (b_x_a_mid >> 32) + carry_bit;
+
+	uint64_t s0 = multhi;		      // high
+	uint64_t s1 = prime128Lower * buf[1]; // low
+
+	s0 += buf[1] << (prime128Shift + prime128Lower * buf[0]);
+
+	// Update the values
+	buf[1] = s1;
+	buf[0] = s0;
+	buf[1] ^= (uint64_t) c;
+      }
+  }
+
+  void sum (uint64_t *hi, uint64_t *lo) const
+  {
+    *hi = buf[0];
+    *lo = buf[1];
+  }
+
+private:
+  uint64_t buf[2];
+};
+
+} // namespace Hash
+} // namespace Rust
+
+#endif // RUST_FNV_HASH_H
-- 
2.25.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [PATCH Rust front-end v2 17/37] gccrs: Add Rust ABI enum helpers
  2022-08-24 11:59 Rust frontend patches v2 herron.philip
                   ` (15 preceding siblings ...)
  2022-08-24 11:59 ` [PATCH Rust front-end v2 16/37] gccrs: Add port of FNV hash used during legacy symbol mangling herron.philip
@ 2022-08-24 11:59 ` herron.philip
  2022-08-24 11:59 ` [PATCH Rust front-end v2 18/37] gccrs: Add Base62 implementation herron.philip
                   ` (20 subsequent siblings)
  37 siblings, 0 replies; 59+ messages in thread
From: herron.philip @ 2022-08-24 11:59 UTC (permalink / raw)
  To: gcc-patches; +Cc: gcc-rust, Philip Herron

From: Philip Herron <philip.herron@embecosm.com>

This is a simple helper over an enum of possible ABI options in Rust.
---
 gcc/rust/util/rust-abi.cc | 72 +++++++++++++++++++++++++++++++++++++++
 gcc/rust/util/rust-abi.h  | 45 ++++++++++++++++++++++++
 2 files changed, 117 insertions(+)
 create mode 100644 gcc/rust/util/rust-abi.cc
 create mode 100644 gcc/rust/util/rust-abi.h

diff --git a/gcc/rust/util/rust-abi.cc b/gcc/rust/util/rust-abi.cc
new file mode 100644
index 00000000000..6477c3790af
--- /dev/null
+++ b/gcc/rust/util/rust-abi.cc
@@ -0,0 +1,72 @@
+// 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/>.
+
+#include "rust-abi.h"
+
+namespace Rust {
+
+Rust::ABI
+get_abi_from_string (const std::string &abi)
+{
+  if (abi.compare ("rust") == 0)
+    return Rust::ABI::RUST;
+  else if (abi.compare ("rust-intrinsic") == 0)
+    return Rust::ABI::INTRINSIC;
+  else if (abi.compare ("C") == 0)
+    return Rust::ABI::C;
+  else if (abi.compare ("cdecl") == 0)
+    return Rust::ABI::CDECL;
+  else if (abi.compare ("stdcall") == 0)
+    return Rust::ABI::STDCALL;
+  else if (abi.compare ("fastcall") == 0)
+    return Rust::ABI::FASTCALL;
+  else if (abi.compare ("sysv64") == 0)
+    return Rust::ABI::SYSV64;
+  else if (abi.compare ("win64") == 0)
+    return Rust::ABI::WIN64;
+
+  return Rust::ABI::UNKNOWN;
+}
+
+std::string
+get_string_from_abi (Rust::ABI abi)
+{
+  switch (abi)
+    {
+    case Rust::ABI::RUST:
+      return "rust";
+    case Rust::ABI::INTRINSIC:
+      return "rust-intrinsic";
+    case Rust::ABI::C:
+      return "C";
+    case Rust::ABI::CDECL:
+      return "cdecl";
+    case Rust::ABI::STDCALL:
+      return "stdcall";
+    case Rust::ABI::FASTCALL:
+      return "fastcall";
+    case Rust::ABI::SYSV64:
+      return "sysv64";
+    case Rust::ABI::WIN64:
+      return "win64";
+
+    case Rust::ABI::UNKNOWN:
+      return "unknown";
+    }
+  return "unknown";
+}
+
+} // namespace Rust
diff --git a/gcc/rust/util/rust-abi.h b/gcc/rust/util/rust-abi.h
new file mode 100644
index 00000000000..d794cc35fb3
--- /dev/null
+++ b/gcc/rust/util/rust-abi.h
@@ -0,0 +1,45 @@
+// 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_ABI_OPTIONS_H
+#define RUST_ABI_OPTIONS_H
+
+#include "rust-system.h"
+
+namespace Rust {
+
+enum ABI
+{
+  UNKNOWN,
+  RUST,
+  INTRINSIC,
+  C,
+  CDECL,
+  STDCALL,
+  FASTCALL,
+  WIN64,
+  SYSV64
+};
+
+extern Rust::ABI
+get_abi_from_string (const std::string &abi);
+
+extern std::string
+get_string_from_abi (Rust::ABI abi);
+
+} // namespace Rust
+
+#endif // RUST_ABI_OPTIONS_H
-- 
2.25.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [PATCH Rust front-end v2 18/37] gccrs: Add Base62 implementation
  2022-08-24 11:59 Rust frontend patches v2 herron.philip
                   ` (16 preceding siblings ...)
  2022-08-24 11:59 ` [PATCH Rust front-end v2 17/37] gccrs: Add Rust ABI enum helpers herron.philip
@ 2022-08-24 11:59 ` herron.philip
  2022-08-24 11:59 ` [PATCH Rust front-end v2 19/37] gccrs: Add implementation of Optional herron.philip
                   ` (19 subsequent siblings)
  37 siblings, 0 replies; 59+ messages in thread
From: herron.philip @ 2022-08-24 11:59 UTC (permalink / raw)
  To: gcc-patches; +Cc: gcc-rust, Arthur Cohen

From: Arthur Cohen <arthur.cohen@embecosm.com>

Used for V0 symbol mangling scheme which.
---
 gcc/rust/util/rust-base62.cc | 46 ++++++++++++++++++++++++++++++++++++
 gcc/rust/util/rust-base62.h  | 34 ++++++++++++++++++++++++++
 2 files changed, 80 insertions(+)
 create mode 100644 gcc/rust/util/rust-base62.cc
 create mode 100644 gcc/rust/util/rust-base62.h

diff --git a/gcc/rust/util/rust-base62.cc b/gcc/rust/util/rust-base62.cc
new file mode 100644
index 00000000000..bdab23338c3
--- /dev/null
+++ b/gcc/rust/util/rust-base62.cc
@@ -0,0 +1,46 @@
+// 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/>.
+
+#include "rust-base62.h"
+
+namespace Rust {
+
+std::string
+base62_integer (uint64_t value)
+{
+  const static std::string base_64
+    = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@$";
+  std::string buffer (128, '\0');
+  size_t idx = 0;
+  size_t base = 62;
+
+  do
+    {
+      buffer[idx] = base_64[(value % base)];
+      idx++;
+      value = value / base;
+    }
+  while (value != 0);
+
+  std::reverse (buffer.begin (), buffer.begin () + idx);
+  return buffer.substr (0, idx);
+}
+
+} // namespace Rust
+
+// FIXME: Add unit testing using the selftest framework
diff --git a/gcc/rust/util/rust-base62.h b/gcc/rust/util/rust-base62.h
new file mode 100644
index 00000000000..fa610d3e5a4
--- /dev/null
+++ b/gcc/rust/util/rust-base62.h
@@ -0,0 +1,34 @@
+// 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_BASE62_H
+#define RUST_BASE62_H
+
+#include "rust-system.h"
+
+namespace Rust {
+
+/**
+ * Get the Base62 representation of an integer
+ */
+std::string
+base62_integer (uint64_t value);
+
+} // namespace Rust
+
+#endif /* !RUST_BASE62_H */
-- 
2.25.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [PATCH Rust front-end v2 19/37] gccrs: Add implementation of Optional
  2022-08-24 11:59 Rust frontend patches v2 herron.philip
                   ` (17 preceding siblings ...)
  2022-08-24 11:59 ` [PATCH Rust front-end v2 18/37] gccrs: Add Base62 implementation herron.philip
@ 2022-08-24 11:59 ` herron.philip
  2022-08-24 11:59 ` [PATCH Rust front-end v2 20/37] gccrs: Add attributes checker herron.philip
                   ` (18 subsequent siblings)
  37 siblings, 0 replies; 59+ messages in thread
From: herron.philip @ 2022-08-24 11:59 UTC (permalink / raw)
  To: gcc-patches; +Cc: gcc-rust, Arthur Cohen

From: Arthur Cohen <arthur.cohen@embecosm.com>

Add an Optional<T> class to improve error handling
---
 gcc/rust/util/rust-optional-test.cc | 111 +++++++++++
 gcc/rust/util/rust-optional.h       | 278 ++++++++++++++++++++++++++++
 2 files changed, 389 insertions(+)
 create mode 100644 gcc/rust/util/rust-optional-test.cc
 create mode 100644 gcc/rust/util/rust-optional.h

diff --git a/gcc/rust/util/rust-optional-test.cc b/gcc/rust/util/rust-optional-test.cc
new file mode 100644
index 00000000000..9d5b4ba5735
--- /dev/null
+++ b/gcc/rust/util/rust-optional-test.cc
@@ -0,0 +1,111 @@
+// 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/>.
+
+#include "rust-optional.h"
+
+#include "config.h"
+#include "selftest.h"
+
+#if CHECKING_P
+
+static void
+rust_optional_create ()
+{
+  auto opt = Rust::Optional<int>::some (15);
+
+  ASSERT_TRUE (opt.is_some ());
+  ASSERT_EQ (opt.get (), 15);
+
+  Rust::Optional<int> const_opt = Rust::Optional<int>::some (15);
+  const int &value = const_opt.get ();
+
+  ASSERT_EQ (value, 15);
+}
+
+static void
+rust_optional_operators ()
+{
+  auto opt = Rust::Optional<int>::some (15);
+
+  // as bool
+  ASSERT_TRUE (opt);
+
+  // deref
+  ASSERT_EQ (*opt, 15);
+
+  class Methodable
+  {
+  public:
+    int method () { return 15; }
+  };
+
+  auto m_opt = Rust::Optional<Methodable>::some (Methodable ());
+  ASSERT_EQ (m_opt->method (), 15);
+}
+
+static void
+rust_optional_take ()
+{
+  auto opt = Rust::Optional<int>::some (15);
+  auto value = opt.take ();
+
+  ASSERT_EQ (value, 15);
+  ASSERT_TRUE (opt.is_none ());
+}
+
+static void
+rust_optional_map ()
+{
+  auto opt = Rust::Optional<int>::some (15);
+  auto twice = opt.map<int> ([] (int value) { return value * 2; });
+
+  ASSERT_FALSE (opt);
+  ASSERT_TRUE (twice);
+  ASSERT_EQ (*twice, 30);
+}
+
+static void
+rust_optional_reference ()
+{
+  auto value = std::vector<std::string> ();
+  value.emplace_back ("rust");
+  value.emplace_back ("+");
+  value.emplace_back ("gcc");
+  value.emplace_back ("=");
+  value.emplace_back ("<3");
+
+  auto opt = Rust::Optional<std::vector<std::string> &>::some (value);
+
+  ASSERT_EQ (opt->at (0), "rust");
+  ASSERT_EQ (opt->at (2), "gcc");
+}
+
+#endif /* #if CHECKING_P */
+
+void
+rust_optional_test ()
+{
+#if CHECKING_P
+  rust_optional_create ();
+  rust_optional_operators ();
+  rust_optional_take ();
+  rust_optional_map ();
+  rust_optional_reference ();
+
+#endif /* #if CHECKING_P */
+}
diff --git a/gcc/rust/util/rust-optional.h b/gcc/rust/util/rust-optional.h
new file mode 100644
index 00000000000..56465400250
--- /dev/null
+++ b/gcc/rust/util/rust-optional.h
@@ -0,0 +1,278 @@
+// 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_OPTIONAL_H
+#define RUST_OPTIONAL_H
+
+#include "config.h"
+#include "rust-system.h"
+
+#include "selftest.h"
+
+namespace Rust {
+
+/**
+ * Tagged union to try and simulate a sum type. This is safer and more ergonomic
+ * than one of the two alternatives we're currently using in the compiler:
+ *
+ * 1. Storing a raw pointer, which can be `nullptr` or valid
+ *
+ * This is wildly unsafe, and usable in conjunction with local references, stack
+ * variables, or pointers managed elsewhere, which can cause crashes, hard to
+ * debug issues or undefined behavior. Likewise, if you do not check for the
+ * pointer's validity, this will cause a crash.
+ *
+ * 2. Storing an extra boolean alongside the object
+ *
+ * This causes implementors to use a "dummy object": Either an empty version or
+ * an error version. But what happens if what you really wanted to store was
+ * the empty or error version? You can also easily incorporate logic bugs if you
+ * forget to check for the associated boolean.
+ *
+ * The `Optional<T>` type has the same "ergonomic" cost: You need to check
+ * whether your option is valid or not. However, the main advantage is that it
+ * is more restrictive: You can only acess the member it contains "safely".
+ * It is similar to storing a value + an associated boolean, but has the
+ * advantage of making up only one member in your class.
+ * You also benefit from some helper methods such as `map()`.
+ *
+ * You also get helper functions and operator overloading to "seamlessly"
+ * replace raw pointer alternatives.
+ *
+ * ```c++
+ * MyType *raw_pointer = something_that_can_fail();
+ * if (raw_pointer)
+ *     raw_pointer->method();
+ *
+ * // or
+ *
+ * Optional<MyType> opt = something_that_can_fail2();
+ * if (opt)
+ *     opt->method();
+ *
+ * // equivalent to
+ *
+ * if (opt.is_some())
+ *     opt.get().method();
+ * ```
+ */
+template <typename T> class Optional
+{
+private:
+  struct Empty
+  {
+  };
+
+  enum Kind
+  {
+    Some,
+    None
+  } kind;
+
+  union Content
+  {
+    Empty empty;
+    T value;
+
+    Content () = default;
+  } content;
+
+  Optional<T> (Kind kind, Content content) : kind (kind), content (content) {}
+
+public:
+  Optional (const Optional &other) = default;
+  Optional &operator= (const Optional &other) = default;
+  Optional (Optional &&other) = default;
+
+  static Optional<T> some (T value)
+  {
+    Content content;
+    content.value = value;
+
+    return Optional (Kind::Some, content);
+  }
+
+  static Optional<T> none ()
+  {
+    Content content;
+    content.empty = Empty ();
+
+    return Optional (Kind::None, content);
+  }
+
+  bool is_some () const { return kind == Kind::Some; }
+  bool is_none () const { return !is_some (); }
+
+  /**
+   * Enable boolean-like comparisons.
+   */
+  operator bool () { return is_some (); }
+
+  /**
+   * Enables dereferencing to access the contained value
+   */
+  T &operator* () { return get (); }
+  const T &operator* () const { return get (); }
+  T *operator-> () { return &get (); }
+  const T *operator-> () const { return &get (); }
+
+  const T &get () const
+  {
+    rust_assert (is_some ());
+
+    return content.value;
+  }
+
+  T &get ()
+  {
+    rust_assert (is_some ());
+
+    return content.value;
+  }
+
+  T take ()
+  {
+    rust_assert (is_some ());
+
+    auto to_return = std::move (content.value);
+
+    content.empty = Empty ();
+    kind = Kind::None;
+
+    return to_return;
+  }
+
+  template <typename U> Optional<U> map (std::function<U (T)> functor)
+  {
+    if (is_none ())
+      return Optional::none ();
+
+    auto value = functor (take ());
+
+    return Optional::some (value);
+  }
+};
+
+template <typename T> class Optional<T &>
+{
+private:
+  struct Empty
+  {
+  };
+
+  enum Kind
+  {
+    Some,
+    None
+  } kind;
+
+  union Content
+  {
+    Empty empty;
+    T *value;
+
+    Content () = default;
+  } content;
+
+  Optional<T &> (Kind kind, Content content) : kind (kind), content (content) {}
+
+public:
+  Optional (const Optional &other) = default;
+  Optional (Optional &&other) = default;
+
+  static Optional<T &> some (T &value)
+  {
+    Content content;
+    content.value = &value;
+
+    return Optional (Kind::Some, content);
+  }
+
+  static Optional<T &> none ()
+  {
+    Content content;
+    content.empty = Empty ();
+
+    return Optional (Kind::None, content);
+  }
+
+  bool is_some () const { return kind == Kind::Some; }
+  bool is_none () const { return !is_some (); }
+
+  // FIXME: Can we factor this in a single class?
+
+  /**
+   * Enable boolean-like comparisons.
+   */
+  operator bool () { return is_some (); }
+
+  /**
+   * Enables dereferencing to access the contained value
+   */
+  T &operator* () { return get (); }
+  const T &operator* () const { return get (); }
+  T *operator-> () { return &get (); }
+  const T *operator-> () const { return &get (); }
+
+  const T &get () const
+  {
+    rust_assert (is_some ());
+
+    return *content.value;
+  }
+
+  T &get ()
+  {
+    rust_assert (is_some ());
+
+    return *content.value;
+  }
+
+  T &take ()
+  {
+    rust_assert (is_some ());
+
+    auto to_return = std::move (content.value);
+
+    content.empty = Empty ();
+    kind = Kind::None;
+
+    return *to_return;
+  }
+
+  template <typename U> Optional<U &> map (std::function<U &(T &)> functor)
+  {
+    if (is_none ())
+      return Optional::none ();
+
+    auto value = functor (take ());
+
+    return Optional::some (value);
+  }
+};
+
+} // namespace Rust
+
+#ifdef CHECKING_P
+
+void
+rust_optional_test ();
+
+#endif // !CHECKING_P
+
+#endif // !RUST_OPTIONAL_H
-- 
2.25.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [PATCH Rust front-end v2 20/37] gccrs: Add attributes checker
  2022-08-24 11:59 Rust frontend patches v2 herron.philip
                   ` (18 preceding siblings ...)
  2022-08-24 11:59 ` [PATCH Rust front-end v2 19/37] gccrs: Add implementation of Optional herron.philip
@ 2022-08-24 11:59 ` herron.philip
  2022-08-24 11:59 ` [PATCH Rust front-end v2 21/37] gccrs: Add helpers mappings canonical path and lang items herron.philip
                   ` (17 subsequent siblings)
  37 siblings, 0 replies; 59+ messages in thread
From: herron.philip @ 2022-08-24 11:59 UTC (permalink / raw)
  To: gcc-patches; +Cc: gcc-rust, Arthur Cohen

From: Arthur Cohen <arthur.cohen@embecosm.com>

The attribute checker is responsible for checking the validity of various
attributes including built-in ones. It is currently unfinished and will
receive some modifications, as well as become the host of some existing
code in the compiler which needs to be refactored. One of its
responsibilities is to make sure that arguments given to built-in
attributes are correct, or contain the correct type of information. This
visitor also checks that an attribute is allowed to be used in the current
particular context.
---
 gcc/rust/util/rust-attributes.cc | 839 +++++++++++++++++++++++++++++++
 gcc/rust/util/rust-attributes.h  | 270 ++++++++++
 2 files changed, 1109 insertions(+)
 create mode 100644 gcc/rust/util/rust-attributes.cc
 create mode 100644 gcc/rust/util/rust-attributes.h

diff --git a/gcc/rust/util/rust-attributes.cc b/gcc/rust/util/rust-attributes.cc
new file mode 100644
index 00000000000..bf4bb2fbfe9
--- /dev/null
+++ b/gcc/rust/util/rust-attributes.cc
@@ -0,0 +1,839 @@
+// 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/>.
+
+#include "rust-attributes.h"
+#include "rust-ast.h"
+#include "rust-ast-full.h"
+#include "rust-diagnostics.h"
+#include "safe-ctype.h"
+
+namespace Rust {
+namespace Analysis {
+
+// https://doc.rust-lang.org/stable/nightly-rustc/src/rustc_feature/builtin_attrs.rs.html#248
+static const BuiltinAttrDefinition __definitions[] = {
+  {"inline", CODE_GENERATION},
+  {"cold", CODE_GENERATION},
+  {"cfg", EXPANSION},
+  {"cfg_attr", EXPANSION},
+  {"deprecated", STATIC_ANALYSIS},
+  {"allow", STATIC_ANALYSIS},
+  {"doc", HIR_LOWERING},
+  {"must_use", STATIC_ANALYSIS},
+  {"lang", HIR_LOWERING},
+  {"link_section", CODE_GENERATION},
+  {"no_mangle", CODE_GENERATION},
+  {"repr", CODE_GENERATION},
+  {"path", EXPANSION},
+};
+
+BuiltinAttributeMappings *
+BuiltinAttributeMappings::get ()
+{
+  static BuiltinAttributeMappings *instance = nullptr;
+  if (instance == nullptr)
+    instance = new BuiltinAttributeMappings ();
+
+  return instance;
+}
+
+const BuiltinAttrDefinition &
+BuiltinAttributeMappings::lookup_builtin (const std::string &attr_name) const
+{
+  auto it = mappings.find (attr_name);
+  if (it == mappings.end ())
+    return BuiltinAttrDefinition::error_node ();
+
+  return it->second;
+}
+
+BuiltinAttributeMappings::BuiltinAttributeMappings ()
+{
+  size_t ndefinitions = sizeof (__definitions) / sizeof (BuiltinAttrDefinition);
+  for (size_t i = 0; i < ndefinitions; i++)
+    {
+      const BuiltinAttrDefinition &def = __definitions[i];
+      mappings.insert ({def.name, def});
+    }
+}
+
+AttributeChecker::AttributeChecker () {}
+
+void
+AttributeChecker::go (AST::Crate &crate)
+{
+  check_attributes (crate.get_inner_attrs ());
+
+  for (auto &item : crate.items)
+    item->accept_vis (*this);
+}
+
+static bool
+is_builtin (const AST::Attribute &attribute, BuiltinAttrDefinition &builtin)
+{
+  auto &segments = attribute.get_path ().get_segments ();
+
+  // Builtin attributes always have a single segment. This avoids us creating
+  // strings all over the place and performing a linear search in the builtins
+  // map
+  if (segments.size () != 1)
+    return false;
+
+  builtin = BuiltinAttributeMappings::get ()->lookup_builtin (
+    segments.at (0).get_segment_name ());
+
+  return !builtin.is_error ();
+}
+
+/**
+ * Check that the string given to #[doc(alias = ...)] or #[doc(alias(...))] is
+ * valid.
+ *
+ * This means no whitespace characters other than spaces and no quoting
+ * characters.
+ */
+static void
+check_doc_alias (const std::string &alias_input, const Location &locus)
+{
+  // FIXME: The locus here is for the whole attribute. Can we get the locus
+  // of the alias input instead?
+  for (auto c : alias_input)
+    if ((ISSPACE (c) && c != ' ') || c == '\'' || c == '\"')
+      {
+	auto to_print = std::string (1, c);
+	switch (c)
+	  {
+	  case '\n':
+	    to_print = "\\n";
+	    break;
+	  case '\t':
+	    to_print = "\\t";
+	    break;
+	  default:
+	    break;
+	  }
+	rust_error_at (locus,
+		       "invalid character used in %<#[doc(alias)]%> input: %qs",
+		       to_print.c_str ());
+      }
+
+  if (alias_input.empty ())
+    return;
+
+  if (alias_input.front () == ' ' || alias_input.back () == ' ')
+    rust_error_at (locus,
+		   "%<#[doc(alias)]%> input cannot start or end with a space");
+}
+
+static void
+check_doc_attribute (const AST::Attribute &attribute)
+{
+  if (!attribute.has_attr_input ())
+    {
+      rust_error_at (
+	attribute.get_locus (),
+	// FIXME: Improve error message here. Rustc has a very good one
+	"%<#[doc]%> cannot be an empty attribute");
+      return;
+    }
+
+  switch (attribute.get_attr_input ().get_attr_input_type ())
+    {
+    case AST::AttrInput::LITERAL:
+    case AST::AttrInput::META_ITEM:
+      break;
+      // FIXME: Handle them as well
+
+      case AST::AttrInput::TOKEN_TREE: {
+	// FIXME: This doesn't check for #[doc(alias(...))]
+	const auto &option = static_cast<const AST::DelimTokenTree &> (
+	  attribute.get_attr_input ());
+	auto *meta_item = option.parse_to_meta_item ();
+
+	for (auto &item : meta_item->get_items ())
+	  {
+	    if (item->is_key_value_pair ())
+	      {
+		auto name_value
+		  = static_cast<AST::MetaNameValueStr *> (item.get ())
+		      ->get_name_value_pair ();
+
+		// FIXME: Check for other stuff than #[doc(alias = ...)]
+		if (name_value.first == "alias")
+		  check_doc_alias (name_value.second, attribute.get_locus ());
+	      }
+	  }
+	break;
+      }
+    }
+}
+
+void
+AttributeChecker::check_attribute (const AST::Attribute &attribute)
+{
+  BuiltinAttrDefinition result;
+
+  // This checker does not check non-builtin attributes
+  if (!is_builtin (attribute, result))
+    return;
+
+  // TODO: Add checks here for each builtin attribute
+  // TODO: Have an enum of builtins as well, switching on strings is annoying
+  // and costly
+  if (result.name == "doc")
+    check_doc_attribute (attribute);
+}
+
+void
+AttributeChecker::check_attributes (const AST::AttrVec &attributes)
+{
+  for (auto &attr : attributes)
+    check_attribute (attr);
+}
+
+void
+AttributeChecker::visit (AST::Token &tok)
+{}
+
+void
+AttributeChecker::visit (AST::DelimTokenTree &delim_tok_tree)
+{}
+
+void
+AttributeChecker::visit (AST::AttrInputMetaItemContainer &input)
+{}
+
+void
+AttributeChecker::visit (AST::IdentifierExpr &ident_expr)
+{}
+
+void
+AttributeChecker::visit (AST::Lifetime &lifetime)
+{}
+
+void
+AttributeChecker::visit (AST::LifetimeParam &lifetime_param)
+{}
+
+void
+AttributeChecker::visit (AST::ConstGenericParam &const_param)
+{}
+
+// rust-path.h
+void
+AttributeChecker::visit (AST::PathInExpression &path)
+{}
+
+void
+AttributeChecker::visit (AST::TypePathSegment &segment)
+{}
+
+void
+AttributeChecker::visit (AST::TypePathSegmentGeneric &segment)
+{}
+
+void
+AttributeChecker::visit (AST::TypePathSegmentFunction &segment)
+{}
+
+void
+AttributeChecker::visit (AST::TypePath &path)
+{}
+
+void
+AttributeChecker::visit (AST::QualifiedPathInExpression &path)
+{}
+
+void
+AttributeChecker::visit (AST::QualifiedPathInType &path)
+{}
+
+// rust-expr.h
+void
+AttributeChecker::visit (AST::LiteralExpr &expr)
+{}
+
+void
+AttributeChecker::visit (AST::AttrInputLiteral &attr_input)
+{}
+
+void
+AttributeChecker::visit (AST::MetaItemLitExpr &meta_item)
+{}
+
+void
+AttributeChecker::visit (AST::MetaItemPathLit &meta_item)
+{}
+
+void
+AttributeChecker::visit (AST::BorrowExpr &expr)
+{}
+
+void
+AttributeChecker::visit (AST::DereferenceExpr &expr)
+{}
+
+void
+AttributeChecker::visit (AST::ErrorPropagationExpr &expr)
+{}
+
+void
+AttributeChecker::visit (AST::NegationExpr &expr)
+{}
+
+void
+AttributeChecker::visit (AST::ArithmeticOrLogicalExpr &expr)
+{}
+
+void
+AttributeChecker::visit (AST::ComparisonExpr &expr)
+{}
+
+void
+AttributeChecker::visit (AST::LazyBooleanExpr &expr)
+{}
+
+void
+AttributeChecker::visit (AST::TypeCastExpr &expr)
+{}
+
+void
+AttributeChecker::visit (AST::AssignmentExpr &expr)
+{}
+
+void
+AttributeChecker::visit (AST::CompoundAssignmentExpr &expr)
+{}
+
+void
+AttributeChecker::visit (AST::GroupedExpr &expr)
+{}
+
+void
+AttributeChecker::visit (AST::ArrayElemsValues &elems)
+{}
+
+void
+AttributeChecker::visit (AST::ArrayElemsCopied &elems)
+{}
+
+void
+AttributeChecker::visit (AST::ArrayExpr &expr)
+{}
+
+void
+AttributeChecker::visit (AST::ArrayIndexExpr &expr)
+{}
+
+void
+AttributeChecker::visit (AST::TupleExpr &expr)
+{}
+
+void
+AttributeChecker::visit (AST::TupleIndexExpr &expr)
+{}
+
+void
+AttributeChecker::visit (AST::StructExprStruct &expr)
+{}
+
+void
+AttributeChecker::visit (AST::StructExprFieldIdentifier &field)
+{}
+
+void
+AttributeChecker::visit (AST::StructExprFieldIdentifierValue &field)
+{}
+
+void
+AttributeChecker::visit (AST::StructExprFieldIndexValue &field)
+{}
+
+void
+AttributeChecker::visit (AST::StructExprStructFields &expr)
+{}
+
+void
+AttributeChecker::visit (AST::StructExprStructBase &expr)
+{}
+
+void
+AttributeChecker::visit (AST::CallExpr &expr)
+{}
+
+void
+AttributeChecker::visit (AST::MethodCallExpr &expr)
+{}
+
+void
+AttributeChecker::visit (AST::FieldAccessExpr &expr)
+{}
+
+void
+AttributeChecker::visit (AST::ClosureExprInner &expr)
+{}
+
+void
+AttributeChecker::visit (AST::BlockExpr &expr)
+{}
+
+void
+AttributeChecker::visit (AST::ClosureExprInnerTyped &expr)
+{}
+
+void
+AttributeChecker::visit (AST::ContinueExpr &expr)
+{}
+
+void
+AttributeChecker::visit (AST::BreakExpr &expr)
+{}
+
+void
+AttributeChecker::visit (AST::RangeFromToExpr &expr)
+{}
+
+void
+AttributeChecker::visit (AST::RangeFromExpr &expr)
+{}
+
+void
+AttributeChecker::visit (AST::RangeToExpr &expr)
+{}
+
+void
+AttributeChecker::visit (AST::RangeFullExpr &expr)
+{}
+
+void
+AttributeChecker::visit (AST::RangeFromToInclExpr &expr)
+{}
+
+void
+AttributeChecker::visit (AST::RangeToInclExpr &expr)
+{}
+
+void
+AttributeChecker::visit (AST::ReturnExpr &expr)
+{}
+
+void
+AttributeChecker::visit (AST::UnsafeBlockExpr &expr)
+{}
+
+void
+AttributeChecker::visit (AST::LoopExpr &expr)
+{}
+
+void
+AttributeChecker::visit (AST::WhileLoopExpr &expr)
+{}
+
+void
+AttributeChecker::visit (AST::WhileLetLoopExpr &expr)
+{}
+
+void
+AttributeChecker::visit (AST::ForLoopExpr &expr)
+{}
+
+void
+AttributeChecker::visit (AST::IfExpr &expr)
+{}
+
+void
+AttributeChecker::visit (AST::IfExprConseqElse &expr)
+{}
+
+void
+AttributeChecker::visit (AST::IfExprConseqIf &expr)
+{}
+
+void
+AttributeChecker::visit (AST::IfExprConseqIfLet &expr)
+{}
+
+void
+AttributeChecker::visit (AST::IfLetExpr &expr)
+{}
+
+void
+AttributeChecker::visit (AST::IfLetExprConseqElse &expr)
+{}
+
+void
+AttributeChecker::visit (AST::IfLetExprConseqIf &expr)
+{}
+
+void
+AttributeChecker::visit (AST::IfLetExprConseqIfLet &expr)
+{}
+
+void
+AttributeChecker::visit (AST::MatchExpr &expr)
+{}
+
+void
+AttributeChecker::visit (AST::AwaitExpr &expr)
+{}
+
+void
+AttributeChecker::visit (AST::AsyncBlockExpr &expr)
+{}
+
+// rust-item.h
+void
+AttributeChecker::visit (AST::TypeParam &param)
+{}
+
+void
+AttributeChecker::visit (AST::LifetimeWhereClauseItem &item)
+{}
+
+void
+AttributeChecker::visit (AST::TypeBoundWhereClauseItem &item)
+{}
+
+void
+AttributeChecker::visit (AST::Method &method)
+{}
+
+void
+AttributeChecker::visit (AST::Module &module)
+{}
+
+void
+AttributeChecker::visit (AST::ExternCrate &crate)
+{}
+
+void
+AttributeChecker::visit (AST::UseTreeGlob &use_tree)
+{}
+
+void
+AttributeChecker::visit (AST::UseTreeList &use_tree)
+{}
+
+void
+AttributeChecker::visit (AST::UseTreeRebind &use_tree)
+{}
+
+void
+AttributeChecker::visit (AST::UseDeclaration &use_decl)
+{}
+
+void
+AttributeChecker::visit (AST::Function &function)
+{}
+
+void
+AttributeChecker::visit (AST::TypeAlias &type_alias)
+{}
+
+void
+AttributeChecker::visit (AST::StructStruct &struct_item)
+{
+  check_attributes (struct_item.get_outer_attrs ());
+}
+
+void
+AttributeChecker::visit (AST::TupleStruct &tuple_struct)
+{}
+
+void
+AttributeChecker::visit (AST::EnumItem &item)
+{}
+
+void
+AttributeChecker::visit (AST::EnumItemTuple &item)
+{}
+
+void
+AttributeChecker::visit (AST::EnumItemStruct &item)
+{}
+
+void
+AttributeChecker::visit (AST::EnumItemDiscriminant &item)
+{}
+
+void
+AttributeChecker::visit (AST::Enum &enum_item)
+{}
+
+void
+AttributeChecker::visit (AST::Union &union_item)
+{}
+
+void
+AttributeChecker::visit (AST::ConstantItem &const_item)
+{}
+
+void
+AttributeChecker::visit (AST::StaticItem &static_item)
+{}
+
+void
+AttributeChecker::visit (AST::TraitItemFunc &item)
+{}
+
+void
+AttributeChecker::visit (AST::TraitItemMethod &item)
+{}
+
+void
+AttributeChecker::visit (AST::TraitItemConst &item)
+{}
+
+void
+AttributeChecker::visit (AST::TraitItemType &item)
+{}
+
+void
+AttributeChecker::visit (AST::Trait &trait)
+{}
+
+void
+AttributeChecker::visit (AST::InherentImpl &impl)
+{}
+
+void
+AttributeChecker::visit (AST::TraitImpl &impl)
+{}
+
+void
+AttributeChecker::visit (AST::ExternalStaticItem &item)
+{}
+
+void
+AttributeChecker::visit (AST::ExternalFunctionItem &item)
+{}
+
+void
+AttributeChecker::visit (AST::ExternBlock &block)
+{}
+
+// rust-macro.h
+void
+AttributeChecker::visit (AST::MacroMatchFragment &match)
+{}
+
+void
+AttributeChecker::visit (AST::MacroMatchRepetition &match)
+{}
+
+void
+AttributeChecker::visit (AST::MacroMatcher &matcher)
+{}
+
+void
+AttributeChecker::visit (AST::MacroRulesDefinition &rules_def)
+{}
+
+void
+AttributeChecker::visit (AST::MacroInvocation &macro_invoc)
+{}
+
+void
+AttributeChecker::visit (AST::MetaItemPath &meta_item)
+{}
+
+void
+AttributeChecker::visit (AST::MetaItemSeq &meta_item)
+{}
+
+void
+AttributeChecker::visit (AST::MetaWord &meta_item)
+{}
+
+void
+AttributeChecker::visit (AST::MetaNameValueStr &meta_item)
+{}
+
+void
+AttributeChecker::visit (AST::MetaListPaths &meta_item)
+{}
+
+void
+AttributeChecker::visit (AST::MetaListNameValueStr &meta_item)
+{}
+
+// rust-pattern.h
+void
+AttributeChecker::visit (AST::LiteralPattern &pattern)
+{}
+
+void
+AttributeChecker::visit (AST::IdentifierPattern &pattern)
+{}
+
+void
+AttributeChecker::visit (AST::WildcardPattern &pattern)
+{}
+
+// void AttributeChecker::visit(RangePatternBound& bound){}
+
+void
+AttributeChecker::visit (AST::RangePatternBoundLiteral &bound)
+{}
+
+void
+AttributeChecker::visit (AST::RangePatternBoundPath &bound)
+{}
+
+void
+AttributeChecker::visit (AST::RangePatternBoundQualPath &bound)
+{}
+
+void
+AttributeChecker::visit (AST::RangePattern &pattern)
+{}
+
+void
+AttributeChecker::visit (AST::ReferencePattern &pattern)
+{}
+
+// void AttributeChecker::visit(StructPatternField& field){}
+
+void
+AttributeChecker::visit (AST::StructPatternFieldTuplePat &field)
+{}
+
+void
+AttributeChecker::visit (AST::StructPatternFieldIdentPat &field)
+{}
+
+void
+AttributeChecker::visit (AST::StructPatternFieldIdent &field)
+{}
+
+void
+AttributeChecker::visit (AST::StructPattern &pattern)
+{}
+
+// void AttributeChecker::visit(TupleStructItems& tuple_items){}
+
+void
+AttributeChecker::visit (AST::TupleStructItemsNoRange &tuple_items)
+{}
+
+void
+AttributeChecker::visit (AST::TupleStructItemsRange &tuple_items)
+{}
+
+void
+AttributeChecker::visit (AST::TupleStructPattern &pattern)
+{}
+
+// void AttributeChecker::visit(TuplePatternItems& tuple_items){}
+
+void
+AttributeChecker::visit (AST::TuplePatternItemsMultiple &tuple_items)
+{}
+
+void
+AttributeChecker::visit (AST::TuplePatternItemsRanged &tuple_items)
+{}
+
+void
+AttributeChecker::visit (AST::TuplePattern &pattern)
+{}
+
+void
+AttributeChecker::visit (AST::GroupedPattern &pattern)
+{}
+
+void
+AttributeChecker::visit (AST::SlicePattern &pattern)
+{}
+
+// rust-stmt.h
+void
+AttributeChecker::visit (AST::EmptyStmt &stmt)
+{}
+
+void
+AttributeChecker::visit (AST::LetStmt &stmt)
+{}
+
+void
+AttributeChecker::visit (AST::ExprStmtWithoutBlock &stmt)
+{}
+
+void
+AttributeChecker::visit (AST::ExprStmtWithBlock &stmt)
+{}
+
+// rust-type.h
+void
+AttributeChecker::visit (AST::TraitBound &bound)
+{}
+
+void
+AttributeChecker::visit (AST::ImplTraitType &type)
+{}
+
+void
+AttributeChecker::visit (AST::TraitObjectType &type)
+{}
+
+void
+AttributeChecker::visit (AST::ParenthesisedType &type)
+{}
+
+void
+AttributeChecker::visit (AST::ImplTraitTypeOneBound &type)
+{}
+
+void
+AttributeChecker::visit (AST::TraitObjectTypeOneBound &type)
+{}
+
+void
+AttributeChecker::visit (AST::TupleType &type)
+{}
+
+void
+AttributeChecker::visit (AST::NeverType &type)
+{}
+
+void
+AttributeChecker::visit (AST::RawPointerType &type)
+{}
+
+void
+AttributeChecker::visit (AST::ReferenceType &type)
+{}
+
+void
+AttributeChecker::visit (AST::ArrayType &type)
+{}
+
+void
+AttributeChecker::visit (AST::SliceType &type)
+{}
+
+void
+AttributeChecker::visit (AST::InferredType &type)
+{}
+
+void
+AttributeChecker::visit (AST::BareFunctionType &type)
+{}
+
+} // namespace Analysis
+} // namespace Rust
diff --git a/gcc/rust/util/rust-attributes.h b/gcc/rust/util/rust-attributes.h
new file mode 100644
index 00000000000..3ac93ff5908
--- /dev/null
+++ b/gcc/rust/util/rust-attributes.h
@@ -0,0 +1,270 @@
+// 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/>.
+
+#include "rust-ast.h"
+#include "rust-system.h"
+#include "rust-ast-visitor.h"
+
+namespace Rust {
+namespace Analysis {
+
+enum CompilerPass
+{
+  UNKNOWN,
+
+  EXPANSION,
+  NAME_RESOLUTION,
+  HIR_LOWERING,
+  TYPE_CHECK,
+  STATIC_ANALYSIS,
+  CODE_GENERATION
+};
+
+struct BuiltinAttrDefinition
+{
+  std::string name;
+  CompilerPass handler;
+
+  static BuiltinAttrDefinition get_error ()
+  {
+    return BuiltinAttrDefinition{"", UNKNOWN};
+  }
+
+  static BuiltinAttrDefinition &error_node ()
+  {
+    static BuiltinAttrDefinition error_node = get_error ();
+    return error_node;
+  }
+
+  bool is_error () const { return name.empty (); }
+};
+
+class BuiltinAttributeMappings
+{
+public:
+  static BuiltinAttributeMappings *get ();
+
+  const BuiltinAttrDefinition &
+  lookup_builtin (const std::string &attr_name) const;
+
+private:
+  BuiltinAttributeMappings ();
+
+  std::map<std::string, const BuiltinAttrDefinition> mappings;
+};
+
+/**
+ * Checks the validity of various attributes. The goal of this visitor is to
+ * make sure that attributes are applied in allowed contexts, for example to
+ * make sure that #[inline] is only applied to functions and closures, as well
+ * as checking the "arguments" or input given to these attributes, making sure
+ * it is appropriate and valid.
+ */
+class AttributeChecker : public AST::ASTVisitor
+{
+public:
+  AttributeChecker ();
+
+  /**
+   * Check all the attributes of all the items of a crate
+   */
+  void go (AST::Crate &crate);
+
+private:
+  /* Check the validity of a given attribute */
+  void check_attribute (const AST::Attribute &attribute);
+
+  /* Check the validity of all given attributes */
+  void check_attributes (const AST::AttrVec &attributes);
+
+  // rust-ast.h
+  void visit (AST::Token &tok);
+  void visit (AST::DelimTokenTree &delim_tok_tree);
+  void visit (AST::AttrInputMetaItemContainer &input);
+  void visit (AST::IdentifierExpr &ident_expr);
+  void visit (AST::Lifetime &lifetime);
+  void visit (AST::LifetimeParam &lifetime_param);
+  void visit (AST::ConstGenericParam &const_param);
+
+  // rust-path.h
+  void visit (AST::PathInExpression &path);
+  void visit (AST::TypePathSegment &segment);
+  void visit (AST::TypePathSegmentGeneric &segment);
+  void visit (AST::TypePathSegmentFunction &segment);
+  void visit (AST::TypePath &path);
+  void visit (AST::QualifiedPathInExpression &path);
+  void visit (AST::QualifiedPathInType &path);
+
+  // rust-expr.h
+  void visit (AST::LiteralExpr &expr);
+  void visit (AST::AttrInputLiteral &attr_input);
+  void visit (AST::MetaItemLitExpr &meta_item);
+  void visit (AST::MetaItemPathLit &meta_item);
+  void visit (AST::BorrowExpr &expr);
+  void visit (AST::DereferenceExpr &expr);
+  void visit (AST::ErrorPropagationExpr &expr);
+  void visit (AST::NegationExpr &expr);
+  void visit (AST::ArithmeticOrLogicalExpr &expr);
+  void visit (AST::ComparisonExpr &expr);
+  void visit (AST::LazyBooleanExpr &expr);
+  void visit (AST::TypeCastExpr &expr);
+  void visit (AST::AssignmentExpr &expr);
+  void visit (AST::CompoundAssignmentExpr &expr);
+  void visit (AST::GroupedExpr &expr);
+  void visit (AST::ArrayElemsValues &elems);
+  void visit (AST::ArrayElemsCopied &elems);
+  void visit (AST::ArrayExpr &expr);
+  void visit (AST::ArrayIndexExpr &expr);
+  void visit (AST::TupleExpr &expr);
+  void visit (AST::TupleIndexExpr &expr);
+  void visit (AST::StructExprStruct &expr);
+  void visit (AST::StructExprFieldIdentifier &field);
+  void visit (AST::StructExprFieldIdentifierValue &field);
+  void visit (AST::StructExprFieldIndexValue &field);
+  void visit (AST::StructExprStructFields &expr);
+  void visit (AST::StructExprStructBase &expr);
+  void visit (AST::CallExpr &expr);
+  void visit (AST::MethodCallExpr &expr);
+  void visit (AST::FieldAccessExpr &expr);
+  void visit (AST::ClosureExprInner &expr);
+  void visit (AST::BlockExpr &expr);
+  void visit (AST::ClosureExprInnerTyped &expr);
+  void visit (AST::ContinueExpr &expr);
+  void visit (AST::BreakExpr &expr);
+  void visit (AST::RangeFromToExpr &expr);
+  void visit (AST::RangeFromExpr &expr);
+  void visit (AST::RangeToExpr &expr);
+  void visit (AST::RangeFullExpr &expr);
+  void visit (AST::RangeFromToInclExpr &expr);
+  void visit (AST::RangeToInclExpr &expr);
+  void visit (AST::ReturnExpr &expr);
+  void visit (AST::UnsafeBlockExpr &expr);
+  void visit (AST::LoopExpr &expr);
+  void visit (AST::WhileLoopExpr &expr);
+  void visit (AST::WhileLetLoopExpr &expr);
+  void visit (AST::ForLoopExpr &expr);
+  void visit (AST::IfExpr &expr);
+  void visit (AST::IfExprConseqElse &expr);
+  void visit (AST::IfExprConseqIf &expr);
+  void visit (AST::IfExprConseqIfLet &expr);
+  void visit (AST::IfLetExpr &expr);
+  void visit (AST::IfLetExprConseqElse &expr);
+  void visit (AST::IfLetExprConseqIf &expr);
+  void visit (AST::IfLetExprConseqIfLet &expr);
+  void visit (AST::MatchExpr &expr);
+  void visit (AST::AwaitExpr &expr);
+  void visit (AST::AsyncBlockExpr &expr);
+
+  // rust-item.h
+  void visit (AST::TypeParam &param);
+  void visit (AST::LifetimeWhereClauseItem &item);
+  void visit (AST::TypeBoundWhereClauseItem &item);
+  void visit (AST::Method &method);
+  void visit (AST::Module &module);
+  void visit (AST::ExternCrate &crate);
+  void visit (AST::UseTreeGlob &use_tree);
+  void visit (AST::UseTreeList &use_tree);
+  void visit (AST::UseTreeRebind &use_tree);
+  void visit (AST::UseDeclaration &use_decl);
+  void visit (AST::Function &function);
+  void visit (AST::TypeAlias &type_alias);
+  void visit (AST::StructStruct &struct_item);
+  void visit (AST::TupleStruct &tuple_struct);
+  void visit (AST::EnumItem &item);
+  void visit (AST::EnumItemTuple &item);
+  void visit (AST::EnumItemStruct &item);
+  void visit (AST::EnumItemDiscriminant &item);
+  void visit (AST::Enum &enum_item);
+  void visit (AST::Union &union_item);
+  void visit (AST::ConstantItem &const_item);
+  void visit (AST::StaticItem &static_item);
+  void visit (AST::TraitItemFunc &item);
+  void visit (AST::TraitItemMethod &item);
+  void visit (AST::TraitItemConst &item);
+  void visit (AST::TraitItemType &item);
+  void visit (AST::Trait &trait);
+  void visit (AST::InherentImpl &impl);
+  void visit (AST::TraitImpl &impl);
+  void visit (AST::ExternalStaticItem &item);
+  void visit (AST::ExternalFunctionItem &item);
+  void visit (AST::ExternBlock &block);
+
+  // rust-macro.h
+  void visit (AST::MacroMatchFragment &match);
+  void visit (AST::MacroMatchRepetition &match);
+  void visit (AST::MacroMatcher &matcher);
+  void visit (AST::MacroRulesDefinition &rules_def);
+  void visit (AST::MacroInvocation &macro_invoc);
+  void visit (AST::MetaItemPath &meta_item);
+  void visit (AST::MetaItemSeq &meta_item);
+  void visit (AST::MetaWord &meta_item);
+  void visit (AST::MetaNameValueStr &meta_item);
+  void visit (AST::MetaListPaths &meta_item);
+  void visit (AST::MetaListNameValueStr &meta_item);
+
+  // rust-pattern.h
+  void visit (AST::LiteralPattern &pattern);
+  void visit (AST::IdentifierPattern &pattern);
+  void visit (AST::WildcardPattern &pattern);
+  // void visit(RangePatternBound& bound);
+  void visit (AST::RangePatternBoundLiteral &bound);
+  void visit (AST::RangePatternBoundPath &bound);
+  void visit (AST::RangePatternBoundQualPath &bound);
+  void visit (AST::RangePattern &pattern);
+  void visit (AST::ReferencePattern &pattern);
+  // void visit(StructPatternField& field);
+  void visit (AST::StructPatternFieldTuplePat &field);
+  void visit (AST::StructPatternFieldIdentPat &field);
+  void visit (AST::StructPatternFieldIdent &field);
+  void visit (AST::StructPattern &pattern);
+  // void visit(TupleStructItems& tuple_items);
+  void visit (AST::TupleStructItemsNoRange &tuple_items);
+  void visit (AST::TupleStructItemsRange &tuple_items);
+  void visit (AST::TupleStructPattern &pattern);
+  // void visit(TuplePatternItems& tuple_items);
+  void visit (AST::TuplePatternItemsMultiple &tuple_items);
+  void visit (AST::TuplePatternItemsRanged &tuple_items);
+  void visit (AST::TuplePattern &pattern);
+  void visit (AST::GroupedPattern &pattern);
+  void visit (AST::SlicePattern &pattern);
+
+  // rust-stmt.h
+  void visit (AST::EmptyStmt &stmt);
+  void visit (AST::LetStmt &stmt);
+  void visit (AST::ExprStmtWithoutBlock &stmt);
+  void visit (AST::ExprStmtWithBlock &stmt);
+
+  // rust-type.h
+  void visit (AST::TraitBound &bound);
+  void visit (AST::ImplTraitType &type);
+  void visit (AST::TraitObjectType &type);
+  void visit (AST::ParenthesisedType &type);
+  void visit (AST::ImplTraitTypeOneBound &type);
+  void visit (AST::TraitObjectTypeOneBound &type);
+  void visit (AST::TupleType &type);
+  void visit (AST::NeverType &type);
+  void visit (AST::RawPointerType &type);
+  void visit (AST::ReferenceType &type);
+  void visit (AST::ArrayType &type);
+  void visit (AST::SliceType &type);
+  void visit (AST::InferredType &type);
+  void visit (AST::BareFunctionType &type);
+};
+
+} // namespace Analysis
+} // namespace Rust
-- 
2.25.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [PATCH Rust front-end v2 21/37] gccrs: Add helpers mappings canonical path and lang items
  2022-08-24 11:59 Rust frontend patches v2 herron.philip
                   ` (19 preceding siblings ...)
  2022-08-24 11:59 ` [PATCH Rust front-end v2 20/37] gccrs: Add attributes checker herron.philip
@ 2022-08-24 11:59 ` herron.philip
  2022-08-24 11:59 ` [PATCH Rust front-end v2 22/37] gccrs: Add type resolution and trait solving pass herron.philip
                   ` (16 subsequent siblings)
  37 siblings, 0 replies; 59+ messages in thread
From: herron.philip @ 2022-08-24 11:59 UTC (permalink / raw)
  To: gcc-patches; +Cc: gcc-rust, Philip Herron

From: Philip Herron <philip.herron@embecosm.com>

These are various helper classes used in the compiler pipeline.
---
 gcc/rust/util/rust-canonical-path.h   | 195 +++++
 gcc/rust/util/rust-common.h           |  53 ++
 gcc/rust/util/rust-hir-map.cc         | 980 ++++++++++++++++++++++++++
 gcc/rust/util/rust-hir-map.h          | 356 ++++++++++
 gcc/rust/util/rust-identifier.h       |  49 ++
 gcc/rust/util/rust-lang-item.h        | 377 ++++++++++
 gcc/rust/util/rust-mapping-common.h   |  85 +++
 gcc/rust/util/rust-stacked-contexts.h |  86 +++
 8 files changed, 2181 insertions(+)
 create mode 100644 gcc/rust/util/rust-canonical-path.h
 create mode 100644 gcc/rust/util/rust-common.h
 create mode 100644 gcc/rust/util/rust-hir-map.cc
 create mode 100644 gcc/rust/util/rust-hir-map.h
 create mode 100644 gcc/rust/util/rust-identifier.h
 create mode 100644 gcc/rust/util/rust-lang-item.h
 create mode 100644 gcc/rust/util/rust-mapping-common.h
 create mode 100644 gcc/rust/util/rust-stacked-contexts.h

diff --git a/gcc/rust/util/rust-canonical-path.h b/gcc/rust/util/rust-canonical-path.h
new file mode 100644
index 00000000000..54cc0390849
--- /dev/null
+++ b/gcc/rust/util/rust-canonical-path.h
@@ -0,0 +1,195 @@
+// 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_CANONICAL_PATH
+#define RUST_CANONICAL_PATH
+
+#include "rust-system.h"
+#include "rust-mapping-common.h"
+
+namespace Rust {
+namespace Resolver {
+
+// https://doc.rust-lang.org/reference/paths.html#canonical-paths
+//
+// struct X - path X
+// impl X { fn test - path X::test }
+//
+// struct X<T> - path X
+//
+// impl X<T>   { fn test - path X::test}
+// impl X<i32> { fn test - path X<i32>::test }
+// impl X<f32> { fn test - path X<f32>::test }
+//
+// pub trait Trait { // ::a::Trait
+//   fn f(&self); // ::a::Trait::f
+// }
+//
+// impl Trait for Struct {
+//    fn f(&self) {} // <::a::Struct as ::a::Trait>::f
+// }
+class CanonicalPath
+{
+public:
+  CanonicalPath (const CanonicalPath &other) : segs (other.segs) {}
+
+  CanonicalPath &operator= (const CanonicalPath &other)
+  {
+    segs = other.segs;
+    return *this;
+  }
+
+  static CanonicalPath new_seg (NodeId id, const std::string &path)
+  {
+    rust_assert (!path.empty ());
+    return CanonicalPath ({std::pair<NodeId, std::string> (id, path)},
+			  UNKNOWN_CREATENUM);
+  }
+
+  static CanonicalPath
+  trait_impl_projection_seg (NodeId id, const CanonicalPath &trait_seg,
+			     const CanonicalPath &impl_type_seg)
+  {
+    return CanonicalPath::new_seg (id, "<" + impl_type_seg.get () + " as "
+					 + trait_seg.get () + ">");
+  }
+
+  std::string get () const
+  {
+    std::string buf;
+    for (size_t i = 0; i < segs.size (); i++)
+      {
+	bool have_more = (i + 1) < segs.size ();
+	const std::string &seg = segs.at (i).second;
+	buf += seg + (have_more ? "::" : "");
+      }
+    return buf;
+  }
+
+  static CanonicalPath get_big_self (NodeId id)
+  {
+    return CanonicalPath::new_seg (id, "Self");
+  }
+
+  static CanonicalPath create_empty ()
+  {
+    return CanonicalPath ({}, UNKNOWN_CREATENUM);
+  }
+
+  bool is_empty () const { return segs.size () == 0; }
+
+  CanonicalPath append (const CanonicalPath &other) const
+  {
+    rust_assert (!other.is_empty ());
+    if (is_empty ())
+      return CanonicalPath (other.segs, crate_num);
+
+    std::vector<std::pair<NodeId, std::string>> copy (segs);
+    for (auto &s : other.segs)
+      copy.push_back (s);
+
+    return CanonicalPath (copy, crate_num);
+  }
+
+  // if we have the path A::B::C this will give a callback for each segment
+  // including the prefix, example:
+  //
+  // path:
+  //   A::B::C
+  //
+  // iterate:
+  //   A
+  //   A::B
+  //   A::B::C
+  void iterate (std::function<bool (const CanonicalPath &)> cb) const
+  {
+    std::vector<std::pair<NodeId, std::string>> buf;
+    for (auto &seg : segs)
+      {
+	buf.push_back (seg);
+	if (!cb (CanonicalPath (buf, crate_num)))
+	  return;
+      }
+  }
+
+  // if we have the path A::B::C this will give a callback for each segment
+  // example:
+  //
+  // path:
+  //   A::B::C
+  //
+  // iterate:
+  //   A
+  //      B
+  //         C
+  void iterate_segs (std::function<bool (const CanonicalPath &)> cb) const
+  {
+    for (auto &seg : segs)
+      {
+	std::vector<std::pair<NodeId, std::string>> buf;
+	buf.push_back ({seg.first, seg.second});
+	if (!cb (CanonicalPath (buf, crate_num)))
+	  return;
+      }
+  }
+
+  size_t size () const { return segs.size (); }
+
+  NodeId get_node_id () const
+  {
+    rust_assert (!segs.empty ());
+    return segs.back ().first;
+  }
+
+  const std::pair<NodeId, std::string> &get_seg_at (size_t index) const
+  {
+    rust_assert (index < size ());
+    return segs.at (index);
+  }
+
+  bool is_equal (const CanonicalPath &b) const
+  {
+    return get ().compare (b.get ()) == 0;
+  }
+
+  void set_crate_num (CrateNum n) { crate_num = n; }
+
+  CrateNum get_crate_num () const
+  {
+    rust_assert (crate_num != UNKNOWN_CREATENUM);
+    return crate_num;
+  }
+
+  bool operator== (const CanonicalPath &b) const { return is_equal (b); }
+
+  bool operator< (const CanonicalPath &b) const { return get () < b.get (); }
+
+private:
+  explicit CanonicalPath (std::vector<std::pair<NodeId, std::string>> path,
+			  CrateNum crate_num)
+    : segs (path), crate_num (crate_num)
+  {}
+
+  std::vector<std::pair<NodeId, std::string>> segs;
+  CrateNum crate_num;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_CANONICAL_PATH
diff --git a/gcc/rust/util/rust-common.h b/gcc/rust/util/rust-common.h
new file mode 100644
index 00000000000..a3f6fb07d8d
--- /dev/null
+++ b/gcc/rust/util/rust-common.h
@@ -0,0 +1,53 @@
+// Copyright (C) 2021-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/>.
+
+// Common definitions useful throughout the Rust frontend.
+
+#ifndef RUST_COMMON
+#define RUST_COMMON
+
+namespace Rust {
+
+enum Mutability
+{
+  Imm,
+  Mut
+};
+
+enum Unsafety
+{
+  Unsafe,
+  Normal
+};
+
+enum Polarity
+{
+  Positive,
+  Negative
+};
+
+enum AsyncConstStatus
+{
+  NONE,
+  CONST_FN,
+  ASYNC_FN
+};
+
+} // namespace Rust
+
+#endif // RUST_COMMON
diff --git a/gcc/rust/util/rust-hir-map.cc b/gcc/rust/util/rust-hir-map.cc
new file mode 100644
index 00000000000..8705fdcf381
--- /dev/null
+++ b/gcc/rust/util/rust-hir-map.cc
@@ -0,0 +1,980 @@
+// 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/>.
+
+#include "rust-hir-map.h"
+#include "rust-ast-full.h"
+#include "rust-diagnostics.h"
+#include "rust-hir-full.h"
+#include "rust-macro-builtins.h"
+#include "rust-mapping-common.h"
+
+namespace Rust {
+namespace Analysis {
+
+NodeMapping
+NodeMapping::get_error ()
+{
+  return NodeMapping (UNKNOWN_CREATENUM, UNKNOWN_NODEID, UNKNOWN_HIRID,
+		      UNKNOWN_LOCAL_DEFID);
+}
+
+CrateNum
+NodeMapping::get_crate_num () const
+{
+  return crateNum;
+}
+
+NodeId
+NodeMapping::get_nodeid () const
+{
+  return nodeId;
+}
+
+HirId
+NodeMapping::get_hirid () const
+{
+  return hirId;
+}
+
+LocalDefId
+NodeMapping::get_local_defid () const
+{
+  return localDefId;
+}
+
+DefId
+NodeMapping::get_defid () const
+{
+  return get_defid (get_crate_num (), get_local_defid ());
+}
+
+DefId
+NodeMapping::get_defid (CrateNum crate_num, LocalDefId local_defid)
+{
+  return DefId{crate_num, local_defid};
+}
+
+std::string
+NodeMapping::as_string () const
+{
+  std::ostringstream ss;
+  ss << "["
+     << "C: " << get_crate_num ();
+  if (get_nodeid () != UNKNOWN_NODEID)
+    ss << " Nid: " << get_nodeid ();
+
+  if (get_hirid () != UNKNOWN_HIRID)
+    ss << " Hid: " << get_hirid ();
+
+  if (get_local_defid () != UNKNOWN_LOCAL_DEFID)
+    ss << " Lid: " << get_local_defid ();
+
+  ss << "]";
+  return ss.str ();
+}
+
+// Mappings Class now
+static const HirId kDefaultNodeIdBegin = 1;
+static const HirId kDefaultHirIdBegin = 1;
+static const HirId kDefaultCrateNumBegin = 0;
+
+Mappings::Mappings ()
+  : crateNumItr (kDefaultCrateNumBegin), currentCrateNum (UNKNOWN_CREATENUM),
+    hirIdIter (kDefaultHirIdBegin), nodeIdIter (kDefaultNodeIdBegin)
+{}
+
+Mappings::~Mappings () {}
+
+Mappings *
+Mappings::get ()
+{
+  static std::unique_ptr<Mappings> instance;
+  if (!instance)
+    instance = std::unique_ptr<Mappings> (new Mappings ());
+
+  return instance.get ();
+}
+
+CrateNum
+Mappings::get_next_crate_num (const std::string &name)
+{
+  auto id = crateNumItr;
+  crateNumItr++;
+  set_crate_name (id, name);
+  return id;
+}
+
+void
+Mappings::set_current_crate (CrateNum crateNum)
+{
+  currentCrateNum = crateNum;
+}
+
+CrateNum
+Mappings::get_current_crate () const
+{
+  return currentCrateNum;
+}
+
+bool
+Mappings::get_crate_name (CrateNum crate_num, std::string &name) const
+{
+  auto it = crate_names.find (crate_num);
+  if (it == crate_names.end ())
+    return false;
+
+  name.assign (it->second);
+  return true;
+}
+
+void
+Mappings::set_crate_name (CrateNum crate_num, const std::string &name)
+{
+  crate_names[crate_num] = name;
+}
+
+std::string
+Mappings::get_current_crate_name () const
+{
+  std::string name;
+  bool ok = get_crate_name (get_current_crate (), name);
+  rust_assert (ok);
+  return name;
+}
+
+bool
+Mappings::lookup_crate_name (const std::string &crate_name,
+			     CrateNum &resolved_crate_num) const
+{
+  for (const auto &it : crate_names)
+    {
+      if (it.second.compare (crate_name) == 0)
+	{
+	  resolved_crate_num = it.first;
+	  return true;
+	}
+    }
+  return false;
+}
+
+bool
+Mappings::crate_num_to_nodeid (const CrateNum &crate_num, NodeId &node_id) const
+{
+  auto it = ast_crate_mappings.find (crate_num);
+  if (it == ast_crate_mappings.end ())
+    return false;
+
+  node_id = it->second->get_node_id ();
+  return true;
+}
+
+bool
+Mappings::node_is_crate (NodeId node_id) const
+{
+  for (const auto &it : ast_crate_mappings)
+    {
+      NodeId crate_node_id = it.second->get_node_id ();
+      if (crate_node_id == node_id)
+	return true;
+    }
+  return false;
+}
+
+NodeId
+Mappings::get_next_node_id ()
+{
+  auto it = nodeIdIter;
+  nodeIdIter++;
+  return it;
+}
+
+HirId
+Mappings::get_next_hir_id (CrateNum crateNum)
+{
+  auto id = hirIdIter;
+  hirIdIter++;
+
+  auto it = hirNodesWithinCrate.find (crateNum);
+  if (it == hirNodesWithinCrate.end ())
+    {
+      hirNodesWithinCrate.insert ({crateNum, {}});
+    }
+
+  hirNodesWithinCrate[crateNum].insert (id);
+  return id;
+}
+
+LocalDefId
+Mappings::get_next_localdef_id (CrateNum crateNum)
+{
+  auto it = localIdIter.find (crateNum);
+  if (it == localIdIter.end ())
+    {
+      localIdIter.insert ({crateNum, 1});
+    }
+
+  it = localIdIter.find (crateNum);
+  rust_assert (it != localIdIter.end ());
+
+  LocalDefId id = it->second;
+  localIdIter[crateNum] = id + 1;
+  return id;
+}
+
+AST::Crate &
+Mappings::get_ast_crate (CrateNum crateNum)
+{
+  auto it = ast_crate_mappings.find (crateNum);
+  rust_assert (it != ast_crate_mappings.end ());
+  return *it->second;
+}
+
+AST::Crate &
+Mappings::get_ast_crate_by_node_id (NodeId id)
+{
+  auto i = crate_node_to_crate_num.find (id);
+  rust_assert (i != crate_node_to_crate_num.end ());
+
+  CrateNum crateNum = i->second;
+  auto it = ast_crate_mappings.find (crateNum);
+  rust_assert (it != ast_crate_mappings.end ());
+  return *it->second;
+}
+
+AST::Crate &
+Mappings::insert_ast_crate (std::unique_ptr<AST::Crate> &&crate,
+			    CrateNum crate_num)
+{
+  auto it = ast_crate_mappings.find (crate_num);
+  rust_assert (it == ast_crate_mappings.end ());
+
+  // store it
+  ast_crate_mappings.insert ({crate_num, crate.release ()});
+
+  // return the reference to it
+  it = ast_crate_mappings.find (crate_num);
+  rust_assert (it != ast_crate_mappings.end ());
+  return *it->second;
+}
+
+HIR::Crate &
+Mappings::get_hir_crate (CrateNum crateNum)
+{
+  auto it = hir_crate_mappings.find (crateNum);
+  rust_assert (it != hir_crate_mappings.end ());
+  return *it->second;
+}
+
+bool
+Mappings::is_local_hirid_crate (HirId crateNum)
+{
+  for (const auto &it : hir_crate_mappings)
+    {
+      const auto &crate = it.second;
+      if (crate->get_mappings ().get_hirid () == crateNum)
+	return true;
+    }
+  return false;
+}
+
+HIR::Crate &
+Mappings::insert_hir_crate (std::unique_ptr<HIR::Crate> &&crate)
+{
+  CrateNum crateNum = crate->get_mappings ().get_crate_num ();
+  auto it = hir_crate_mappings.find (crateNum);
+  rust_assert (it == hir_crate_mappings.end ());
+
+  insert_node_to_hir (crate->get_mappings ().get_nodeid (),
+		      crate->get_mappings ().get_hirid ());
+  hir_crate_mappings.insert ({crateNum, crate.release ()});
+
+  it = hir_crate_mappings.find (crateNum);
+  rust_assert (it != hir_crate_mappings.end ());
+  return *it->second;
+}
+
+void
+Mappings::insert_defid_mapping (DefId id, HIR::Item *item)
+{
+  CrateNum crate_num = id.crateNum;
+  LocalDefId local_def_id = id.localDefId;
+
+  rust_assert (lookup_defid (id) == nullptr);
+  rust_assert (lookup_local_defid (crate_num, local_def_id) == nullptr);
+
+  defIdMappings[id] = item;
+  insert_local_defid_mapping (crate_num, local_def_id, item);
+}
+
+HIR::Item *
+Mappings::lookup_defid (DefId id)
+{
+  auto it = defIdMappings.find (id);
+  if (it == defIdMappings.end ())
+    return nullptr;
+
+  return it->second;
+}
+
+void
+Mappings::insert_hir_item (HIR::Item *item)
+{
+  auto id = item->get_mappings ().get_hirid ();
+  rust_assert (lookup_hir_item (id) == nullptr);
+
+  hirItemMappings[id] = item;
+  insert_node_to_hir (item->get_mappings ().get_nodeid (), id);
+}
+
+HIR::Item *
+Mappings::lookup_hir_item (HirId id)
+{
+  auto it = hirItemMappings.find (id);
+  if (it == hirItemMappings.end ())
+    return nullptr;
+
+  return it->second;
+}
+
+void
+Mappings::insert_hir_trait_item (HIR::TraitItem *item)
+{
+  auto id = item->get_mappings ().get_hirid ();
+  rust_assert (lookup_hir_trait_item (id) == nullptr);
+
+  hirTraitItemMappings[id] = item;
+  insert_node_to_hir (item->get_mappings ().get_nodeid (), id);
+}
+
+HIR::TraitItem *
+Mappings::lookup_hir_trait_item (HirId id)
+{
+  auto it = hirTraitItemMappings.find (id);
+  if (it == hirTraitItemMappings.end ())
+    return nullptr;
+
+  return it->second;
+}
+
+void
+Mappings::insert_hir_extern_block (HIR::ExternBlock *block)
+{
+  auto id = block->get_mappings ().get_hirid ();
+  rust_assert (lookup_hir_extern_block (id) == nullptr);
+
+  hirExternBlockMappings[id] = block;
+  insert_node_to_hir (block->get_mappings ().get_nodeid (), id);
+}
+
+HIR::ExternBlock *
+Mappings::lookup_hir_extern_block (HirId id)
+{
+  auto it = hirExternBlockMappings.find (id);
+  if (it == hirExternBlockMappings.end ())
+    return nullptr;
+
+  return it->second;
+}
+
+void
+Mappings::insert_hir_extern_item (HIR::ExternalItem *item, HirId parent_block)
+{
+  auto id = item->get_mappings ().get_hirid ();
+  rust_assert (lookup_hir_extern_item (id, nullptr) == nullptr);
+
+  hirExternItemMappings[id] = {item, parent_block};
+  insert_node_to_hir (item->get_mappings ().get_nodeid (), id);
+}
+
+HIR::ExternalItem *
+Mappings::lookup_hir_extern_item (HirId id, HirId *parent_block)
+{
+  auto it = hirExternItemMappings.find (id);
+  if (it == hirExternItemMappings.end ())
+    return nullptr;
+
+  *parent_block = it->second.second;
+
+  return it->second.first;
+}
+
+void
+Mappings::insert_hir_impl_block (HIR::ImplBlock *item)
+{
+  auto id = item->get_mappings ().get_hirid ();
+  rust_assert (lookup_hir_impl_block (id) == nullptr);
+
+  hirImplBlockMappings[id] = item;
+  insert_node_to_hir (item->get_mappings ().get_nodeid (), id);
+}
+
+HIR::ImplBlock *
+Mappings::lookup_hir_impl_block (HirId id)
+{
+  auto it = hirImplBlockMappings.find (id);
+  if (it == hirImplBlockMappings.end ())
+    return nullptr;
+
+  return it->second;
+}
+
+void
+Mappings::insert_module (HIR::Module *module)
+{
+  auto id = module->get_mappings ().get_hirid ();
+  rust_assert (lookup_module (id) == nullptr);
+
+  hirModuleMappings[id] = module;
+  insert_node_to_hir (module->get_mappings ().get_nodeid (), id);
+}
+
+HIR::Module *
+Mappings::lookup_module (HirId id)
+{
+  auto it = hirModuleMappings.find (id);
+  if (it == hirModuleMappings.end ())
+    return nullptr;
+
+  return it->second;
+}
+
+void
+Mappings::insert_hir_implitem (HirId parent_impl_id, HIR::ImplItem *item)
+{
+  auto id = item->get_impl_mappings ().get_hirid ();
+  rust_assert (lookup_hir_implitem (id, nullptr) == nullptr);
+
+  hirImplItemMappings[id]
+    = std::pair<HirId, HIR::ImplItem *> (parent_impl_id, item);
+  insert_node_to_hir (item->get_impl_mappings ().get_nodeid (), id);
+}
+
+HIR::ImplItem *
+Mappings::lookup_hir_implitem (HirId id, HirId *parent_impl_id)
+{
+  auto it = hirImplItemMappings.find (id);
+  if (it == hirImplItemMappings.end ())
+    return nullptr;
+
+  std::pair<HirId, HIR::ImplItem *> &ref = it->second;
+  if (parent_impl_id != nullptr)
+    *parent_impl_id = ref.first;
+
+  return ref.second;
+}
+
+void
+Mappings::insert_hir_expr (HIR::Expr *expr)
+{
+  auto id = expr->get_mappings ().get_hirid ();
+  hirExprMappings[id] = expr;
+
+  insert_node_to_hir (expr->get_mappings ().get_nodeid (), id);
+  insert_location (id, expr->get_locus ());
+}
+
+HIR::Expr *
+Mappings::lookup_hir_expr (HirId id)
+{
+  auto it = hirExprMappings.find (id);
+  if (it == hirExprMappings.end ())
+    return nullptr;
+
+  return it->second;
+}
+
+void
+Mappings::insert_hir_path_expr_seg (HIR::PathExprSegment *expr)
+{
+  auto id = expr->get_mappings ().get_hirid ();
+  rust_assert (lookup_hir_path_expr_seg (id) == nullptr);
+
+  hirPathSegMappings[id] = expr;
+  insert_node_to_hir (expr->get_mappings ().get_nodeid (), id);
+  insert_location (id, expr->get_locus ());
+}
+
+HIR::PathExprSegment *
+Mappings::lookup_hir_path_expr_seg (HirId id)
+{
+  auto it = hirPathSegMappings.find (id);
+  if (it == hirPathSegMappings.end ())
+    return nullptr;
+
+  return it->second;
+}
+
+void
+Mappings::insert_hir_generic_param (HIR::GenericParam *param)
+{
+  auto id = param->get_mappings ().get_hirid ();
+  rust_assert (lookup_hir_generic_param (id) == nullptr);
+
+  hirGenericParamMappings[id] = param;
+  insert_node_to_hir (param->get_mappings ().get_nodeid (), id);
+  insert_location (id, param->get_locus ());
+}
+
+HIR::GenericParam *
+Mappings::lookup_hir_generic_param (HirId id)
+{
+  auto it = hirGenericParamMappings.find (id);
+  if (it == hirGenericParamMappings.end ())
+    return nullptr;
+
+  return it->second;
+}
+
+void
+Mappings::insert_hir_type (HIR::Type *type)
+{
+  auto id = type->get_mappings ().get_hirid ();
+  rust_assert (lookup_hir_type (id) == nullptr);
+
+  hirTypeMappings[id] = type;
+  insert_node_to_hir (type->get_mappings ().get_nodeid (), id);
+}
+
+HIR::Type *
+Mappings::lookup_hir_type (HirId id)
+{
+  auto it = hirTypeMappings.find (id);
+  if (it == hirTypeMappings.end ())
+    return nullptr;
+
+  return it->second;
+}
+
+void
+Mappings::insert_hir_stmt (HIR::Stmt *stmt)
+{
+  auto id = stmt->get_mappings ().get_hirid ();
+  rust_assert (lookup_hir_stmt (id) == nullptr);
+
+  hirStmtMappings[id] = stmt;
+  insert_node_to_hir (stmt->get_mappings ().get_nodeid (), id);
+}
+
+HIR::Stmt *
+Mappings::lookup_hir_stmt (HirId id)
+{
+  auto it = hirStmtMappings.find (id);
+  if (it == hirStmtMappings.end ())
+    return nullptr;
+
+  return it->second;
+}
+
+void
+Mappings::insert_hir_param (HIR::FunctionParam *param)
+{
+  auto id = param->get_mappings ().get_hirid ();
+  rust_assert (lookup_hir_param (id) == nullptr);
+
+  hirParamMappings[id] = param;
+  insert_node_to_hir (param->get_mappings ().get_nodeid (), id);
+}
+
+HIR::FunctionParam *
+Mappings::lookup_hir_param (HirId id)
+{
+  auto it = hirParamMappings.find (id);
+  if (it == hirParamMappings.end ())
+    return nullptr;
+
+  return it->second;
+}
+
+void
+Mappings::insert_hir_self_param (HIR::SelfParam *param)
+{
+  auto id = param->get_mappings ().get_hirid ();
+  rust_assert (lookup_hir_self_param (id) == nullptr);
+
+  hirSelfParamMappings[id] = param;
+  insert_node_to_hir (param->get_mappings ().get_nodeid (), id);
+}
+
+HIR::SelfParam *
+Mappings::lookup_hir_self_param (HirId id)
+{
+  auto it = hirSelfParamMappings.find (id);
+  if (it == hirSelfParamMappings.end ())
+    return nullptr;
+
+  return it->second;
+}
+
+void
+Mappings::insert_hir_struct_field (HIR::StructExprField *field)
+{
+  auto id = field->get_mappings ().get_hirid ();
+  rust_assert (lookup_hir_struct_field (id) == nullptr);
+
+  hirStructFieldMappings[id] = field;
+  insert_node_to_hir (field->get_mappings ().get_nodeid (), id);
+}
+
+HIR::StructExprField *
+Mappings::lookup_hir_struct_field (HirId id)
+{
+  auto it = hirStructFieldMappings.find (id);
+  if (it == hirStructFieldMappings.end ())
+    return nullptr;
+
+  return it->second;
+}
+
+void
+Mappings::insert_hir_pattern (HIR::Pattern *pattern)
+{
+  auto id = pattern->get_pattern_mappings ().get_hirid ();
+  rust_assert (lookup_hir_pattern (id) == nullptr);
+
+  hirPatternMappings[id] = pattern;
+  insert_node_to_hir (pattern->get_pattern_mappings ().get_nodeid (), id);
+}
+
+HIR::Pattern *
+Mappings::lookup_hir_pattern (HirId id)
+{
+  auto it = hirPatternMappings.find (id);
+  if (it == hirPatternMappings.end ())
+    return nullptr;
+
+  return it->second;
+}
+
+void
+Mappings::insert_local_defid_mapping (CrateNum crateNum, LocalDefId id,
+				      HIR::Item *item)
+{
+  rust_assert (lookup_local_defid (crateNum, id) == nullptr);
+  localDefIdMappings[crateNum][id] = item;
+}
+
+HIR::Item *
+Mappings::lookup_local_defid (CrateNum crateNum, LocalDefId id)
+{
+  auto it = localDefIdMappings.find (crateNum);
+  if (it == localDefIdMappings.end ())
+    return nullptr;
+
+  auto iy = it->second.find (id);
+  if (iy == it->second.end ())
+    return nullptr;
+
+  return iy->second;
+}
+
+void
+Mappings::walk_local_defids_for_crate (CrateNum crateNum,
+				       std::function<bool (HIR::Item *)> cb)
+{
+  auto it = localDefIdMappings.find (crateNum);
+  if (it == localDefIdMappings.end ())
+    return;
+
+  for (auto iy = it->second.begin (); iy != it->second.end (); iy++)
+    {
+      if (!cb (iy->second))
+	return;
+    }
+}
+
+void
+Mappings::insert_node_to_hir (NodeId id, HirId ref)
+{
+  nodeIdToHirMappings[id] = ref;
+  hirIdToNodeMappings[ref] = id;
+}
+
+bool
+Mappings::lookup_node_to_hir (NodeId id, HirId *ref)
+{
+  auto it = nodeIdToHirMappings.find (id);
+  if (it == nodeIdToHirMappings.end ())
+    return false;
+
+  *ref = it->second;
+  return true;
+}
+
+bool
+Mappings::lookup_hir_to_node (HirId id, NodeId *ref)
+{
+  auto it = hirIdToNodeMappings.find (id);
+  if (it == hirIdToNodeMappings.end ())
+    return false;
+
+  *ref = it->second;
+  return true;
+}
+
+void
+Mappings::insert_location (HirId id, Location locus)
+{
+  locations[id] = locus;
+}
+
+Location
+Mappings::lookup_location (HirId id)
+{
+  auto it = locations.find (id);
+  if (it == locations.end ())
+    return Location ();
+
+  return it->second;
+}
+
+bool
+Mappings::resolve_nodeid_to_stmt (NodeId id, HIR::Stmt **stmt)
+{
+  auto it = nodeIdToHirMappings.find (id);
+  if (it == nodeIdToHirMappings.end ())
+    return false;
+
+  HirId resolved = it->second;
+  auto resolved_stmt = lookup_hir_stmt (resolved);
+  *stmt = resolved_stmt;
+  return resolved_stmt != nullptr;
+}
+
+void
+Mappings::iterate_impl_items (
+  std::function<bool (HirId, HIR::ImplItem *, HIR::ImplBlock *)> cb)
+{
+  for (auto it = hirImplItemMappings.begin (); it != hirImplItemMappings.end ();
+       it++)
+    {
+      auto id = it->first;
+      auto impl_item = it->second.second;
+      auto impl
+	= lookup_associated_impl (impl_item->get_impl_mappings ().get_hirid ());
+      if (!cb (id, impl_item, impl))
+	return;
+    }
+}
+
+void
+Mappings::iterate_impl_blocks (std::function<bool (HirId, HIR::ImplBlock *)> cb)
+{
+  for (auto it = hirImplBlockMappings.begin ();
+       it != hirImplBlockMappings.end (); it++)
+    {
+      HirId id = it->first;
+      HIR::ImplBlock *impl_block = it->second;
+      if (!cb (id, impl_block))
+	return;
+    }
+}
+
+void
+Mappings::iterate_trait_items (
+  std::function<bool (HIR::TraitItem *, HIR::Trait *)> cb)
+{
+  for (auto it = hirTraitItemMappings.begin ();
+       it != hirTraitItemMappings.end (); it++)
+    {
+      HirId trait_item_id = it->first;
+      HIR::TraitItem *trait_item = it->second;
+      HIR::Trait *trait = lookup_trait_item_mapping (trait_item_id);
+
+      if (!cb (trait_item, trait))
+	return;
+    }
+}
+
+void
+Mappings::insert_macro_def (AST::MacroRulesDefinition *macro)
+{
+  static std::map<std::string, std::function<AST::ASTFragment (
+				 Location, AST::MacroInvocData &)>>
+    builtin_macros = {
+      {"assert", MacroBuiltin::assert},
+      {"file", MacroBuiltin::file},
+      {"line", MacroBuiltin::line},
+      {"column", MacroBuiltin::column},
+      {"include_bytes", MacroBuiltin::include_bytes},
+      {"include_str", MacroBuiltin::include_str},
+      {"compile_error", MacroBuiltin::compile_error},
+      {"concat", MacroBuiltin::concat},
+      {"env", MacroBuiltin::env},
+      {"cfg", MacroBuiltin::cfg},
+      {"include", MacroBuiltin::include},
+    };
+
+  auto outer_attrs = macro->get_outer_attrs ();
+  bool should_be_builtin
+    = std::any_of (outer_attrs.begin (), outer_attrs.end (),
+		   [] (AST::Attribute attr) {
+		     return attr.get_path () == "rustc_builtin_macro";
+		   });
+  if (should_be_builtin)
+    {
+      auto builtin = builtin_macros.find (macro->get_rule_name ());
+      if (builtin != builtin_macros.end ())
+	macro->set_builtin_transcriber (builtin->second);
+      else
+	rust_error_at (macro->get_locus (),
+		       "cannot find a built-in macro with name %qs",
+		       macro->get_rule_name ().c_str ());
+    }
+
+  auto it = macroMappings.find (macro->get_node_id ());
+  rust_assert (it == macroMappings.end ());
+
+  macroMappings[macro->get_node_id ()] = macro;
+}
+
+bool
+Mappings::lookup_macro_def (NodeId id, AST::MacroRulesDefinition **def)
+{
+  auto it = macroMappings.find (id);
+  if (it == macroMappings.end ())
+    return false;
+
+  *def = it->second;
+  return true;
+}
+
+void
+Mappings::insert_visibility (NodeId id, Privacy::ModuleVisibility visibility)
+{
+  visibility_map.insert ({id, visibility});
+}
+
+bool
+Mappings::lookup_visibility (NodeId id, Privacy::ModuleVisibility &def)
+{
+  auto it = visibility_map.find (id);
+  if (it == visibility_map.end ())
+    return false;
+
+  def = it->second;
+  return true;
+}
+
+void
+Mappings::insert_module_child (NodeId module, NodeId child)
+{
+  auto it = module_child_map.find (module);
+  if (it == module_child_map.end ())
+    module_child_map.insert ({module, {child}});
+  else
+    it->second.emplace_back (child);
+}
+
+Optional<std::vector<NodeId> &>
+Mappings::lookup_module_children (NodeId module)
+{
+  auto it = module_child_map.find (module);
+  if (it == module_child_map.end ())
+    return Optional<std::vector<NodeId> &>::none ();
+
+  return Optional<std::vector<NodeId> &>::some (it->second);
+}
+
+void
+Mappings::insert_module_child_item (NodeId module,
+				    Resolver::CanonicalPath child)
+{
+  rust_assert (!child.is_empty ());
+  rust_assert (child.get_node_id () != UNKNOWN_NODEID);
+
+  auto it = module_child_items.find (module);
+  if (it == module_child_items.end ())
+    module_child_items.insert ({module, {child}});
+  else
+    it->second.emplace_back (child);
+}
+
+Optional<std::vector<Resolver::CanonicalPath> &>
+Mappings::lookup_module_chidren_items (NodeId module)
+{
+  auto it = module_child_items.find (module);
+  if (it == module_child_items.end ())
+    return Optional<std::vector<Resolver::CanonicalPath> &>::none ();
+
+  return Optional<std::vector<Resolver::CanonicalPath> &>::some (it->second);
+}
+
+Optional<Resolver::CanonicalPath &>
+Mappings::lookup_module_child (NodeId module, const std::string &item_name)
+{
+  Optional<std::vector<Resolver::CanonicalPath> &> children
+    = lookup_module_chidren_items (module);
+  if (children.is_none ())
+    return Optional<Resolver::CanonicalPath &>::none ();
+
+  // lookup the children to match the name if we can
+  for (auto &child : children.get ())
+    {
+      const std::string &raw_identifier = child.get ();
+      bool found = raw_identifier.compare (item_name) == 0;
+      if (found)
+	return Optional<Resolver::CanonicalPath &>::some (child);
+    }
+  return Optional<Resolver::CanonicalPath &>::none ();
+}
+
+void
+Mappings::insert_child_item_to_parent_module_mapping (NodeId child_item,
+						      NodeId parent_module)
+{
+  child_to_parent_module_map.insert ({child_item, parent_module});
+}
+
+Optional<NodeId>
+Mappings::lookup_parent_module (NodeId child_item)
+{
+  auto it = child_to_parent_module_map.find (child_item);
+  if (it == child_to_parent_module_map.end ())
+    return Optional<NodeId>::none ();
+
+  return Optional<NodeId>::some (it->second);
+}
+
+bool
+Mappings::node_is_module (NodeId query)
+{
+  return module_child_items.find (query) != module_child_items.end ();
+}
+
+void
+Mappings::insert_ast_item (AST::Item *item)
+{
+  auto it = ast_item_mappings.find (item->get_node_id ());
+  rust_assert (it == ast_item_mappings.end ());
+
+  ast_item_mappings[item->get_node_id ()] = item;
+}
+
+bool
+Mappings::lookup_ast_item (NodeId id, AST::Item **result)
+{
+  auto it = ast_item_mappings.find (id);
+  if (it == ast_item_mappings.end ())
+    return false;
+
+  *result = it->second;
+  return true;
+}
+
+} // namespace Analysis
+} // namespace Rust
diff --git a/gcc/rust/util/rust-hir-map.h b/gcc/rust/util/rust-hir-map.h
new file mode 100644
index 00000000000..98fcfe6a6a7
--- /dev/null
+++ b/gcc/rust/util/rust-hir-map.h
@@ -0,0 +1,356 @@
+// 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_HIR_MAP_H
+#define RUST_HIR_MAP_H
+
+#include "rust-optional.h"
+#include "rust-system.h"
+#include "rust-location.h"
+#include "rust-mapping-common.h"
+#include "rust-canonical-path.h"
+#include "rust-ast-full-decls.h"
+#include "rust-hir-full-decls.h"
+#include "rust-lang-item.h"
+#include "rust-privacy-common.h"
+
+namespace Rust {
+namespace Analysis {
+
+class NodeMapping
+{
+public:
+  NodeMapping (CrateNum crateNum, NodeId nodeId, HirId hirId,
+	       LocalDefId localDefId)
+    : crateNum (crateNum), nodeId (nodeId), hirId (hirId),
+      localDefId (localDefId)
+  {}
+
+  static NodeMapping get_error ();
+
+  CrateNum get_crate_num () const;
+  NodeId get_nodeid () const;
+  HirId get_hirid () const;
+  LocalDefId get_local_defid () const;
+  DefId get_defid () const;
+
+  static DefId get_defid (CrateNum crate_num, LocalDefId local_defid);
+
+  std::string as_string () const;
+
+  bool is_equal (const NodeMapping &other) const
+  {
+    return get_crate_num () == other.get_crate_num ()
+	   && get_nodeid () == other.get_nodeid ()
+	   && get_hirid () == other.get_hirid ()
+	   && get_local_defid () == other.get_local_defid ();
+  }
+
+private:
+  CrateNum crateNum;
+  NodeId nodeId;
+  HirId hirId;
+  LocalDefId localDefId;
+};
+
+class Mappings
+{
+public:
+  static Mappings *get ();
+  ~Mappings ();
+
+  CrateNum get_next_crate_num (const std::string &name);
+  void set_current_crate (CrateNum crateNum);
+  CrateNum get_current_crate () const;
+  bool get_crate_name (CrateNum crate_num, std::string &name) const;
+  void set_crate_name (CrateNum crate_num, const std::string &name);
+  std::string get_current_crate_name () const;
+  bool lookup_crate_name (const std::string &crate_name,
+			  CrateNum &resolved_crate_num) const;
+  bool crate_num_to_nodeid (const CrateNum &crate_num, NodeId &node_id) const;
+  bool node_is_crate (NodeId node_id) const;
+
+  NodeId get_next_node_id ();
+  HirId get_next_hir_id () { return get_next_hir_id (get_current_crate ()); }
+  HirId get_next_hir_id (CrateNum crateNum);
+  LocalDefId get_next_localdef_id ()
+  {
+    return get_next_localdef_id (get_current_crate ());
+  }
+  LocalDefId get_next_localdef_id (CrateNum crateNum);
+
+  AST::Crate &get_ast_crate (CrateNum crateNum);
+  AST::Crate &get_ast_crate_by_node_id (NodeId id);
+  AST::Crate &insert_ast_crate (std::unique_ptr<AST::Crate> &&crate,
+				CrateNum crate_num);
+  HIR::Crate &insert_hir_crate (std::unique_ptr<HIR::Crate> &&crate);
+  HIR::Crate &get_hir_crate (CrateNum crateNum);
+  bool is_local_hirid_crate (HirId crateNum);
+
+  void insert_defid_mapping (DefId id, HIR::Item *item);
+  HIR::Item *lookup_defid (DefId id);
+
+  void insert_local_defid_mapping (CrateNum crateNum, LocalDefId id,
+				   HIR::Item *item);
+  HIR::Item *lookup_local_defid (CrateNum crateNum, LocalDefId id);
+
+  void insert_hir_item (HIR::Item *item);
+  HIR::Item *lookup_hir_item (HirId id);
+
+  void insert_hir_trait_item (HIR::TraitItem *item);
+  HIR::TraitItem *lookup_hir_trait_item (HirId id);
+
+  void insert_hir_extern_block (HIR::ExternBlock *block);
+  HIR::ExternBlock *lookup_hir_extern_block (HirId id);
+
+  void insert_hir_extern_item (HIR::ExternalItem *item, HirId parent_block);
+  HIR::ExternalItem *lookup_hir_extern_item (HirId id, HirId *parent_block);
+
+  void insert_hir_impl_block (HIR::ImplBlock *item);
+  HIR::ImplBlock *lookup_hir_impl_block (HirId id);
+
+  void insert_module (HIR::Module *module);
+  HIR::Module *lookup_module (HirId id);
+
+  void insert_hir_implitem (HirId parent_impl_id, HIR::ImplItem *item);
+  HIR::ImplItem *lookup_hir_implitem (HirId id, HirId *parent_impl_id);
+
+  void insert_hir_expr (HIR::Expr *expr);
+  HIR::Expr *lookup_hir_expr (HirId id);
+
+  void insert_hir_path_expr_seg (HIR::PathExprSegment *expr);
+  HIR::PathExprSegment *lookup_hir_path_expr_seg (HirId id);
+
+  void insert_hir_generic_param (HIR::GenericParam *expr);
+  HIR::GenericParam *lookup_hir_generic_param (HirId id);
+
+  void insert_hir_type (HIR::Type *type);
+  HIR::Type *lookup_hir_type (HirId id);
+
+  void insert_hir_stmt (HIR::Stmt *stmt);
+  HIR::Stmt *lookup_hir_stmt (HirId id);
+
+  void insert_hir_param (HIR::FunctionParam *type);
+  HIR::FunctionParam *lookup_hir_param (HirId id);
+
+  void insert_hir_self_param (HIR::SelfParam *type);
+  HIR::SelfParam *lookup_hir_self_param (HirId id);
+
+  void insert_hir_struct_field (HIR::StructExprField *type);
+  HIR::StructExprField *lookup_hir_struct_field (HirId id);
+
+  void insert_hir_pattern (HIR::Pattern *pattern);
+  HIR::Pattern *lookup_hir_pattern (HirId id);
+
+  void walk_local_defids_for_crate (CrateNum crateNum,
+				    std::function<bool (HIR::Item *)> cb);
+
+  void insert_node_to_hir (NodeId id, HirId ref);
+  bool lookup_node_to_hir (NodeId id, HirId *ref);
+  bool lookup_hir_to_node (HirId id, NodeId *ref);
+
+  void insert_location (HirId id, Location locus);
+  Location lookup_location (HirId id);
+
+  bool resolve_nodeid_to_stmt (NodeId id, HIR::Stmt **stmt);
+
+  std::set<HirId> &get_hirids_within_crate (CrateNum crate)
+  {
+    return hirNodesWithinCrate[crate];
+  }
+
+  void insert_impl_item_mapping (HirId impl_item_id, HIR::ImplBlock *impl)
+  {
+    rust_assert (hirImplItemsToImplMappings.find (impl_item_id)
+		 == hirImplItemsToImplMappings.end ());
+    hirImplItemsToImplMappings[impl_item_id] = impl;
+  }
+
+  HIR::ImplBlock *lookup_associated_impl (HirId impl_item_id)
+  {
+    auto lookup = hirImplItemsToImplMappings.find (impl_item_id);
+    rust_assert (lookup != hirImplItemsToImplMappings.end ());
+    return lookup->second;
+  }
+
+  void iterate_impl_items (
+    std::function<bool (HirId, HIR::ImplItem *, HIR::ImplBlock *)> cb);
+
+  void iterate_impl_blocks (std::function<bool (HirId, HIR::ImplBlock *)> cb);
+
+  void iterate_trait_items (
+    std::function<bool (HIR::TraitItem *item, HIR::Trait *)> cb);
+
+  bool is_impl_item (HirId id)
+  {
+    HirId parent_impl_block_id = UNKNOWN_HIRID;
+    return lookup_hir_implitem (id, &parent_impl_block_id) != nullptr;
+  }
+
+  void insert_trait_item_mapping (HirId trait_item_id, HIR::Trait *trait)
+  {
+    rust_assert (hirTraitItemsToTraitMappings.find (trait_item_id)
+		 == hirTraitItemsToTraitMappings.end ());
+    hirTraitItemsToTraitMappings[trait_item_id] = trait;
+  }
+
+  HIR::Trait *lookup_trait_item_mapping (HirId trait_item_id)
+  {
+    auto lookup = hirTraitItemsToTraitMappings.find (trait_item_id);
+    rust_assert (lookup != hirTraitItemsToTraitMappings.end ());
+    return lookup->second;
+  }
+
+  void insert_canonical_path (NodeId id, const Resolver::CanonicalPath path)
+  {
+    const Resolver::CanonicalPath *p = nullptr;
+    if (lookup_canonical_path (id, &p))
+      {
+	// if we have already stored a canonical path this is ok so long as
+	// this new path is equal or is smaller that the existing one but in
+	// that case we ignore it.
+	if (p->is_equal (path))
+	  return;
+	else
+	  {
+	    rust_assert (p->size () >= path.size ());
+	    return;
+	  }
+      }
+
+    paths.emplace (id, std::move (path));
+  }
+
+  bool lookup_canonical_path (NodeId id, const Resolver::CanonicalPath **path)
+  {
+    auto it = paths.find (id);
+    if (it == paths.end ())
+      return false;
+
+    *path = &it->second;
+    return true;
+  }
+
+  void insert_lang_item (RustLangItem::ItemType item_type, DefId id)
+  {
+    auto it = lang_item_mappings.find (item_type);
+    rust_assert (it == lang_item_mappings.end ());
+
+    lang_item_mappings[item_type] = id;
+  }
+
+  bool lookup_lang_item (RustLangItem::ItemType item_type, DefId *id)
+  {
+    auto it = lang_item_mappings.find (item_type);
+    if (it == lang_item_mappings.end ())
+      return false;
+
+    *id = it->second;
+    return true;
+  }
+
+  void insert_macro_def (AST::MacroRulesDefinition *macro);
+
+  bool lookup_macro_def (NodeId id, AST::MacroRulesDefinition **def);
+
+  void insert_visibility (NodeId id, Privacy::ModuleVisibility visibility);
+  bool lookup_visibility (NodeId id, Privacy::ModuleVisibility &def);
+
+  void insert_module_child (NodeId module, NodeId child);
+  Optional<std::vector<NodeId> &> lookup_module_children (NodeId module);
+
+  void insert_module_child_item (NodeId module, Resolver::CanonicalPath item);
+  Optional<std::vector<Resolver::CanonicalPath> &>
+  lookup_module_chidren_items (NodeId module);
+  Optional<Resolver::CanonicalPath &>
+  lookup_module_child (NodeId module, const std::string &item_name);
+
+  void insert_child_item_to_parent_module_mapping (NodeId child_item,
+						   NodeId parent_module);
+  Optional<NodeId> lookup_parent_module (NodeId child_item);
+  bool node_is_module (NodeId query);
+
+  void insert_ast_item (AST::Item *item);
+  bool lookup_ast_item (NodeId id, AST::Item **result);
+
+private:
+  Mappings ();
+
+  CrateNum crateNumItr;
+  CrateNum currentCrateNum;
+  HirId hirIdIter;
+  NodeId nodeIdIter;
+  std::map<CrateNum, LocalDefId> localIdIter;
+
+  std::map<NodeId, CrateNum> crate_node_to_crate_num;
+  std::map<CrateNum, AST::Crate *> ast_crate_mappings;
+  std::map<CrateNum, HIR::Crate *> hir_crate_mappings;
+  std::map<DefId, HIR::Item *> defIdMappings;
+  std::map<CrateNum, std::map<LocalDefId, HIR::Item *>> localDefIdMappings;
+
+  std::map<HirId, HIR::Module *> hirModuleMappings;
+  std::map<HirId, HIR::Item *> hirItemMappings;
+  std::map<HirId, HIR::Type *> hirTypeMappings;
+  std::map<HirId, HIR::Expr *> hirExprMappings;
+  std::map<HirId, HIR::Stmt *> hirStmtMappings;
+  std::map<HirId, HIR::FunctionParam *> hirParamMappings;
+  std::map<HirId, HIR::StructExprField *> hirStructFieldMappings;
+  std::map<HirId, std::pair<HirId, HIR::ImplItem *>> hirImplItemMappings;
+  std::map<HirId, HIR::SelfParam *> hirSelfParamMappings;
+  std::map<HirId, HIR::ImplBlock *> hirImplItemsToImplMappings;
+  std::map<HirId, HIR::ImplBlock *> hirImplBlockMappings;
+  std::map<HirId, HIR::TraitItem *> hirTraitItemMappings;
+  std::map<HirId, HIR::ExternBlock *> hirExternBlockMappings;
+  std::map<HirId, std::pair<HIR::ExternalItem *, HirId>> hirExternItemMappings;
+  std::map<HirId, HIR::PathExprSegment *> hirPathSegMappings;
+  std::map<HirId, HIR::GenericParam *> hirGenericParamMappings;
+  std::map<HirId, HIR::Trait *> hirTraitItemsToTraitMappings;
+  std::map<HirId, HIR::Pattern *> hirPatternMappings;
+  std::map<RustLangItem::ItemType, DefId> lang_item_mappings;
+  std::map<NodeId, const Resolver::CanonicalPath> paths;
+  std::map<NodeId, Location> locations;
+  std::map<NodeId, HirId> nodeIdToHirMappings;
+  std::map<HirId, NodeId> hirIdToNodeMappings;
+
+  // all hirid nodes
+  std::map<CrateNum, std::set<HirId>> hirNodesWithinCrate;
+
+  // macros
+  std::map<NodeId, AST::MacroRulesDefinition *> macroMappings;
+
+  // crate names
+  std::map<CrateNum, std::string> crate_names;
+
+  // Low level visibility map for each DefId
+  std::map<NodeId, Privacy::ModuleVisibility> visibility_map;
+
+  // Module tree maps
+
+  // Maps each module's node id to a list of its children
+  std::map<NodeId, std::vector<NodeId>> module_child_map;
+  std::map<NodeId, std::vector<Resolver::CanonicalPath>> module_child_items;
+  std::map<NodeId, NodeId> child_to_parent_module_map;
+
+  // AST mappings
+  std::map<NodeId, AST::Item *> ast_item_mappings;
+};
+
+} // namespace Analysis
+} // namespace Rust
+
+#endif // RUST_HIR_MAP_H
diff --git a/gcc/rust/util/rust-identifier.h b/gcc/rust/util/rust-identifier.h
new file mode 100644
index 00000000000..0b5cb70b373
--- /dev/null
+++ b/gcc/rust/util/rust-identifier.h
@@ -0,0 +1,49 @@
+// 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_IDENTIFIER
+#define RUST_IDENTIFIER
+
+#include "rust-canonical-path.h"
+#include "rust-location.h"
+
+namespace Rust {
+
+struct RustIdent
+{
+  Resolver::CanonicalPath path;
+  Location locus;
+
+  RustIdent (const Resolver::CanonicalPath &path, Location locus)
+    : path (path), locus (locus)
+  {}
+
+  RustIdent (const RustIdent &other) : path (other.path), locus (other.locus) {}
+
+  RustIdent &operator= (const RustIdent &other)
+  {
+    path = other.path;
+    locus = other.locus;
+
+    return *this;
+  }
+};
+
+} // namespace Rust
+
+#endif // RUST_IDENTIFIER
diff --git a/gcc/rust/util/rust-lang-item.h b/gcc/rust/util/rust-lang-item.h
new file mode 100644
index 00000000000..9d1ee900aec
--- /dev/null
+++ b/gcc/rust/util/rust-lang-item.h
@@ -0,0 +1,377 @@
+// 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/>.
+
+#include "rust-system.h"
+#include "operator.h"
+
+namespace Rust {
+namespace Analysis {
+
+// https://github.com/rust-lang/rust/blob/master/library/core/src/ops/arith.rs
+class RustLangItem
+{
+public:
+  enum ItemType
+  {
+    ADD,
+    SUBTRACT,
+    MULTIPLY,
+    DIVIDE,
+    REMAINDER,
+    BITAND,
+    BITOR,
+    BITXOR,
+    SHL,
+    SHR,
+
+    NEGATION,
+    NOT,
+
+    ADD_ASSIGN,
+    SUB_ASSIGN,
+    MUL_ASSIGN,
+    DIV_ASSIGN,
+    REM_ASSIGN,
+    BITAND_ASSIGN,
+    BITOR_ASSIGN,
+    BITXOR_ASSIGN,
+    SHL_ASSIGN,
+    SHR_ASSIGN,
+
+    DEREF,
+    DEREF_MUT,
+
+    // https://github.com/rust-lang/rust/blob/master/library/core/src/ops/index.rs
+    INDEX,
+    INDEX_MUT,
+
+    // https://github.com/rust-lang/rust/blob/master/library/core/src/ops/range.rs
+    RANGE_FULL,
+    RANGE,
+    RANGE_FROM,
+    RANGE_TO,
+    RANGE_INCLUSIVE,
+    RANGE_TO_INCLUSIVE,
+
+    // https://github.com/rust-lang/rust/blob/master/library/core/src/ptr/const_ptr.rs
+    CONST_PTR,
+    MUT_PTR,
+    CONST_SLICE_PTR,
+
+    UNKNOWN,
+  };
+
+  static ItemType Parse (const std::string &item)
+  {
+    if (item.compare ("add") == 0)
+      {
+	return ItemType::ADD;
+      }
+    else if (item.compare ("sub") == 0)
+      {
+	return ItemType::SUBTRACT;
+      }
+    else if (item.compare ("mul") == 0)
+      {
+	return ItemType::MULTIPLY;
+      }
+    else if (item.compare ("div") == 0)
+      {
+	return ItemType::DIVIDE;
+      }
+    else if (item.compare ("rem") == 0)
+      {
+	return ItemType::REMAINDER;
+      }
+    else if (item.compare ("bitand") == 0)
+      {
+	return ItemType::BITAND;
+      }
+    else if (item.compare ("bitor") == 0)
+      {
+	return ItemType::BITOR;
+      }
+    else if (item.compare ("bitxor") == 0)
+      {
+	return ItemType::BITXOR;
+      }
+    else if (item.compare ("shl") == 0)
+      {
+	return ItemType::SHL;
+      }
+    else if (item.compare ("shr") == 0)
+      {
+	return ItemType::SHR;
+      }
+    else if (item.compare ("neg") == 0)
+      {
+	return ItemType::NEGATION;
+      }
+    else if (item.compare ("not") == 0)
+      {
+	return ItemType::NOT;
+      }
+    else if (item.compare ("add_assign") == 0)
+      {
+	return ItemType::ADD_ASSIGN;
+      }
+    else if (item.compare ("sub_assign") == 0)
+      {
+	return ItemType::SUB_ASSIGN;
+      }
+    else if (item.compare ("mul_assign") == 0)
+      {
+	return ItemType::MUL_ASSIGN;
+      }
+    else if (item.compare ("div_assign") == 0)
+      {
+	return ItemType::DIV_ASSIGN;
+      }
+    else if (item.compare ("rem_assign") == 0)
+      {
+	return ItemType::REM_ASSIGN;
+      }
+    else if (item.compare ("bitand_assign") == 0)
+      {
+	return ItemType::BITAND_ASSIGN;
+      }
+    else if (item.compare ("bitor_assign") == 0)
+      {
+	return ItemType::BITOR_ASSIGN;
+      }
+    else if (item.compare ("bitxor_assign") == 0)
+      {
+	return ItemType::BITXOR_ASSIGN;
+      }
+    else if (item.compare ("shl_assign") == 0)
+      {
+	return ItemType::SHL_ASSIGN;
+      }
+    else if (item.compare ("shr_assign") == 0)
+      {
+	return ItemType::SHR_ASSIGN;
+      }
+    else if (item.compare ("deref") == 0)
+      {
+	return ItemType::DEREF;
+      }
+    else if (item.compare ("deref_mut") == 0)
+      {
+	return ItemType::DEREF_MUT;
+      }
+    else if (item.compare ("index") == 0)
+      {
+	return ItemType::INDEX;
+      }
+    else if (item.compare ("index_mut") == 0)
+      {
+	return ItemType::INDEX_MUT;
+      }
+    else if (item.compare ("RangeFull") == 0)
+      {
+	return ItemType::RANGE_FULL;
+      }
+    else if (item.compare ("Range") == 0)
+      {
+	return ItemType::RANGE;
+      }
+    else if (item.compare ("RangeFrom") == 0)
+      {
+	return ItemType::RANGE_FROM;
+      }
+    else if (item.compare ("RangeTo") == 0)
+      {
+	return ItemType::RANGE_TO;
+      }
+    else if (item.compare ("RangeInclusive") == 0)
+      {
+	return ItemType::RANGE_INCLUSIVE;
+      }
+    else if (item.compare ("RangeToInclusive") == 0)
+      {
+	return ItemType::RANGE_TO_INCLUSIVE;
+      }
+    else if (item.compare ("const_ptr") == 0)
+      {
+	return ItemType::CONST_PTR;
+      }
+    else if (item.compare ("mut_ptr") == 0)
+      {
+	return ItemType::MUT_PTR;
+      }
+    else if (item.compare ("const_slice_ptr") == 0)
+      {
+	return ItemType::CONST_SLICE_PTR;
+      }
+
+    return ItemType::UNKNOWN;
+  }
+
+  static std::string ToString (ItemType type)
+  {
+    switch (type)
+      {
+      case ADD:
+	return "add";
+      case SUBTRACT:
+	return "sub";
+      case MULTIPLY:
+	return "mul";
+      case DIVIDE:
+	return "div";
+      case REMAINDER:
+	return "rem";
+      case BITAND:
+	return "bitand";
+      case BITOR:
+	return "bitor";
+      case BITXOR:
+	return "bitxor";
+      case SHL:
+	return "shl";
+      case SHR:
+	return "shr";
+      case NEGATION:
+	return "neg";
+      case NOT:
+	return "not";
+      case ADD_ASSIGN:
+	return "add_assign";
+      case SUB_ASSIGN:
+	return "sub_assign";
+      case MUL_ASSIGN:
+	return "mul_assign";
+      case DIV_ASSIGN:
+	return "div_assign";
+      case REM_ASSIGN:
+	return "rem_assign";
+      case BITAND_ASSIGN:
+	return "bitand_assign";
+      case BITOR_ASSIGN:
+	return "bitor_assign";
+      case BITXOR_ASSIGN:
+	return "bitxor_assign";
+      case SHL_ASSIGN:
+	return "shl_assign";
+      case SHR_ASSIGN:
+	return "shr_assign";
+      case DEREF:
+	return "deref";
+      case DEREF_MUT:
+	return "deref_mut";
+      case INDEX:
+	return "index";
+      case INDEX_MUT:
+	return "index_mut";
+      case RANGE_FULL:
+	return "RangeFull";
+      case RANGE:
+	return "Range";
+      case RANGE_FROM:
+	return "RangeFrom";
+      case RANGE_TO:
+	return "RangeTo";
+      case RANGE_INCLUSIVE:
+	return "RangeInclusive";
+      case RANGE_TO_INCLUSIVE:
+	return "RangeToInclusive";
+      case CONST_PTR:
+	return "const_ptr";
+      case MUT_PTR:
+	return "mut_ptr";
+      case CONST_SLICE_PTR:
+	return "const_slice_ptr";
+
+      case UNKNOWN:
+	return "<UNKNOWN>";
+      }
+    return "<UNKNOWN>";
+  }
+
+  static ItemType OperatorToLangItem (ArithmeticOrLogicalOperator op)
+  {
+    switch (op)
+      {
+      case ArithmeticOrLogicalOperator::ADD:
+	return ItemType::ADD;
+      case ArithmeticOrLogicalOperator::SUBTRACT:
+	return ItemType::SUBTRACT;
+      case ArithmeticOrLogicalOperator::MULTIPLY:
+	return ItemType::MULTIPLY;
+      case ArithmeticOrLogicalOperator::DIVIDE:
+	return ItemType::DIVIDE;
+      case ArithmeticOrLogicalOperator::MODULUS:
+	return ItemType::REMAINDER;
+      case ArithmeticOrLogicalOperator::BITWISE_AND:
+	return ItemType::BITAND;
+      case ArithmeticOrLogicalOperator::BITWISE_OR:
+	return ItemType::BITOR;
+      case ArithmeticOrLogicalOperator::BITWISE_XOR:
+	return ItemType::BITXOR;
+      case ArithmeticOrLogicalOperator::LEFT_SHIFT:
+	return ItemType::SHL;
+      case ArithmeticOrLogicalOperator::RIGHT_SHIFT:
+	return ItemType::SHR;
+      }
+    return ItemType::UNKNOWN;
+  }
+
+  static ItemType
+  CompoundAssignmentOperatorToLangItem (ArithmeticOrLogicalOperator op)
+  {
+    switch (op)
+      {
+      case ArithmeticOrLogicalOperator::ADD:
+	return ItemType::ADD_ASSIGN;
+      case ArithmeticOrLogicalOperator::SUBTRACT:
+	return ItemType::SUB_ASSIGN;
+      case ArithmeticOrLogicalOperator::MULTIPLY:
+	return ItemType::MUL_ASSIGN;
+      case ArithmeticOrLogicalOperator::DIVIDE:
+	return ItemType::DIV_ASSIGN;
+      case ArithmeticOrLogicalOperator::MODULUS:
+	return ItemType::REM_ASSIGN;
+      case ArithmeticOrLogicalOperator::BITWISE_AND:
+	return ItemType::BITAND_ASSIGN;
+      case ArithmeticOrLogicalOperator::BITWISE_OR:
+	return ItemType::BITOR_ASSIGN;
+      case ArithmeticOrLogicalOperator::BITWISE_XOR:
+	return ItemType::BITXOR_ASSIGN;
+      case ArithmeticOrLogicalOperator::LEFT_SHIFT:
+	return ItemType::SHL_ASSIGN;
+      case ArithmeticOrLogicalOperator::RIGHT_SHIFT:
+	return ItemType::SHR_ASSIGN;
+      }
+    return ItemType::UNKNOWN;
+  }
+
+  static ItemType NegationOperatorToLangItem (NegationOperator op)
+  {
+    switch (op)
+      {
+      case NegationOperator::NEGATE:
+	return ItemType::NEGATION;
+      case NegationOperator::NOT:
+	return ItemType::NOT;
+      }
+    return ItemType::UNKNOWN;
+  }
+};
+
+} // namespace Analysis
+} // namespace Rust
diff --git a/gcc/rust/util/rust-mapping-common.h b/gcc/rust/util/rust-mapping-common.h
new file mode 100644
index 00000000000..c440ae98931
--- /dev/null
+++ b/gcc/rust/util/rust-mapping-common.h
@@ -0,0 +1,85 @@
+// 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_MAPPING_COMMON
+#define RUST_MAPPING_COMMON
+
+#include "rust-system.h"
+
+namespace Rust {
+
+// refers to a Crate
+typedef uint32_t CrateNum;
+// refers to any node in the AST in current Crate
+typedef uint32_t NodeId;
+// refers to any node in the HIR for the current crate
+typedef uint32_t HirId;
+// refers to any top-level decl in HIR
+typedef uint32_t LocalDefId;
+
+struct DefId
+{
+  CrateNum crateNum;
+  LocalDefId localDefId;
+
+  bool operator== (const DefId &other) const
+  {
+    return this->crateNum == other.crateNum
+	   && this->localDefId == other.localDefId;
+  }
+
+  bool operator!= (const DefId &other) const { return !(*this == other); }
+
+  bool operator< (const DefId &other) const
+  {
+    return ((uint64_t) this->crateNum << 32 | this->localDefId)
+	   < ((uint64_t) other.crateNum << 32 | other.localDefId);
+  }
+
+  std::string as_string () const
+  {
+    std::string buf;
+    buf += std::to_string (crateNum);
+    buf += " "; // or anything else
+    buf += std::to_string (localDefId);
+    return buf;
+  }
+};
+
+#define UNKNOWN_CREATENUM ((uint32_t) (0))
+#define UNKNOWN_NODEID ((uint32_t) (0))
+#define UNKNOWN_HIRID ((uint32_t) (0))
+#define UNKNOWN_LOCAL_DEFID ((uint32_t) (0))
+#define UNKNOWN_DEFID (DefId{0, 0})
+
+} // namespace Rust
+
+namespace std {
+template <> struct hash<Rust::DefId>
+{
+  size_t operator() (const Rust::DefId &id) const noexcept
+  {
+    // TODO: Check if we can improve performance by having a better hash
+    // algorithm for `DefId`s
+    return hash<uint32_t> () (hash<uint32_t> () (id.crateNum)
+			      + hash<uint32_t> () (id.localDefId));
+  }
+};
+} // namespace std
+
+#endif // RUST_MAPPING_COMMON
diff --git a/gcc/rust/util/rust-stacked-contexts.h b/gcc/rust/util/rust-stacked-contexts.h
new file mode 100644
index 00000000000..c34eb907f06
--- /dev/null
+++ b/gcc/rust/util/rust-stacked-contexts.h
@@ -0,0 +1,86 @@
+// 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_CONTEXT_STACK_H
+#define RUST_CONTEXT_STACK_H
+
+#include "rust-system.h"
+
+namespace Rust {
+
+/**
+ * Context stack util class. This class is useful for situations where you can
+ * enter the same kind of context multiple times. For example, when dealing with
+ * unsafe contexts, you might be tempted to simply keep a boolean value.
+ *
+ * ```rust
+ * let a = 15;
+ * unsafe { // we set the boolean to true
+ *     // Now unsafe operations are allowed!
+ *     let b = *(&a as *const i32);
+ *     let c = std::mem::transmute<i32, f32>(b); // Urgh!
+ * } // we set it to false
+ * ```
+ *
+ * However, since the language allows nested unsafe blocks, you may run into
+ * this situation:
+ *
+ * ```rust
+ * unsafe { // we set the boolean to true
+ *     unsafe { // we set the boolean to true
+ *     } // we set it to false
+ *
+ *     // Now unsafe operations are forbidden again, the boolean is false
+ *     let f = std::mem::transmute<i32, f32>(15); // Error!
+ * } // we set it to false
+ * ```
+ */
+template <typename T> class StackedContexts
+{
+public:
+  /**
+   * Enter a special context
+   */
+  void enter (T value) { stack.emplace_back (value); }
+
+  /**
+   * Exit a special context
+   */
+  T exit ()
+  {
+    rust_assert (!stack.empty ());
+
+    auto last = stack.back ();
+    stack.pop_back ();
+
+    return last;
+  }
+
+  /**
+   * Are we currently inside of a special context?
+   */
+  bool is_in_context () const { return !stack.empty (); }
+
+private:
+  /* Actual data */
+  std::vector<T> stack;
+};
+
+} // namespace Rust
+
+#endif /* !RUST_CONTEXT_STACK_H */
-- 
2.25.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [PATCH Rust front-end v2 22/37] gccrs: Add type resolution and trait solving pass
  2022-08-24 11:59 Rust frontend patches v2 herron.philip
                   ` (20 preceding siblings ...)
  2022-08-24 11:59 ` [PATCH Rust front-end v2 21/37] gccrs: Add helpers mappings canonical path and lang items herron.philip
@ 2022-08-24 11:59 ` herron.philip
  2022-08-24 11:59 ` [PATCH Rust front-end v2 23/37] gccrs: Add unsafe checks for Rust herron.philip
                   ` (15 subsequent siblings)
  37 siblings, 0 replies; 59+ messages in thread
From: herron.philip @ 2022-08-24 11:59 UTC (permalink / raw)
  To: gcc-patches; +Cc: gcc-rust, Philip Herron

From: Philip Herron <philip.herron@embecosm.com>

TODO
---
 gcc/rust/typecheck/rust-autoderef.cc          |  398 +++
 gcc/rust/typecheck/rust-autoderef.h           |  178 +
 gcc/rust/typecheck/rust-casts.cc              |  292 ++
 gcc/rust/typecheck/rust-casts.h               |   53 +
 gcc/rust/typecheck/rust-coercion.cc           |  357 ++
 gcc/rust/typecheck/rust-coercion.h            |   93 +
 gcc/rust/typecheck/rust-hir-dot-operator.cc   |  263 ++
 gcc/rust/typecheck/rust-hir-dot-operator.h    |   81 +
 .../rust-hir-inherent-impl-overlap.h          |  186 ++
 gcc/rust/typecheck/rust-hir-path-probe.h      |  540 +++
 gcc/rust/typecheck/rust-hir-trait-ref.h       |  472 +++
 gcc/rust/typecheck/rust-hir-trait-resolve.cc  |  599 ++++
 gcc/rust/typecheck/rust-hir-trait-resolve.h   |   87 +
 gcc/rust/typecheck/rust-hir-type-bounds.h     |   77 +
 .../typecheck/rust-hir-type-check-base.cc     |  439 +++
 gcc/rust/typecheck/rust-hir-type-check-base.h |   80 +
 .../typecheck/rust-hir-type-check-enumitem.cc |  213 ++
 .../typecheck/rust-hir-type-check-enumitem.h  |   50 +
 .../typecheck/rust-hir-type-check-expr.cc     | 1567 +++++++++
 gcc/rust/typecheck/rust-hir-type-check-expr.h |  131 +
 .../typecheck/rust-hir-type-check-implitem.cc |  583 ++++
 .../typecheck/rust-hir-type-check-implitem.h  |  114 +
 .../typecheck/rust-hir-type-check-item.cc     |  237 ++
 gcc/rust/typecheck/rust-hir-type-check-item.h |   58 +
 .../typecheck/rust-hir-type-check-path.cc     |  467 +++
 .../typecheck/rust-hir-type-check-pattern.cc  |  416 +++
 .../typecheck/rust-hir-type-check-pattern.h   |   62 +
 .../typecheck/rust-hir-type-check-stmt.cc     |  498 +++
 gcc/rust/typecheck/rust-hir-type-check-stmt.h |   96 +
 .../rust-hir-type-check-struct-field.h        |   59 +
 .../typecheck/rust-hir-type-check-struct.cc   |  340 ++
 .../typecheck/rust-hir-type-check-toplevel.cc |  364 +++
 .../typecheck/rust-hir-type-check-toplevel.h  |   56 +
 .../typecheck/rust-hir-type-check-type.cc     |  838 +++++
 gcc/rust/typecheck/rust-hir-type-check-type.h |  130 +
 .../typecheck/rust-hir-type-check-util.cc     |   41 +
 gcc/rust/typecheck/rust-hir-type-check-util.h |   50 +
 gcc/rust/typecheck/rust-hir-type-check.cc     |  295 ++
 gcc/rust/typecheck/rust-hir-type-check.h      |  379 +++
 .../typecheck/rust-substitution-mapper.cc     |   77 +
 gcc/rust/typecheck/rust-substitution-mapper.h |  394 +++
 gcc/rust/typecheck/rust-tycheck-dump.h        |  239 ++
 gcc/rust/typecheck/rust-tyctx.cc              |  155 +
 gcc/rust/typecheck/rust-tyty-bounds.cc        |  462 +++
 gcc/rust/typecheck/rust-tyty-call.cc          |  263 ++
 gcc/rust/typecheck/rust-tyty-call.h           |  147 +
 gcc/rust/typecheck/rust-tyty-cmp.h            | 1554 +++++++++
 gcc/rust/typecheck/rust-tyty-rules.h          | 1584 +++++++++
 gcc/rust/typecheck/rust-tyty-visitor.h        |   88 +
 gcc/rust/typecheck/rust-tyty.cc               | 2885 +++++++++++++++++
 gcc/rust/typecheck/rust-tyty.h                | 2533 +++++++++++++++
 51 files changed, 21620 insertions(+)
 create mode 100644 gcc/rust/typecheck/rust-autoderef.cc
 create mode 100644 gcc/rust/typecheck/rust-autoderef.h
 create mode 100644 gcc/rust/typecheck/rust-casts.cc
 create mode 100644 gcc/rust/typecheck/rust-casts.h
 create mode 100644 gcc/rust/typecheck/rust-coercion.cc
 create mode 100644 gcc/rust/typecheck/rust-coercion.h
 create mode 100644 gcc/rust/typecheck/rust-hir-dot-operator.cc
 create mode 100644 gcc/rust/typecheck/rust-hir-dot-operator.h
 create mode 100644 gcc/rust/typecheck/rust-hir-inherent-impl-overlap.h
 create mode 100644 gcc/rust/typecheck/rust-hir-path-probe.h
 create mode 100644 gcc/rust/typecheck/rust-hir-trait-ref.h
 create mode 100644 gcc/rust/typecheck/rust-hir-trait-resolve.cc
 create mode 100644 gcc/rust/typecheck/rust-hir-trait-resolve.h
 create mode 100644 gcc/rust/typecheck/rust-hir-type-bounds.h
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-base.cc
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-base.h
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-enumitem.cc
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-enumitem.h
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-expr.cc
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-expr.h
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-implitem.cc
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-implitem.h
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-item.cc
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-item.h
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-path.cc
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-pattern.cc
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-pattern.h
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-stmt.cc
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-stmt.h
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-struct-field.h
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-struct.cc
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-toplevel.cc
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-toplevel.h
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-type.cc
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-type.h
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-util.cc
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check-util.h
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check.cc
 create mode 100644 gcc/rust/typecheck/rust-hir-type-check.h
 create mode 100644 gcc/rust/typecheck/rust-substitution-mapper.cc
 create mode 100644 gcc/rust/typecheck/rust-substitution-mapper.h
 create mode 100644 gcc/rust/typecheck/rust-tycheck-dump.h
 create mode 100644 gcc/rust/typecheck/rust-tyctx.cc
 create mode 100644 gcc/rust/typecheck/rust-tyty-bounds.cc
 create mode 100644 gcc/rust/typecheck/rust-tyty-call.cc
 create mode 100644 gcc/rust/typecheck/rust-tyty-call.h
 create mode 100644 gcc/rust/typecheck/rust-tyty-cmp.h
 create mode 100644 gcc/rust/typecheck/rust-tyty-rules.h
 create mode 100644 gcc/rust/typecheck/rust-tyty-visitor.h
 create mode 100644 gcc/rust/typecheck/rust-tyty.cc
 create mode 100644 gcc/rust/typecheck/rust-tyty.h

diff --git a/gcc/rust/typecheck/rust-autoderef.cc b/gcc/rust/typecheck/rust-autoderef.cc
new file mode 100644
index 00000000000..423f8e4709b
--- /dev/null
+++ b/gcc/rust/typecheck/rust-autoderef.cc
@@ -0,0 +1,398 @@
+// 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/>.
+
+#include "rust-autoderef.h"
+#include "rust-hir-path-probe.h"
+#include "rust-hir-dot-operator.h"
+#include "rust-hir-trait-resolve.h"
+
+namespace Rust {
+namespace Resolver {
+
+static bool
+resolve_operator_overload_fn (
+  Analysis::RustLangItem::ItemType lang_item_type, const TyTy::BaseType *ty,
+  TyTy::FnType **resolved_fn, HIR::ImplItem **impl_item,
+  Adjustment::AdjustmentType *requires_ref_adjustment);
+
+TyTy::BaseType *
+Adjuster::adjust_type (const std::vector<Adjustment> &adjustments)
+{
+  if (adjustments.size () == 0)
+    return base->clone ();
+
+  return adjustments.back ().get_expected ()->clone ();
+}
+
+Adjustment
+Adjuster::try_deref_type (const TyTy::BaseType *ty,
+			  Analysis::RustLangItem::ItemType deref_lang_item)
+{
+  HIR::ImplItem *impl_item = nullptr;
+  TyTy::FnType *fn = nullptr;
+  Adjustment::AdjustmentType requires_ref_adjustment
+    = Adjustment::AdjustmentType::ERROR;
+  bool operator_overloaded
+    = resolve_operator_overload_fn (deref_lang_item, ty, &fn, &impl_item,
+				    &requires_ref_adjustment);
+  if (!operator_overloaded)
+    {
+      return Adjustment::get_error ();
+    }
+
+  auto resolved_base = fn->get_return_type ()->clone ();
+  bool is_valid_type = resolved_base->get_kind () == TyTy::TypeKind::REF;
+  if (!is_valid_type)
+    return Adjustment::get_error ();
+
+  TyTy::ReferenceType *ref_base
+    = static_cast<TyTy::ReferenceType *> (resolved_base);
+
+  Adjustment::AdjustmentType adjustment_type
+    = Adjustment::AdjustmentType::ERROR;
+  switch (deref_lang_item)
+    {
+    case Analysis::RustLangItem::ItemType::DEREF:
+      adjustment_type = Adjustment::AdjustmentType::DEREF;
+      break;
+
+    case Analysis::RustLangItem::ItemType::DEREF_MUT:
+      adjustment_type = Adjustment::AdjustmentType::DEREF_MUT;
+      break;
+
+    default:
+      break;
+    }
+
+  return Adjustment::get_op_overload_deref_adjustment (adjustment_type, ty,
+						       ref_base, fn, impl_item,
+						       requires_ref_adjustment);
+}
+
+Adjustment
+Adjuster::try_raw_deref_type (const TyTy::BaseType *ty)
+{
+  bool is_valid_type = ty->get_kind () == TyTy::TypeKind::REF;
+  if (!is_valid_type)
+    return Adjustment::get_error ();
+
+  const TyTy::ReferenceType *ref_base
+    = static_cast<const TyTy::ReferenceType *> (ty);
+  auto infered = ref_base->get_base ()->clone ();
+
+  return Adjustment (Adjustment::AdjustmentType::INDIRECTION, ty, infered);
+}
+
+Adjustment
+Adjuster::try_unsize_type (const TyTy::BaseType *ty)
+{
+  bool is_valid_type = ty->get_kind () == TyTy::TypeKind::ARRAY;
+  if (!is_valid_type)
+    return Adjustment::get_error ();
+
+  auto mappings = Analysis::Mappings::get ();
+  auto context = TypeCheckContext::get ();
+
+  const auto ref_base = static_cast<const TyTy::ArrayType *> (ty);
+  auto slice_elem = ref_base->get_element_type ();
+
+  auto slice
+    = new TyTy::SliceType (mappings->get_next_hir_id (), ty->get_ident ().locus,
+			   TyTy::TyVar (slice_elem->get_ref ()));
+  context->insert_implicit_type (slice);
+
+  return Adjustment (Adjustment::AdjustmentType::UNSIZE, ty, slice);
+}
+
+static bool
+resolve_operator_overload_fn (
+  Analysis::RustLangItem::ItemType lang_item_type, const TyTy::BaseType *ty,
+  TyTy::FnType **resolved_fn, HIR::ImplItem **impl_item,
+  Adjustment::AdjustmentType *requires_ref_adjustment)
+{
+  auto context = TypeCheckContext::get ();
+  auto mappings = Analysis::Mappings::get ();
+
+  // look up lang item for arithmetic type
+  std::string associated_item_name
+    = Analysis::RustLangItem::ToString (lang_item_type);
+  DefId respective_lang_item_id = UNKNOWN_DEFID;
+  bool lang_item_defined
+    = mappings->lookup_lang_item (lang_item_type, &respective_lang_item_id);
+
+  if (!lang_item_defined)
+    return false;
+
+  auto segment = HIR::PathIdentSegment (associated_item_name);
+  auto candidate
+    = MethodResolver::Probe (ty, HIR::PathIdentSegment (associated_item_name),
+			     true);
+
+  bool have_implementation_for_lang_item = !candidate.is_error ();
+  if (!have_implementation_for_lang_item)
+    return false;
+
+  // Get the adjusted self
+  Adjuster adj (ty);
+  TyTy::BaseType *adjusted_self = adj.adjust_type (candidate.adjustments);
+
+  // is this the case we are recursive
+  // handle the case where we are within the impl block for this
+  // lang_item otherwise we end up with a recursive operator overload
+  // such as the i32 operator overload trait
+  TypeCheckContextItem &fn_context = context->peek_context ();
+  if (fn_context.get_type () == TypeCheckContextItem::ItemType::IMPL_ITEM)
+    {
+      auto &impl_item = fn_context.get_impl_item ();
+      HIR::ImplBlock *parent = impl_item.first;
+      HIR::Function *fn = impl_item.second;
+
+      if (parent->has_trait_ref ()
+	  && fn->get_function_name ().compare (associated_item_name) == 0)
+	{
+	  TraitReference *trait_reference
+	    = TraitResolver::Lookup (*parent->get_trait_ref ().get ());
+	  if (!trait_reference->is_error ())
+	    {
+	      TyTy::BaseType *lookup = nullptr;
+	      bool ok = context->lookup_type (fn->get_mappings ().get_hirid (),
+					      &lookup);
+	      rust_assert (ok);
+	      rust_assert (lookup->get_kind () == TyTy::TypeKind::FNDEF);
+
+	      TyTy::FnType *fntype = static_cast<TyTy::FnType *> (lookup);
+	      rust_assert (fntype->is_method ());
+
+	      bool is_lang_item_impl
+		= trait_reference->get_mappings ().get_defid ()
+		  == respective_lang_item_id;
+	      bool self_is_lang_item_self
+		= fntype->get_self_type ()->is_equal (*adjusted_self);
+	      bool recursive_operator_overload
+		= is_lang_item_impl && self_is_lang_item_self;
+
+	      if (recursive_operator_overload)
+		return false;
+	    }
+	}
+    }
+
+  TyTy::BaseType *lookup_tyty = candidate.candidate.ty;
+
+  // rust only support impl item deref operator overloading ie you must have an
+  // impl block for it
+  rust_assert (candidate.candidate.type
+	       == PathProbeCandidate::CandidateType::IMPL_FUNC);
+  *impl_item = candidate.candidate.item.impl.impl_item;
+
+  rust_assert (lookup_tyty->get_kind () == TyTy::TypeKind::FNDEF);
+  TyTy::BaseType *lookup = lookup_tyty;
+  TyTy::FnType *fn = static_cast<TyTy::FnType *> (lookup);
+  rust_assert (fn->is_method ());
+
+  if (fn->needs_substitution ())
+    {
+      if (ty->get_kind () == TyTy::TypeKind::ADT)
+	{
+	  const TyTy::ADTType *adt = static_cast<const TyTy::ADTType *> (ty);
+
+	  auto s = fn->get_self_type ()->get_root ();
+	  rust_assert (s->can_eq (adt, false));
+	  rust_assert (s->get_kind () == TyTy::TypeKind::ADT);
+	  const TyTy::ADTType *self_adt
+	    = static_cast<const TyTy::ADTType *> (s);
+
+	  // we need to grab the Self substitutions as the inherit type
+	  // parameters for this
+	  if (self_adt->needs_substitution ())
+	    {
+	      rust_assert (adt->was_substituted ());
+
+	      TyTy::SubstitutionArgumentMappings used_args_in_prev_segment
+		= GetUsedSubstArgs::From (adt);
+
+	      TyTy::SubstitutionArgumentMappings inherit_type_args
+		= self_adt->solve_mappings_from_receiver_for_self (
+		  used_args_in_prev_segment);
+
+	      // there may or may not be inherited type arguments
+	      if (!inherit_type_args.is_error ())
+		{
+		  // need to apply the inherited type arguments to the
+		  // function
+		  lookup = fn->handle_substitions (inherit_type_args);
+		}
+	    }
+	}
+      else
+	{
+	  rust_assert (candidate.adjustments.size () < 2);
+
+	  // lets infer the params for this we could probably fix this up by
+	  // actually just performing a substitution of a single param but this
+	  // seems more generic i think.
+	  //
+	  // this is the case where we had say Foo<&Bar>> and we have derefed to
+	  // the &Bar and we are trying to match a method self of Bar which
+	  // requires another deref which is matched to the deref trait impl of
+	  // &&T so this requires another reference and deref call
+
+	  lookup = fn->infer_substitions (Location ());
+	  rust_assert (lookup->get_kind () == TyTy::TypeKind::FNDEF);
+	  fn = static_cast<TyTy::FnType *> (lookup);
+	  fn->get_self_type ()->unify (adjusted_self);
+	  lookup = fn;
+	}
+    }
+
+  if (candidate.adjustments.size () > 0)
+    *requires_ref_adjustment = candidate.adjustments.at (0).get_type ();
+
+  *resolved_fn = static_cast<TyTy::FnType *> (lookup);
+
+  return true;
+}
+
+AutoderefCycle::AutoderefCycle (bool autoderef_flag)
+  : autoderef_flag (autoderef_flag)
+{}
+
+AutoderefCycle::~AutoderefCycle () {}
+
+void
+AutoderefCycle::try_hook (const TyTy::BaseType &)
+{}
+
+bool
+AutoderefCycle::cycle (const TyTy::BaseType *receiver)
+{
+  const TyTy::BaseType *r = receiver;
+  while (true)
+    {
+      if (try_autoderefed (r))
+	return true;
+
+      // 4. deref to to 1, if cannot deref then quit
+      if (autoderef_flag)
+	return false;
+
+      // try unsize
+      Adjustment unsize = Adjuster::try_unsize_type (r);
+      if (!unsize.is_error ())
+	{
+	  adjustments.push_back (unsize);
+	  auto unsize_r = unsize.get_expected ();
+
+	  if (try_autoderefed (unsize_r))
+	    return true;
+
+	  adjustments.pop_back ();
+	}
+
+      Adjustment deref
+	= Adjuster::try_deref_type (r, Analysis::RustLangItem::ItemType::DEREF);
+      if (!deref.is_error ())
+	{
+	  auto deref_r = deref.get_expected ();
+	  adjustments.push_back (deref);
+
+	  if (try_autoderefed (deref_r))
+	    return true;
+
+	  adjustments.pop_back ();
+	}
+
+      Adjustment deref_mut = Adjuster::try_deref_type (
+	r, Analysis::RustLangItem::ItemType::DEREF_MUT);
+      if (!deref_mut.is_error ())
+	{
+	  auto deref_r = deref_mut.get_expected ();
+	  adjustments.push_back (deref_mut);
+
+	  if (try_autoderefed (deref_r))
+	    return true;
+
+	  adjustments.pop_back ();
+	}
+
+      if (!deref_mut.is_error ())
+	{
+	  auto deref_r = deref_mut.get_expected ();
+	  adjustments.push_back (deref_mut);
+	  Adjustment raw_deref = Adjuster::try_raw_deref_type (deref_r);
+	  adjustments.push_back (raw_deref);
+	  deref_r = raw_deref.get_expected ();
+
+	  if (try_autoderefed (deref_r))
+	    return true;
+
+	  adjustments.pop_back ();
+	  adjustments.pop_back ();
+	}
+
+      if (!deref.is_error ())
+	{
+	  r = deref.get_expected ();
+	  adjustments.push_back (deref);
+	}
+      Adjustment raw_deref = Adjuster::try_raw_deref_type (r);
+      if (raw_deref.is_error ())
+	return false;
+
+      r = raw_deref.get_expected ();
+      adjustments.push_back (raw_deref);
+    }
+  return false;
+}
+
+bool
+AutoderefCycle::try_autoderefed (const TyTy::BaseType *r)
+{
+  try_hook (*r);
+
+  // 1. try raw
+  if (select (*r))
+    return true;
+
+  // 2. try ref
+  TyTy::ReferenceType *r1
+    = new TyTy::ReferenceType (r->get_ref (), TyTy::TyVar (r->get_ref ()),
+			       Mutability::Imm);
+  adjustments.push_back (
+    Adjustment (Adjustment::AdjustmentType::IMM_REF, r, r1));
+  if (select (*r1))
+    return true;
+
+  adjustments.pop_back ();
+
+  // 3. try mut ref
+  TyTy::ReferenceType *r2
+    = new TyTy::ReferenceType (r->get_ref (), TyTy::TyVar (r->get_ref ()),
+			       Mutability::Mut);
+  adjustments.push_back (
+    Adjustment (Adjustment::AdjustmentType::MUT_REF, r, r2));
+  if (select (*r2))
+    return true;
+
+  adjustments.pop_back ();
+
+  return false;
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-autoderef.h b/gcc/rust/typecheck/rust-autoderef.h
new file mode 100644
index 00000000000..2f8d64b97e6
--- /dev/null
+++ b/gcc/rust/typecheck/rust-autoderef.h
@@ -0,0 +1,178 @@
+// 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_AUTODEREF
+#define RUST_AUTODEREF
+
+#include "rust-tyty.h"
+
+namespace Rust {
+namespace Resolver {
+
+class Adjustment
+{
+public:
+  enum AdjustmentType
+  {
+    ERROR,
+
+    IMM_REF,
+    MUT_REF,
+    DEREF,
+    DEREF_MUT,
+    INDIRECTION,
+    UNSIZE,
+  };
+
+  // ctor for all adjustments except derefs
+  Adjustment (AdjustmentType type, const TyTy::BaseType *actual,
+	      const TyTy::BaseType *expected)
+    : Adjustment (type, actual, expected, nullptr, nullptr,
+		  AdjustmentType::ERROR)
+  {}
+
+  static Adjustment get_op_overload_deref_adjustment (
+    AdjustmentType type, const TyTy::BaseType *actual,
+    const TyTy::BaseType *expected, TyTy::FnType *fn, HIR::ImplItem *deref_item,
+    Adjustment::AdjustmentType requires_ref_adjustment)
+  {
+    rust_assert (type == DEREF || type == DEREF_MUT);
+    return Adjustment (type, actual, expected, fn, deref_item,
+		       requires_ref_adjustment);
+  }
+
+  AdjustmentType get_type () const { return type; }
+
+  const TyTy::BaseType *get_actual () const { return actual; }
+  const TyTy::BaseType *get_expected () const { return expected; }
+
+  std::string as_string () const
+  {
+    return Adjustment::type_string (get_type ()) + "->"
+	   + get_expected ()->debug_str ();
+  }
+
+  static std::string type_string (AdjustmentType type)
+  {
+    switch (type)
+      {
+      case AdjustmentType::ERROR:
+	return "ERROR";
+      case AdjustmentType::IMM_REF:
+	return "IMM_REF";
+      case AdjustmentType::MUT_REF:
+	return "MUT_REF";
+      case AdjustmentType::DEREF:
+	return "DEREF";
+      case AdjustmentType::DEREF_MUT:
+	return "DEREF_MUT";
+      case AdjustmentType::INDIRECTION:
+	return "INDIRECTION";
+      case AdjustmentType::UNSIZE:
+	return "UNSIZE";
+      }
+    gcc_unreachable ();
+    return "";
+  }
+
+  static Adjustment get_error () { return Adjustment{ERROR, nullptr, nullptr}; }
+
+  bool is_error () const { return type == ERROR; }
+
+  bool is_deref_adjustment () const { return type == DEREF; }
+
+  bool is_deref_mut_adjustment () const { return type == DEREF_MUT; }
+
+  bool has_operator_overload () const { return deref_operator_fn != nullptr; }
+
+  TyTy::FnType *get_deref_operator_fn () const { return deref_operator_fn; }
+
+  AdjustmentType get_deref_adjustment_type () const
+  {
+    return requires_ref_adjustment;
+  }
+
+  HIR::ImplItem *get_deref_hir_item () const { return deref_item; }
+
+private:
+  Adjustment (AdjustmentType type, const TyTy::BaseType *actual,
+	      const TyTy::BaseType *expected, TyTy::FnType *deref_operator_fn,
+	      HIR::ImplItem *deref_item,
+	      Adjustment::AdjustmentType requires_ref_adjustment)
+    : type (type), actual (actual), expected (expected),
+      deref_operator_fn (deref_operator_fn), deref_item (deref_item),
+      requires_ref_adjustment (requires_ref_adjustment)
+  {}
+
+  AdjustmentType type;
+  const TyTy::BaseType *actual;
+  const TyTy::BaseType *expected;
+
+  // - only used for deref operator_overloads
+  //
+  // the fn that we are calling
+  TyTy::FnType *deref_operator_fn;
+  HIR::ImplItem *deref_item;
+  // operator overloads can requre a reference
+  Adjustment::AdjustmentType requires_ref_adjustment;
+};
+
+class Adjuster
+{
+public:
+  Adjuster (const TyTy::BaseType *ty) : base (ty) {}
+
+  TyTy::BaseType *adjust_type (const std::vector<Adjustment> &adjustments);
+
+  static Adjustment
+  try_deref_type (const TyTy::BaseType *ty,
+		  Analysis::RustLangItem::ItemType deref_lang_item);
+
+  static Adjustment try_raw_deref_type (const TyTy::BaseType *ty);
+
+  static Adjustment try_unsize_type (const TyTy::BaseType *ty);
+
+private:
+  const TyTy::BaseType *base;
+};
+
+class AutoderefCycle
+{
+protected:
+  AutoderefCycle (bool autoderef_flag);
+
+  virtual ~AutoderefCycle ();
+
+  virtual bool select (const TyTy::BaseType &autoderefed) = 0;
+
+  // optional: this is a chance to hook in to grab predicate items on the raw
+  // type
+  virtual void try_hook (const TyTy::BaseType &);
+
+  virtual bool cycle (const TyTy::BaseType *receiver);
+
+  bool try_autoderefed (const TyTy::BaseType *r);
+
+  bool autoderef_flag;
+  std::vector<Adjustment> adjustments;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_AUTODEREF
diff --git a/gcc/rust/typecheck/rust-casts.cc b/gcc/rust/typecheck/rust-casts.cc
new file mode 100644
index 00000000000..61004dfabc3
--- /dev/null
+++ b/gcc/rust/typecheck/rust-casts.cc
@@ -0,0 +1,292 @@
+// 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/>.
+
+#include "rust-casts.h"
+
+namespace Rust {
+namespace Resolver {
+
+TypeCastRules::TypeCastRules (Location locus, TyTy::TyWithLocation from,
+			      TyTy::TyWithLocation to)
+  : locus (locus), from (from), to (to)
+{}
+
+TypeCoercionRules::CoercionResult
+TypeCastRules::resolve (Location locus, TyTy::TyWithLocation from,
+			TyTy::TyWithLocation to)
+{
+  TypeCastRules cast_rules (locus, from, to);
+  return cast_rules.check ();
+}
+
+TypeCoercionRules::CoercionResult
+TypeCastRules::check ()
+{
+  // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/cast.rs#L565-L582
+  auto possible_coercion
+    = TypeCoercionRules::TryCoerce (from.get_ty (), to.get_ty (), locus);
+  if (!possible_coercion.is_error ())
+    return possible_coercion;
+
+  // try the simple cast rules
+  auto simple_cast = cast_rules ();
+  if (!simple_cast.is_error ())
+    return simple_cast;
+
+  // failed to cast
+  emit_cast_error ();
+  return TypeCoercionRules::CoercionResult::get_error ();
+}
+
+TypeCoercionRules::CoercionResult
+TypeCastRules::cast_rules ()
+{
+  // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/cast.rs#L596
+  // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/cast.rs#L654
+
+  rust_debug ("cast_rules from={%s} to={%s}",
+	      from.get_ty ()->debug_str ().c_str (),
+	      to.get_ty ()->debug_str ().c_str ());
+
+  switch (from.get_ty ()->get_kind ())
+    {
+      case TyTy::TypeKind::INFER: {
+	TyTy::InferType *from_infer
+	  = static_cast<TyTy::InferType *> (from.get_ty ());
+	switch (from_infer->get_infer_kind ())
+	  {
+	  case TyTy::InferType::InferTypeKind::GENERAL:
+	    return TypeCoercionRules::CoercionResult{{},
+						     to.get_ty ()->clone ()};
+
+	  case TyTy::InferType::InferTypeKind::INTEGRAL:
+	    switch (to.get_ty ()->get_kind ())
+	      {
+	      case TyTy::TypeKind::CHAR:
+	      case TyTy::TypeKind::BOOL:
+	      case TyTy::TypeKind::USIZE:
+	      case TyTy::TypeKind::ISIZE:
+	      case TyTy::TypeKind::UINT:
+	      case TyTy::TypeKind::INT:
+	      case TyTy::TypeKind::POINTER:
+		return TypeCoercionRules::CoercionResult{
+		  {}, to.get_ty ()->clone ()};
+
+		case TyTy::TypeKind::INFER: {
+		  TyTy::InferType *to_infer
+		    = static_cast<TyTy::InferType *> (to.get_ty ());
+
+		  switch (to_infer->get_infer_kind ())
+		    {
+		    case TyTy::InferType::InferTypeKind::GENERAL:
+		    case TyTy::InferType::InferTypeKind::INTEGRAL:
+		      return TypeCoercionRules::CoercionResult{
+			{}, to.get_ty ()->clone ()};
+
+		    default:
+		      return TypeCoercionRules::CoercionResult::get_error ();
+		    }
+		}
+		break;
+
+	      default:
+		return TypeCoercionRules::CoercionResult::get_error ();
+	      }
+	    break;
+
+	  case TyTy::InferType::InferTypeKind::FLOAT:
+	    switch (to.get_ty ()->get_kind ())
+	      {
+	      case TyTy::TypeKind::USIZE:
+	      case TyTy::TypeKind::ISIZE:
+	      case TyTy::TypeKind::UINT:
+	      case TyTy::TypeKind::INT:
+		return TypeCoercionRules::CoercionResult{
+		  {}, to.get_ty ()->clone ()};
+
+		case TyTy::TypeKind::INFER: {
+		  TyTy::InferType *to_infer
+		    = static_cast<TyTy::InferType *> (to.get_ty ());
+
+		  switch (to_infer->get_infer_kind ())
+		    {
+		    case TyTy::InferType::InferTypeKind::GENERAL:
+		    case TyTy::InferType::InferTypeKind::FLOAT:
+		      return TypeCoercionRules::CoercionResult{
+			{}, to.get_ty ()->clone ()};
+
+		    default:
+		      return TypeCoercionRules::CoercionResult::get_error ();
+		    }
+		}
+		break;
+
+	      default:
+		return TypeCoercionRules::CoercionResult::get_error ();
+	      }
+	    break;
+	  }
+      }
+      break;
+
+    case TyTy::TypeKind::BOOL:
+      switch (to.get_ty ()->get_kind ())
+	{
+	case TyTy::TypeKind::INFER:
+	case TyTy::TypeKind::USIZE:
+	case TyTy::TypeKind::ISIZE:
+	case TyTy::TypeKind::UINT:
+	case TyTy::TypeKind::INT:
+	  return TypeCoercionRules::CoercionResult{{}, to.get_ty ()->clone ()};
+
+	default:
+	  return TypeCoercionRules::CoercionResult::get_error ();
+	}
+      break;
+
+    case TyTy::TypeKind::CHAR:
+    case TyTy::TypeKind::USIZE:
+    case TyTy::TypeKind::ISIZE:
+    case TyTy::TypeKind::UINT:
+    case TyTy::TypeKind::INT:
+      switch (to.get_ty ()->get_kind ())
+	{
+	  case TyTy::TypeKind::CHAR: {
+	    // only u8 and char
+	    bool was_uint = from.get_ty ()->get_kind () == TyTy::TypeKind::UINT;
+	    bool was_u8 = was_uint
+			  && (static_cast<TyTy::UintType *> (from.get_ty ())
+				->get_uint_kind ()
+			      == TyTy::UintType::UintKind::U8);
+	    if (was_u8)
+	      return TypeCoercionRules::CoercionResult{{},
+						       to.get_ty ()->clone ()};
+	  }
+	  break;
+
+	case TyTy::TypeKind::INFER:
+	case TyTy::TypeKind::USIZE:
+	case TyTy::TypeKind::ISIZE:
+	case TyTy::TypeKind::UINT:
+	case TyTy::TypeKind::INT:
+	  return TypeCoercionRules::CoercionResult{{}, to.get_ty ()->clone ()};
+
+	default:
+	  return TypeCoercionRules::CoercionResult::get_error ();
+	}
+      break;
+
+    case TyTy::TypeKind::FLOAT:
+      switch (to.get_ty ()->get_kind ())
+	{
+	case TyTy::TypeKind::FLOAT:
+	  return TypeCoercionRules::CoercionResult{{}, to.get_ty ()->clone ()};
+
+	  case TyTy::TypeKind::INFER: {
+	    TyTy::InferType *to_infer
+	      = static_cast<TyTy::InferType *> (to.get_ty ());
+
+	    switch (to_infer->get_infer_kind ())
+	      {
+	      case TyTy::InferType::InferTypeKind::GENERAL:
+	      case TyTy::InferType::InferTypeKind::FLOAT:
+		return TypeCoercionRules::CoercionResult{
+		  {}, to.get_ty ()->clone ()};
+
+	      default:
+		return TypeCoercionRules::CoercionResult::get_error ();
+	      }
+	  }
+	  break;
+
+	default:
+	  return TypeCoercionRules::CoercionResult::get_error ();
+	}
+      break;
+
+    case TyTy::TypeKind::REF:
+    case TyTy::TypeKind::POINTER:
+      switch (to.get_ty ()->get_kind ())
+	{
+	case TyTy::TypeKind::REF:
+	case TyTy::TypeKind::POINTER:
+	  return check_ptr_ptr_cast ();
+
+	  // FIXME can you cast a pointer to a integral type?
+
+	default:
+	  return TypeCoercionRules::CoercionResult::get_error ();
+	}
+      break;
+
+    default:
+      return TypeCoercionRules::CoercionResult::get_error ();
+    }
+
+  return TypeCoercionRules::CoercionResult::get_error ();
+}
+
+TypeCoercionRules::CoercionResult
+TypeCastRules::check_ptr_ptr_cast ()
+{
+  rust_debug ("check_ptr_ptr_cast from={%s} to={%s}",
+	      from.get_ty ()->debug_str ().c_str (),
+	      to.get_ty ()->debug_str ().c_str ());
+
+  bool from_is_ref = from.get_ty ()->get_kind () == TyTy::TypeKind::REF;
+  bool to_is_ref = to.get_ty ()->get_kind () == TyTy::TypeKind::REF;
+  bool from_is_ptr = from.get_ty ()->get_kind () == TyTy::TypeKind::POINTER;
+  bool to_is_ptr = to.get_ty ()->get_kind () == TyTy::TypeKind::POINTER;
+
+  if (from_is_ptr && to_is_ptr)
+    {
+      // mutability is ignored here as all pointer usage requires unsafe
+      return TypeCoercionRules::CoercionResult{{}, to.get_ty ()->clone ()};
+    }
+  else if (from_is_ref && to_is_ref)
+    {
+      // mutability must be coercedable
+      TyTy::ReferenceType &f
+	= static_cast<TyTy::ReferenceType &> (*from.get_ty ());
+      TyTy::ReferenceType &t
+	= static_cast<TyTy::ReferenceType &> (*to.get_ty ());
+
+      if (TypeCoercionRules::coerceable_mutability (f.mutability (),
+						    t.mutability ()))
+	{
+	  return TypeCoercionRules::CoercionResult{{}, to.get_ty ()->clone ()};
+	}
+    }
+
+  return TypeCoercionRules::CoercionResult::get_error ();
+}
+
+void
+TypeCastRules::emit_cast_error () const
+{
+  // error[E0604]
+  RichLocation r (locus);
+  r.add_range (from.get_locus ());
+  r.add_range (to.get_locus ());
+  rust_error_at (r, "invalid cast %<%s%> to %<%s%>",
+		 from.get_ty ()->get_name ().c_str (),
+		 to.get_ty ()->get_name ().c_str ());
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-casts.h b/gcc/rust/typecheck/rust-casts.h
new file mode 100644
index 00000000000..e908f49b656
--- /dev/null
+++ b/gcc/rust/typecheck/rust-casts.h
@@ -0,0 +1,53 @@
+// 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_CASTS
+#define RUST_CASTS
+
+#include "rust-tyty.h"
+#include "rust-coercion.h"
+
+namespace Rust {
+namespace Resolver {
+
+class TypeCastRules
+{
+public:
+  static TypeCoercionRules::CoercionResult
+  resolve (Location locus, TyTy::TyWithLocation from, TyTy::TyWithLocation to);
+
+protected:
+  TypeCoercionRules::CoercionResult check ();
+  TypeCoercionRules::CoercionResult cast_rules ();
+  TypeCoercionRules::CoercionResult check_ptr_ptr_cast ();
+
+  void emit_cast_error () const;
+
+protected:
+  TypeCastRules (Location locus, TyTy::TyWithLocation from,
+		 TyTy::TyWithLocation to);
+
+  Location locus;
+  TyTy::TyWithLocation from;
+  TyTy::TyWithLocation to;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_CASTS
diff --git a/gcc/rust/typecheck/rust-coercion.cc b/gcc/rust/typecheck/rust-coercion.cc
new file mode 100644
index 00000000000..2ad2b8007ff
--- /dev/null
+++ b/gcc/rust/typecheck/rust-coercion.cc
@@ -0,0 +1,357 @@
+// 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/>.
+
+#include "rust-coercion.h"
+
+namespace Rust {
+namespace Resolver {
+
+TypeCoercionRules::CoercionResult
+TypeCoercionRules::Coerce (TyTy::BaseType *receiver, TyTy::BaseType *expected,
+			   Location locus)
+{
+  TypeCoercionRules resolver (expected, locus, true);
+  bool ok = resolver.do_coercion (receiver);
+  return ok ? resolver.try_result : CoercionResult::get_error ();
+}
+
+TypeCoercionRules::CoercionResult
+TypeCoercionRules::TryCoerce (TyTy::BaseType *receiver,
+			      TyTy::BaseType *expected, Location locus)
+{
+  TypeCoercionRules resolver (expected, locus, false);
+  bool ok = resolver.do_coercion (receiver);
+  return ok ? resolver.try_result : CoercionResult::get_error ();
+}
+
+TypeCoercionRules::TypeCoercionRules (TyTy::BaseType *expected, Location locus,
+				      bool emit_errors)
+  : AutoderefCycle (false), mappings (Analysis::Mappings::get ()),
+    context (TypeCheckContext::get ()), expected (expected), locus (locus),
+    try_result (CoercionResult::get_error ()), emit_errors (emit_errors)
+{}
+
+bool
+TypeCoercionRules::do_coercion (TyTy::BaseType *receiver)
+{
+  // FIXME this is not finished and might be super simplified
+  // see:
+  // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/coercion.rs
+
+  // unsize
+  bool unsafe_error = false;
+  CoercionResult unsize_coercion
+    = coerce_unsized (receiver, expected, unsafe_error);
+  bool valid_unsize_coercion = !unsize_coercion.is_error ();
+  if (valid_unsize_coercion)
+    {
+      try_result = unsize_coercion;
+      return true;
+    }
+  else if (unsafe_error)
+    {
+      // Location lhs = mappings->lookup_location (receiver->get_ref ());
+      // Location rhs = mappings->lookup_location (expected->get_ref ());
+      // object_unsafe_error (locus, lhs, rhs);
+      return false;
+    }
+
+  // pointers
+  switch (expected->get_kind ())
+    {
+      case TyTy::TypeKind::POINTER: {
+	TyTy::PointerType *ptr = static_cast<TyTy::PointerType *> (expected);
+	try_result = coerce_unsafe_ptr (receiver, ptr, ptr->mutability ());
+	return !try_result.is_error ();
+      }
+
+      case TyTy::TypeKind::REF: {
+	TyTy::ReferenceType *ptr
+	  = static_cast<TyTy::ReferenceType *> (expected);
+	try_result
+	  = coerce_borrowed_pointer (receiver, ptr, ptr->mutability ());
+	return !try_result.is_error ();
+      }
+      break;
+
+    default:
+      break;
+    }
+
+  return !try_result.is_error ();
+}
+
+TypeCoercionRules::CoercionResult
+TypeCoercionRules::coerce_unsafe_ptr (TyTy::BaseType *receiver,
+				      TyTy::PointerType *expected,
+				      Mutability to_mutbl)
+{
+  rust_debug ("coerce_unsafe_ptr(a={%s}, b={%s})",
+	      receiver->debug_str ().c_str (), expected->debug_str ().c_str ());
+
+  Mutability from_mutbl = Mutability::Imm;
+  TyTy::BaseType *element = nullptr;
+  switch (receiver->get_kind ())
+    {
+      case TyTy::TypeKind::REF: {
+	TyTy::ReferenceType *ref
+	  = static_cast<TyTy::ReferenceType *> (receiver);
+	from_mutbl = ref->mutability ();
+	element = ref->get_base ();
+      }
+      break;
+
+      case TyTy::TypeKind::POINTER: {
+	TyTy::PointerType *ref = static_cast<TyTy::PointerType *> (receiver);
+	from_mutbl = ref->mutability ();
+	element = ref->get_base ();
+      }
+      break;
+
+      default: {
+	if (receiver->can_eq (expected, false))
+	  return CoercionResult{{}, expected->clone ()};
+
+	return CoercionResult::get_error ();
+      }
+    }
+
+  if (!coerceable_mutability (from_mutbl, to_mutbl))
+    {
+      Location lhs = mappings->lookup_location (receiver->get_ref ());
+      Location rhs = mappings->lookup_location (expected->get_ref ());
+      mismatched_mutability_error (locus, lhs, rhs);
+      return TypeCoercionRules::CoercionResult::get_error ();
+    }
+
+  TyTy::PointerType *result
+    = new TyTy::PointerType (receiver->get_ref (),
+			     TyTy::TyVar (element->get_ref ()), to_mutbl);
+  if (!result->can_eq (expected, false))
+    return CoercionResult::get_error ();
+
+  return CoercionResult{{}, result};
+}
+
+/// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`.
+/// To match `A` with `B`, autoderef will be performed,
+/// calling `deref`/`deref_mut` where necessary.
+TypeCoercionRules::CoercionResult
+TypeCoercionRules::coerce_borrowed_pointer (TyTy::BaseType *receiver,
+					    TyTy::ReferenceType *expected,
+					    Mutability to_mutbl)
+{
+  rust_debug ("coerce_borrowed_pointer(a={%s}, b={%s})",
+	      receiver->debug_str ().c_str (), expected->debug_str ().c_str ());
+
+  Mutability from_mutbl = Mutability::Imm;
+  switch (receiver->get_kind ())
+    {
+      case TyTy::TypeKind::REF: {
+	TyTy::ReferenceType *ref
+	  = static_cast<TyTy::ReferenceType *> (receiver);
+	from_mutbl = ref->mutability ();
+      }
+      break;
+
+      default: {
+	TyTy::BaseType *result = receiver->unify (expected);
+	return CoercionResult{{}, result};
+      }
+    }
+
+  if (!coerceable_mutability (from_mutbl, to_mutbl))
+    {
+      Location lhs = mappings->lookup_location (receiver->get_ref ());
+      Location rhs = mappings->lookup_location (expected->get_ref ());
+      mismatched_mutability_error (locus, lhs, rhs);
+      return TypeCoercionRules::CoercionResult::get_error ();
+    }
+
+  AutoderefCycle::cycle (receiver);
+  return try_result;
+}
+
+// &[T; n] or &mut [T; n] -> &[T]
+// or &mut [T; n] -> &mut [T]
+// or &Concrete -> &Trait, etc.
+TypeCoercionRules::CoercionResult
+TypeCoercionRules::coerce_unsized (TyTy::BaseType *source,
+				   TyTy::BaseType *target, bool &unsafe_error)
+{
+  rust_debug ("coerce_unsized(source={%s}, target={%s})",
+	      source->debug_str ().c_str (), target->debug_str ().c_str ());
+
+  bool source_is_ref = source->get_kind () == TyTy::TypeKind::REF;
+  bool target_is_ref = target->get_kind () == TyTy::TypeKind::REF;
+  bool target_is_ptr = target->get_kind () == TyTy::TypeKind::POINTER;
+
+  bool needs_reborrow = false;
+  TyTy::BaseType *ty_a = source;
+  TyTy::BaseType *ty_b = target;
+  Mutability expected_mutability = Mutability::Imm;
+  if (source_is_ref && target_is_ref)
+    {
+      TyTy::ReferenceType *source_ref
+	= static_cast<TyTy::ReferenceType *> (source);
+      TyTy::ReferenceType *target_ref
+	= static_cast<TyTy::ReferenceType *> (target);
+
+      Mutability from_mutbl = source_ref->mutability ();
+      Mutability to_mutbl = target_ref->mutability ();
+      if (!coerceable_mutability (from_mutbl, to_mutbl))
+	{
+	  unsafe_error = true;
+	  Location lhs = mappings->lookup_location (source->get_ref ());
+	  Location rhs = mappings->lookup_location (target->get_ref ());
+	  mismatched_mutability_error (locus, lhs, rhs);
+	  return TypeCoercionRules::CoercionResult::get_error ();
+	}
+
+      ty_a = source_ref->get_base ();
+      ty_b = target_ref->get_base ();
+      needs_reborrow = true;
+      expected_mutability = to_mutbl;
+
+      adjustments.push_back (
+	Adjustment (Adjustment::AdjustmentType::INDIRECTION, source_ref, ty_a));
+    }
+  else if (source_is_ref && target_is_ptr)
+    {
+      TyTy::ReferenceType *source_ref
+	= static_cast<TyTy::ReferenceType *> (source);
+      TyTy::PointerType *target_ref = static_cast<TyTy::PointerType *> (target);
+
+      Mutability from_mutbl = source_ref->mutability ();
+      Mutability to_mutbl = target_ref->mutability ();
+      if (!coerceable_mutability (from_mutbl, to_mutbl))
+	{
+	  unsafe_error = true;
+	  Location lhs = mappings->lookup_location (source->get_ref ());
+	  Location rhs = mappings->lookup_location (target->get_ref ());
+	  mismatched_mutability_error (locus, lhs, rhs);
+	  return TypeCoercionRules::CoercionResult::get_error ();
+	}
+
+      ty_a = source_ref->get_base ();
+      ty_b = target_ref->get_base ();
+      needs_reborrow = true;
+      expected_mutability = to_mutbl;
+
+      adjustments.push_back (
+	Adjustment (Adjustment::AdjustmentType::INDIRECTION, source_ref, ty_a));
+    }
+
+  // FIXME
+  // there is a bunch of code to ensure something is coerce able to a dyn trait
+  // we need to support but we need to support a few more lang items for that
+  // see:
+  // https://github.com/rust-lang/rust/blob/7eac88abb2e57e752f3302f02be5f3ce3d7adfb4/compiler/rustc_typeck/src/check/coercion.rs#L582
+
+  const auto a = ty_a;
+  const auto b = ty_b;
+
+  bool expect_dyn = b->get_kind () == TyTy::TypeKind::DYNAMIC;
+  bool need_unsize = a->get_kind () != TyTy::TypeKind::DYNAMIC;
+
+  if (expect_dyn && need_unsize)
+    {
+      bool bounds_compatible = b->bounds_compatible (*a, locus, true);
+      if (!bounds_compatible)
+	{
+	  unsafe_error = true;
+	  return TypeCoercionRules::CoercionResult::get_error ();
+	}
+
+      // return the unsize coercion
+      TyTy::BaseType *result = b->clone ();
+      // result->set_ref (a->get_ref ());
+
+      // append a dyn coercion adjustment
+      adjustments.push_back (Adjustment (Adjustment::UNSIZE, a, result));
+
+      // reborrow if needed
+      if (needs_reborrow)
+	{
+	  TyTy::ReferenceType *reborrow
+	    = new TyTy::ReferenceType (source->get_ref (),
+				       TyTy::TyVar (result->get_ref ()),
+				       expected_mutability);
+
+	  Adjustment::AdjustmentType borrow_type
+	    = expected_mutability == Mutability::Imm ? Adjustment::IMM_REF
+						     : Adjustment::MUT_REF;
+	  adjustments.push_back (Adjustment (borrow_type, result, reborrow));
+	  result = reborrow;
+	}
+
+      return CoercionResult{adjustments, result};
+    }
+
+  adjustments.clear ();
+  return TypeCoercionRules::CoercionResult::get_error ();
+}
+
+bool
+TypeCoercionRules::select (const TyTy::BaseType &autoderefed)
+{
+  if (autoderefed.can_eq (expected, false))
+    {
+      try_result = CoercionResult{adjustments, autoderefed.clone ()};
+      return true;
+    }
+  return false;
+}
+
+/// Coercing a mutable reference to an immutable works, while
+/// coercing `&T` to `&mut T` should be forbidden.
+bool
+TypeCoercionRules::coerceable_mutability (Mutability from_mutbl,
+					  Mutability to_mutbl)
+{
+  return to_mutbl == Mutability::Imm || (from_mutbl == to_mutbl);
+}
+
+void
+TypeCoercionRules::mismatched_mutability_error (Location expr_locus,
+						Location lhs, Location rhs)
+{
+  if (!emit_errors)
+    return;
+
+  RichLocation r (expr_locus);
+  r.add_range (lhs);
+  r.add_range (rhs);
+  rust_error_at (r, "mismatched mutability");
+}
+
+void
+TypeCoercionRules::object_unsafe_error (Location expr_locus, Location lhs,
+					Location rhs)
+{
+  if (!emit_errors)
+    return;
+
+  RichLocation r (expr_locus);
+  r.add_range (lhs);
+  r.add_range (rhs);
+  rust_error_at (r, "unsafe unsize coercion");
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-coercion.h b/gcc/rust/typecheck/rust-coercion.h
new file mode 100644
index 00000000000..da28c7c5e1b
--- /dev/null
+++ b/gcc/rust/typecheck/rust-coercion.h
@@ -0,0 +1,93 @@
+// 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_COERCION
+#define RUST_COERCION
+
+#include "rust-autoderef.h"
+#include "rust-hir-type-check.h"
+
+namespace Rust {
+namespace Resolver {
+
+class TypeCoercionRules : protected AutoderefCycle
+{
+public:
+  struct CoercionResult
+  {
+    std::vector<Adjustment> adjustments;
+    TyTy::BaseType *tyty;
+
+    bool is_error ()
+    {
+      return tyty == nullptr || tyty->get_kind () == TyTy::TypeKind::ERROR;
+    }
+
+    static CoercionResult get_error () { return CoercionResult{{}, nullptr}; }
+  };
+
+  static CoercionResult Coerce (TyTy::BaseType *receiver,
+				TyTy::BaseType *expected, Location locus);
+
+  static CoercionResult TryCoerce (TyTy::BaseType *receiver,
+				   TyTy::BaseType *expected, Location locus);
+
+  CoercionResult coerce_unsafe_ptr (TyTy::BaseType *receiver,
+				    TyTy::PointerType *expected,
+				    Mutability mutability);
+
+  CoercionResult coerce_borrowed_pointer (TyTy::BaseType *receiver,
+					  TyTy::ReferenceType *expected,
+					  Mutability mutability);
+
+  CoercionResult coerce_unsized (TyTy::BaseType *receiver,
+				 TyTy::BaseType *expected, bool &unsafe_error);
+
+  static bool coerceable_mutability (Mutability from_mutbl,
+				     Mutability to_mutbl);
+
+  void mismatched_mutability_error (Location expr_locus, Location lhs,
+				    Location rhs);
+  void object_unsafe_error (Location expr_locus, Location lhs, Location rhs);
+
+protected:
+  TypeCoercionRules (TyTy::BaseType *expected, Location locus,
+		     bool emit_errors);
+
+  bool select (const TyTy::BaseType &autoderefed) override;
+
+  bool do_coercion (TyTy::BaseType *receiver);
+
+private:
+  // context info
+  Analysis::Mappings *mappings;
+  TypeCheckContext *context;
+
+  // search
+  TyTy::BaseType *expected;
+  Location locus;
+
+  // mutable fields
+  CoercionResult try_result;
+  bool emit_errors;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_COERCION
diff --git a/gcc/rust/typecheck/rust-hir-dot-operator.cc b/gcc/rust/typecheck/rust-hir-dot-operator.cc
new file mode 100644
index 00000000000..d45f0903478
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-dot-operator.cc
@@ -0,0 +1,263 @@
+// 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/>.
+
+#include "rust-hir-dot-operator.h"
+#include "rust-hir-path-probe.h"
+#include "rust-hir-trait-resolve.h"
+
+namespace Rust {
+namespace Resolver {
+
+MethodResolver::MethodResolver (bool autoderef_flag,
+				const HIR::PathIdentSegment &segment_name)
+  : AutoderefCycle (autoderef_flag), mappings (Analysis::Mappings::get ()),
+    context (TypeCheckContext::get ()), segment_name (segment_name),
+    try_result (MethodCandidate::get_error ())
+{}
+
+MethodCandidate
+MethodResolver::Probe (const TyTy::BaseType *receiver,
+		       const HIR::PathIdentSegment &segment_name,
+		       bool autoderef_flag)
+{
+  MethodResolver resolver (autoderef_flag, segment_name);
+  bool ok = resolver.cycle (receiver);
+  return ok ? resolver.try_result : MethodCandidate::get_error ();
+}
+
+void
+MethodResolver::try_hook (const TyTy::BaseType &r)
+{
+  const auto &specified_bounds = r.get_specified_bounds ();
+  predicate_items = get_predicate_items (segment_name, r, specified_bounds);
+}
+
+bool
+MethodResolver::select (const TyTy::BaseType &receiver)
+{
+  struct impl_item_candidate
+  {
+    HIR::Function *item;
+    HIR::ImplBlock *impl_block;
+    TyTy::FnType *ty;
+  };
+
+  // assemble inherent impl items
+  std::vector<impl_item_candidate> inherent_impl_fns;
+  mappings->iterate_impl_items (
+    [&] (HirId id, HIR::ImplItem *item, HIR::ImplBlock *impl) mutable -> bool {
+      bool is_trait_impl = impl->has_trait_ref ();
+      if (is_trait_impl)
+	return true;
+
+      bool is_fn
+	= item->get_impl_item_type () == HIR::ImplItem::ImplItemType::FUNCTION;
+      if (!is_fn)
+	return true;
+
+      HIR::Function *func = static_cast<HIR::Function *> (item);
+      if (!func->is_method ())
+	return true;
+
+      bool name_matches
+	= func->get_function_name ().compare (segment_name.as_string ()) == 0;
+      if (!name_matches)
+	return true;
+
+      TyTy::BaseType *ty = nullptr;
+      if (!context->lookup_type (func->get_mappings ().get_hirid (), &ty))
+	return true;
+      if (ty->get_kind () == TyTy::TypeKind::ERROR)
+	return true;
+
+      rust_assert (ty->get_kind () == TyTy::TypeKind::FNDEF);
+      TyTy::FnType *fnty = static_cast<TyTy::FnType *> (ty);
+
+      inherent_impl_fns.push_back ({func, impl, fnty});
+
+      return true;
+    });
+
+  struct trait_item_candidate
+  {
+    const HIR::TraitItemFunc *item;
+    const HIR::Trait *trait;
+    TyTy::FnType *ty;
+    const TraitReference *reference;
+    const TraitItemReference *item_ref;
+  };
+
+  std::vector<trait_item_candidate> trait_fns;
+  mappings->iterate_impl_blocks ([&] (HirId id,
+				      HIR::ImplBlock *impl) mutable -> bool {
+    bool is_trait_impl = impl->has_trait_ref ();
+    if (!is_trait_impl)
+      return true;
+
+    // look for impl implementation else lookup the associated trait item
+    for (auto &impl_item : impl->get_impl_items ())
+      {
+	bool is_fn = impl_item->get_impl_item_type ()
+		     == HIR::ImplItem::ImplItemType::FUNCTION;
+	if (!is_fn)
+	  continue;
+
+	HIR::Function *func = static_cast<HIR::Function *> (impl_item.get ());
+	if (!func->is_method ())
+	  continue;
+
+	bool name_matches
+	  = func->get_function_name ().compare (segment_name.as_string ()) == 0;
+	if (!name_matches)
+	  continue;
+
+	TyTy::BaseType *ty = nullptr;
+	if (!context->lookup_type (func->get_mappings ().get_hirid (), &ty))
+	  continue;
+	if (ty->get_kind () == TyTy::TypeKind::ERROR)
+	  continue;
+
+	rust_assert (ty->get_kind () == TyTy::TypeKind::FNDEF);
+	TyTy::FnType *fnty = static_cast<TyTy::FnType *> (ty);
+
+	inherent_impl_fns.push_back ({func, impl, fnty});
+	return true;
+      }
+
+    TraitReference *trait_ref
+      = TraitResolver::Resolve (*impl->get_trait_ref ().get ());
+    rust_assert (!trait_ref->is_error ());
+
+    auto item_ref
+      = trait_ref->lookup_trait_item (segment_name.as_string (),
+				      TraitItemReference::TraitItemType::FN);
+    if (item_ref->is_error ())
+      return true;
+
+    const HIR::Trait *trait = trait_ref->get_hir_trait_ref ();
+    HIR::TraitItem *item = item_ref->get_hir_trait_item ();
+    rust_assert (item->get_item_kind () == HIR::TraitItem::TraitItemKind::FUNC);
+    HIR::TraitItemFunc *func = static_cast<HIR::TraitItemFunc *> (item);
+
+    TyTy::BaseType *ty = item_ref->get_tyty ();
+    rust_assert (ty->get_kind () == TyTy::TypeKind::FNDEF);
+    TyTy::FnType *fnty = static_cast<TyTy::FnType *> (ty);
+
+    trait_item_candidate candidate{func, trait, fnty, trait_ref, item_ref};
+    trait_fns.push_back (candidate);
+
+    return true;
+  });
+
+  // lookup specified bounds for an associated item
+  struct precdicate_candidate
+  {
+    TyTy::TypeBoundPredicateItem lookup;
+    TyTy::FnType *fntype;
+  };
+
+  for (auto impl_item : inherent_impl_fns)
+    {
+      TyTy::FnType *fn = impl_item.ty;
+      rust_assert (fn->is_method ());
+
+      TyTy::BaseType *fn_self = fn->get_self_type ();
+      if (fn_self->can_eq (&receiver, false))
+	{
+	  PathProbeCandidate::ImplItemCandidate c{impl_item.item,
+						  impl_item.impl_block};
+	  try_result = MethodCandidate{
+	    PathProbeCandidate (PathProbeCandidate::CandidateType::IMPL_FUNC,
+				fn, impl_item.item->get_locus (), c),
+	    adjustments};
+	  return true;
+	}
+    }
+
+  for (auto trait_item : trait_fns)
+    {
+      TyTy::FnType *fn = trait_item.ty;
+      rust_assert (fn->is_method ());
+
+      TyTy::BaseType *fn_self = fn->get_self_type ();
+      if (fn_self->can_eq (&receiver, false))
+	{
+	  PathProbeCandidate::TraitItemCandidate c{trait_item.reference,
+						   trait_item.item_ref,
+						   nullptr};
+	  try_result = MethodCandidate{
+	    PathProbeCandidate (PathProbeCandidate::CandidateType::TRAIT_FUNC,
+				fn, trait_item.item->get_locus (), c),
+	    adjustments};
+	  return true;
+	}
+    }
+
+  for (const auto &predicate : predicate_items)
+    {
+      const TyTy::FnType *fn = predicate.fntype;
+      rust_assert (fn->is_method ());
+
+      TyTy::BaseType *fn_self = fn->get_self_type ();
+      if (fn_self->can_eq (&receiver, false))
+	{
+	  const TraitReference *trait_ref
+	    = predicate.lookup.get_parent ()->get ();
+	  const TraitItemReference *trait_item
+	    = predicate.lookup.get_raw_item ();
+
+	  PathProbeCandidate::TraitItemCandidate c{trait_ref, trait_item,
+						   nullptr};
+	  try_result = MethodCandidate{
+	    PathProbeCandidate (PathProbeCandidate::CandidateType::TRAIT_FUNC,
+				fn->clone (), trait_item->get_locus (), c),
+	    adjustments};
+	  return true;
+	}
+    }
+
+  return false;
+}
+
+std::vector<MethodResolver::predicate_candidate>
+MethodResolver::get_predicate_items (
+  const HIR::PathIdentSegment &segment_name, const TyTy::BaseType &receiver,
+  const std::vector<TyTy::TypeBoundPredicate> &specified_bounds)
+{
+  std::vector<predicate_candidate> predicate_items;
+  for (auto &bound : specified_bounds)
+    {
+      TyTy::TypeBoundPredicateItem lookup
+	= bound.lookup_associated_item (segment_name.as_string ());
+      if (lookup.is_error ())
+	continue;
+
+      TyTy::BaseType *ty = lookup.get_tyty_for_receiver (&receiver);
+      if (ty->get_kind () == TyTy::TypeKind::FNDEF)
+	{
+	  TyTy::FnType *fnty = static_cast<TyTy::FnType *> (ty);
+	  predicate_candidate candidate{lookup, fnty};
+	  predicate_items.push_back (candidate);
+	}
+    }
+
+  return predicate_items;
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-hir-dot-operator.h b/gcc/rust/typecheck/rust-hir-dot-operator.h
new file mode 100644
index 00000000000..750601a2d9e
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-dot-operator.h
@@ -0,0 +1,81 @@
+// 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_HIR_DOT_OPERATOR
+#define RUST_HIR_DOT_OPERATOR
+
+#include "rust-hir-path-probe.h"
+
+namespace Rust {
+namespace Resolver {
+
+struct MethodCandidate
+{
+  PathProbeCandidate candidate;
+  std::vector<Adjustment> adjustments;
+
+  static MethodCandidate get_error ()
+  {
+    return {PathProbeCandidate::get_error (), {}};
+  }
+
+  bool is_error () const { return candidate.is_error (); }
+};
+
+class MethodResolver : protected AutoderefCycle
+{
+public:
+  struct predicate_candidate
+  {
+    TyTy::TypeBoundPredicateItem lookup;
+    TyTy::FnType *fntype;
+  };
+
+  static MethodCandidate Probe (const TyTy::BaseType *receiver,
+				const HIR::PathIdentSegment &segment_name,
+				bool autoderef_flag = false);
+
+  static std::vector<predicate_candidate> get_predicate_items (
+    const HIR::PathIdentSegment &segment_name, const TyTy::BaseType &receiver,
+    const std::vector<TyTy::TypeBoundPredicate> &specified_bounds);
+
+protected:
+  MethodResolver (bool autoderef_flag,
+		  const HIR::PathIdentSegment &segment_name);
+
+  void try_hook (const TyTy::BaseType &r) override;
+
+  bool select (const TyTy::BaseType &receiver) override;
+
+private:
+  // context info
+  Analysis::Mappings *mappings;
+  TypeCheckContext *context;
+
+  // search
+  const HIR::PathIdentSegment &segment_name;
+  std::vector<MethodResolver::predicate_candidate> predicate_items;
+
+  // mutable fields
+  MethodCandidate try_result;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_HIR_DOT_OPERATOR
diff --git a/gcc/rust/typecheck/rust-hir-inherent-impl-overlap.h b/gcc/rust/typecheck/rust-hir-inherent-impl-overlap.h
new file mode 100644
index 00000000000..2890b54a00d
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-inherent-impl-overlap.h
@@ -0,0 +1,186 @@
+// 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_HIR_INHERENT_IMPL_ITEM_OVERLAP_H
+#define RUST_HIR_INHERENT_IMPL_ITEM_OVERLAP_H
+
+#include "rust-hir-type-check-base.h"
+#include "rust-hir-full.h"
+
+namespace Rust {
+namespace Resolver {
+
+class ImplItemToName : private TypeCheckBase, private HIR::HIRImplVisitor
+{
+public:
+  static bool resolve (HIR::ImplItem *item, std::string &name_result)
+  {
+    ImplItemToName resolver (name_result);
+    item->accept_vis (resolver);
+    return resolver.ok;
+  }
+
+  void visit (HIR::TypeAlias &alias) override
+  {
+    ok = true;
+    result.assign (alias.get_new_type_name ());
+  }
+
+  void visit (HIR::Function &function) override
+  {
+    ok = true;
+    result.assign (function.get_function_name ());
+  }
+
+  void visit (HIR::ConstantItem &constant) override
+  {
+    ok = true;
+    result.assign (constant.get_identifier ());
+  }
+
+private:
+  ImplItemToName (std::string &result)
+    : TypeCheckBase (), ok (false), result (result)
+  {}
+
+  bool ok;
+  std::string &result;
+};
+
+class OverlappingImplItemPass : public TypeCheckBase
+{
+public:
+  static void go ()
+  {
+    OverlappingImplItemPass pass;
+
+    // generate mappings
+    pass.mappings->iterate_impl_items (
+      [&] (HirId id, HIR::ImplItem *impl_item, HIR::ImplBlock *impl) -> bool {
+	// ignoring trait-impls might need thought later on
+	if (impl->has_trait_ref ())
+	  return true;
+
+	pass.process_impl_item (id, impl_item, impl);
+	return true;
+      });
+
+    pass.scan ();
+  }
+
+  void process_impl_item (HirId id, HIR::ImplItem *impl_item,
+			  HIR::ImplBlock *impl)
+  {
+    // lets make a mapping of impl-item Self type to (impl-item,name):
+    // {
+    //   impl-type -> [ (item, name), ... ]
+    // }
+
+    HirId impl_type_id = impl->get_type ()->get_mappings ().get_hirid ();
+    TyTy::BaseType *impl_type = nullptr;
+    bool ok = context->lookup_type (impl_type_id, &impl_type);
+    rust_assert (ok);
+
+    std::string impl_item_name;
+    ok = ImplItemToName::resolve (impl_item, impl_item_name);
+    rust_assert (ok);
+
+    std::pair<HIR::ImplItem *, std::string> elem (impl_item, impl_item_name);
+    impl_mappings[impl_type].insert (std::move (elem));
+  }
+
+  void scan ()
+  {
+    // we can now brute force the map looking for can_eq on each of the
+    // impl_items_types to look for possible colliding impl blocks;
+    for (auto it = impl_mappings.begin (); it != impl_mappings.end (); it++)
+      {
+	TyTy::BaseType *query = it->first;
+
+	for (auto iy = impl_mappings.begin (); iy != impl_mappings.end (); iy++)
+	  {
+	    TyTy::BaseType *candidate = iy->first;
+	    if (query == candidate)
+	      continue;
+
+	    if (query->can_eq (candidate, false))
+	      {
+		// we might be in the case that we have:
+		//
+		// *const T vs *const [T]
+		//
+		// so lets use an equality check when the
+		// candidates are both generic to be sure we dont emit a false
+		// positive
+
+		bool a = query->is_concrete ();
+		bool b = candidate->is_concrete ();
+		bool both_generic = !a && !b;
+		if (both_generic)
+		  {
+		    if (!query->is_equal (*candidate))
+		      continue;
+		  }
+
+		possible_collision (it->second, iy->second);
+	      }
+	  }
+      }
+  }
+
+  void possible_collision (
+    std::set<std::pair<HIR::ImplItem *, std::string> > query,
+    std::set<std::pair<HIR::ImplItem *, std::string> > candidate)
+  {
+    for (auto &q : query)
+      {
+	HIR::ImplItem *query_impl_item = q.first;
+	std::string query_impl_item_name = q.second;
+
+	for (auto &c : candidate)
+	  {
+	    HIR::ImplItem *candidate_impl_item = c.first;
+	    std::string candidate_impl_item_name = c.second;
+
+	    if (query_impl_item_name.compare (candidate_impl_item_name) == 0)
+	      collision_detected (query_impl_item, candidate_impl_item,
+				  candidate_impl_item_name);
+	  }
+      }
+  }
+
+  void collision_detected (HIR::ImplItem *query, HIR::ImplItem *dup,
+			   const std::string &name)
+  {
+    RichLocation r (dup->get_locus ());
+    r.add_range (query->get_locus ());
+    rust_error_at (r, "duplicate definitions with name %s", name.c_str ());
+  }
+
+private:
+  OverlappingImplItemPass () : TypeCheckBase () {}
+
+  std::map<TyTy::BaseType *,
+	   std::set<std::pair<HIR::ImplItem *, std::string> > >
+    impl_mappings;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_HIR_INHERENT_IMPL_ITEM_OVERLAP_H
diff --git a/gcc/rust/typecheck/rust-hir-path-probe.h b/gcc/rust/typecheck/rust-hir-path-probe.h
new file mode 100644
index 00000000000..bd4f91e49bf
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-path-probe.h
@@ -0,0 +1,540 @@
+// 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_HIR_PATH_PROBE_H
+#define RUST_HIR_PATH_PROBE_H
+
+#include "rust-hir-type-check-base.h"
+#include "rust-hir-full.h"
+#include "rust-tyty.h"
+#include "rust-substitution-mapper.h"
+#include "rust-hir-type-bounds.h"
+
+namespace Rust {
+namespace Resolver {
+
+struct PathProbeCandidate
+{
+  enum CandidateType
+  {
+    ERROR,
+
+    ENUM_VARIANT,
+
+    IMPL_CONST,
+    IMPL_TYPE_ALIAS,
+    IMPL_FUNC,
+
+    TRAIT_ITEM_CONST,
+    TRAIT_TYPE_ALIAS,
+    TRAIT_FUNC,
+  };
+
+  struct EnumItemCandidate
+  {
+    const TyTy::ADTType *parent;
+    const TyTy::VariantDef *variant;
+  };
+
+  struct ImplItemCandidate
+  {
+    HIR::ImplItem *impl_item;
+    HIR::ImplBlock *parent;
+  };
+
+  struct TraitItemCandidate
+  {
+    const TraitReference *trait_ref;
+    const TraitItemReference *item_ref;
+    HIR::ImplBlock *impl;
+  };
+
+  CandidateType type;
+  TyTy::BaseType *ty;
+  Location locus;
+  union Candidate
+  {
+    EnumItemCandidate enum_field;
+    ImplItemCandidate impl;
+    TraitItemCandidate trait;
+
+    Candidate (EnumItemCandidate enum_field) : enum_field (enum_field) {}
+    Candidate (ImplItemCandidate impl) : impl (impl) {}
+    Candidate (TraitItemCandidate trait) : trait (trait) {}
+  } item;
+
+  PathProbeCandidate (CandidateType type, TyTy::BaseType *ty, Location locus,
+		      EnumItemCandidate enum_field)
+    : type (type), ty (ty), item (enum_field)
+  {}
+
+  PathProbeCandidate (CandidateType type, TyTy::BaseType *ty, Location locus,
+		      ImplItemCandidate impl)
+    : type (type), ty (ty), item (impl)
+  {}
+
+  PathProbeCandidate (CandidateType type, TyTy::BaseType *ty, Location locus,
+		      TraitItemCandidate trait)
+    : type (type), ty (ty), item (trait)
+  {}
+
+  std::string as_string () const
+  {
+    return "PathProbe candidate TODO - as_string";
+  }
+
+  bool is_enum_candidate () const { return type == ENUM_VARIANT; }
+
+  bool is_impl_candidate () const
+  {
+    return type == IMPL_CONST || type == IMPL_TYPE_ALIAS || type == IMPL_FUNC;
+  }
+
+  bool is_trait_candidate () const
+  {
+    return type == TRAIT_ITEM_CONST || type == TRAIT_TYPE_ALIAS
+	   || type == TRAIT_FUNC;
+  }
+
+  bool is_full_trait_item_candidate () const
+  {
+    return is_trait_candidate () && item.trait.impl == nullptr;
+  }
+
+  static PathProbeCandidate get_error ()
+  {
+    return PathProbeCandidate (ERROR, nullptr, Location (),
+			       ImplItemCandidate{nullptr, nullptr});
+  }
+
+  bool is_error () const { return type == ERROR; }
+};
+
+class PathProbeType : public TypeCheckBase, public HIR::HIRImplVisitor
+{
+public:
+  static std::vector<PathProbeCandidate>
+  Probe (const TyTy::BaseType *receiver,
+	 const HIR::PathIdentSegment &segment_name, bool probe_impls,
+	 bool probe_bounds, bool ignore_mandatory_trait_items,
+	 DefId specific_trait_id = UNKNOWN_DEFID)
+  {
+    PathProbeType probe (receiver, segment_name, specific_trait_id);
+    if (probe_impls)
+      {
+	if (receiver->get_kind () == TyTy::TypeKind::ADT)
+	  {
+	    const TyTy::ADTType *adt
+	      = static_cast<const TyTy::ADTType *> (receiver);
+	    if (adt->is_enum ())
+	      probe.process_enum_item_for_candiates (adt);
+	  }
+
+	probe.process_impl_items_for_candidates ();
+      }
+
+    if (!probe_bounds)
+      return probe.candidates;
+
+    if (!probe.is_reciever_generic ())
+      {
+	std::vector<std::pair<TraitReference *, HIR::ImplBlock *>> probed_bounds
+	  = TypeBoundsProbe::Probe (receiver);
+	for (auto &candidate : probed_bounds)
+	  {
+	    const TraitReference *trait_ref = candidate.first;
+	    if (specific_trait_id != UNKNOWN_DEFID)
+	      {
+		if (trait_ref->get_mappings ().get_defid ()
+		    != specific_trait_id)
+		  continue;
+	      }
+
+	    HIR::ImplBlock *impl = candidate.second;
+	    probe.process_associated_trait_for_candidates (
+	      trait_ref, impl, ignore_mandatory_trait_items);
+	  }
+      }
+
+    for (const TyTy::TypeBoundPredicate &predicate :
+	 receiver->get_specified_bounds ())
+      {
+	const TraitReference *trait_ref = predicate.get ();
+	if (specific_trait_id != UNKNOWN_DEFID)
+	  {
+	    if (trait_ref->get_mappings ().get_defid () != specific_trait_id)
+	      continue;
+	  }
+
+	probe.process_predicate_for_candidates (predicate,
+						ignore_mandatory_trait_items);
+      }
+
+    return probe.candidates;
+  }
+
+  void visit (HIR::TypeAlias &alias) override
+  {
+    Identifier name = alias.get_new_type_name ();
+    if (search.as_string ().compare (name) == 0)
+      {
+	HirId tyid = alias.get_mappings ().get_hirid ();
+	TyTy::BaseType *ty = nullptr;
+	bool ok = context->lookup_type (tyid, &ty);
+	rust_assert (ok);
+
+	PathProbeCandidate::ImplItemCandidate impl_item_candidate{&alias,
+								  current_impl};
+	PathProbeCandidate candidate{
+	  PathProbeCandidate::CandidateType::IMPL_TYPE_ALIAS, ty,
+	  alias.get_locus (), impl_item_candidate};
+	candidates.push_back (std::move (candidate));
+      }
+  }
+
+  void visit (HIR::ConstantItem &constant) override
+  {
+    Identifier name = constant.get_identifier ();
+    if (search.as_string ().compare (name) == 0)
+      {
+	HirId tyid = constant.get_mappings ().get_hirid ();
+	TyTy::BaseType *ty = nullptr;
+	bool ok = context->lookup_type (tyid, &ty);
+	rust_assert (ok);
+
+	PathProbeCandidate::ImplItemCandidate impl_item_candidate{&constant,
+								  current_impl};
+	PathProbeCandidate candidate{
+	  PathProbeCandidate::CandidateType::IMPL_CONST, ty,
+	  constant.get_locus (), impl_item_candidate};
+	candidates.push_back (std::move (candidate));
+      }
+  }
+
+  void visit (HIR::Function &function) override
+  {
+    Identifier name = function.get_function_name ();
+    if (search.as_string ().compare (name) == 0)
+      {
+	HirId tyid = function.get_mappings ().get_hirid ();
+	TyTy::BaseType *ty = nullptr;
+	bool ok = context->lookup_type (tyid, &ty);
+	rust_assert (ok);
+
+	PathProbeCandidate::ImplItemCandidate impl_item_candidate{&function,
+								  current_impl};
+	PathProbeCandidate candidate{
+	  PathProbeCandidate::CandidateType::IMPL_FUNC, ty,
+	  function.get_locus (), impl_item_candidate};
+	candidates.push_back (std::move (candidate));
+      }
+  }
+
+protected:
+  void process_enum_item_for_candiates (const TyTy::ADTType *adt)
+  {
+    if (specific_trait_id != UNKNOWN_DEFID)
+      return;
+
+    TyTy::VariantDef *v;
+    if (!adt->lookup_variant (search.as_string (), &v))
+      return;
+
+    PathProbeCandidate::EnumItemCandidate enum_item_candidate{adt, v};
+    PathProbeCandidate candidate{
+      PathProbeCandidate::CandidateType::ENUM_VARIANT, receiver->clone (),
+      mappings->lookup_location (adt->get_ty_ref ()), enum_item_candidate};
+    candidates.push_back (std::move (candidate));
+  }
+
+  void process_impl_items_for_candidates ()
+  {
+    mappings->iterate_impl_items ([&] (HirId id, HIR::ImplItem *item,
+				       HIR::ImplBlock *impl) mutable -> bool {
+      process_impl_item_candidate (id, item, impl);
+      return true;
+    });
+  }
+
+  void process_impl_item_candidate (HirId id, HIR::ImplItem *item,
+				    HIR::ImplBlock *impl)
+  {
+    current_impl = impl;
+    HirId impl_ty_id = impl->get_type ()->get_mappings ().get_hirid ();
+    TyTy::BaseType *impl_block_ty = nullptr;
+    if (!context->lookup_type (impl_ty_id, &impl_block_ty))
+      return;
+
+    if (!receiver->can_eq (impl_block_ty, false))
+      {
+	if (!impl_block_ty->can_eq (receiver, false))
+	  return;
+      }
+
+    // lets visit the impl_item
+    item->accept_vis (*this);
+  }
+
+  void
+  process_associated_trait_for_candidates (const TraitReference *trait_ref,
+					   HIR::ImplBlock *impl,
+					   bool ignore_mandatory_trait_items)
+  {
+    const TraitItemReference *trait_item_ref = nullptr;
+    if (!trait_ref->lookup_trait_item (search.as_string (), &trait_item_ref))
+      return;
+
+    bool trait_item_needs_implementation = !trait_item_ref->is_optional ();
+    if (ignore_mandatory_trait_items && trait_item_needs_implementation)
+      return;
+
+    PathProbeCandidate::CandidateType candidate_type;
+    switch (trait_item_ref->get_trait_item_type ())
+      {
+      case TraitItemReference::TraitItemType::FN:
+	candidate_type = PathProbeCandidate::CandidateType::TRAIT_FUNC;
+	break;
+      case TraitItemReference::TraitItemType::CONST:
+	candidate_type = PathProbeCandidate::CandidateType::TRAIT_ITEM_CONST;
+	break;
+      case TraitItemReference::TraitItemType::TYPE:
+	candidate_type = PathProbeCandidate::CandidateType::TRAIT_TYPE_ALIAS;
+	break;
+
+      case TraitItemReference::TraitItemType::ERROR:
+      default:
+	gcc_unreachable ();
+	break;
+      }
+
+    TyTy::BaseType *trait_item_tyty = trait_item_ref->get_tyty ();
+
+    // we can substitute the Self with the receiver here
+    if (trait_item_tyty->get_kind () == TyTy::TypeKind::FNDEF)
+      {
+	TyTy::FnType *fn = static_cast<TyTy::FnType *> (trait_item_tyty);
+	TyTy::SubstitutionParamMapping *param = nullptr;
+	for (auto &param_mapping : fn->get_substs ())
+	  {
+	    const HIR::TypeParam &type_param
+	      = param_mapping.get_generic_param ();
+	    if (type_param.get_type_representation ().compare ("Self") == 0)
+	      {
+		param = &param_mapping;
+		break;
+	      }
+	  }
+	rust_assert (param != nullptr);
+
+	std::vector<TyTy::SubstitutionArg> mappings;
+	mappings.push_back (TyTy::SubstitutionArg (param, receiver->clone ()));
+
+	Location locus; // FIXME
+	TyTy::SubstitutionArgumentMappings args (std::move (mappings), locus);
+	trait_item_tyty = SubstMapperInternal::Resolve (trait_item_tyty, args);
+      }
+
+    PathProbeCandidate::TraitItemCandidate trait_item_candidate{trait_ref,
+								trait_item_ref,
+								impl};
+
+    PathProbeCandidate candidate{candidate_type, trait_item_tyty,
+				 trait_ref->get_locus (), trait_item_candidate};
+    candidates.push_back (std::move (candidate));
+  }
+
+  void
+  process_predicate_for_candidates (const TyTy::TypeBoundPredicate &predicate,
+				    bool ignore_mandatory_trait_items)
+  {
+    const TraitReference *trait_ref = predicate.get ();
+
+    TyTy::TypeBoundPredicateItem item
+      = predicate.lookup_associated_item (search.as_string ());
+    if (item.is_error ())
+      return;
+
+    if (ignore_mandatory_trait_items && item.needs_implementation ())
+      return;
+
+    const TraitItemReference *trait_item_ref = item.get_raw_item ();
+    PathProbeCandidate::CandidateType candidate_type;
+    switch (trait_item_ref->get_trait_item_type ())
+      {
+      case TraitItemReference::TraitItemType::FN:
+	candidate_type = PathProbeCandidate::CandidateType::TRAIT_FUNC;
+	break;
+      case TraitItemReference::TraitItemType::CONST:
+	candidate_type = PathProbeCandidate::CandidateType::TRAIT_ITEM_CONST;
+	break;
+      case TraitItemReference::TraitItemType::TYPE:
+	candidate_type = PathProbeCandidate::CandidateType::TRAIT_TYPE_ALIAS;
+	break;
+
+      case TraitItemReference::TraitItemType::ERROR:
+      default:
+	gcc_unreachable ();
+	break;
+      }
+
+    TyTy::BaseType *trait_item_tyty = item.get_tyty_for_receiver (receiver);
+    PathProbeCandidate::TraitItemCandidate trait_item_candidate{trait_ref,
+								trait_item_ref,
+								nullptr};
+    PathProbeCandidate candidate{candidate_type, trait_item_tyty,
+				 trait_item_ref->get_locus (),
+				 trait_item_candidate};
+    candidates.push_back (std::move (candidate));
+  }
+
+protected:
+  PathProbeType (const TyTy::BaseType *receiver,
+		 const HIR::PathIdentSegment &query, DefId specific_trait_id)
+    : TypeCheckBase (), receiver (receiver), search (query),
+      current_impl (nullptr), specific_trait_id (specific_trait_id)
+  {}
+
+  std::vector<std::pair<const TraitReference *, HIR::ImplBlock *>>
+  union_bounds (
+    const std::vector<std::pair</*const*/ TraitReference *, HIR::ImplBlock *>>
+      a,
+    const std::vector<std::pair<const TraitReference *, HIR::ImplBlock *>> b)
+    const
+  {
+    std::map<DefId, std::pair<const TraitReference *, HIR::ImplBlock *>> mapper;
+    for (auto &ref : a)
+      {
+	mapper.insert ({ref.first->get_mappings ().get_defid (), ref});
+      }
+    for (auto &ref : b)
+      {
+	mapper.insert ({ref.first->get_mappings ().get_defid (), ref});
+      }
+
+    std::vector<std::pair<const TraitReference *, HIR::ImplBlock *>> union_set;
+    for (auto it = mapper.begin (); it != mapper.end (); it++)
+      {
+	union_set.push_back ({it->second.first, it->second.second});
+      }
+    return union_set;
+  }
+
+  bool is_reciever_generic () const
+  {
+    const TyTy::BaseType *root = receiver->get_root ();
+    bool receiver_is_type_param = root->get_kind () == TyTy::TypeKind::PARAM;
+    bool receiver_is_dyn = root->get_kind () == TyTy::TypeKind::DYNAMIC;
+    return receiver_is_type_param || receiver_is_dyn;
+  }
+
+  const TyTy::BaseType *receiver;
+  const HIR::PathIdentSegment &search;
+  std::vector<PathProbeCandidate> candidates;
+  HIR::ImplBlock *current_impl;
+  DefId specific_trait_id;
+};
+
+class ReportMultipleCandidateError : private TypeCheckBase,
+				     private HIR::HIRImplVisitor
+{
+public:
+  static void Report (std::vector<PathProbeCandidate> &candidates,
+		      const HIR::PathIdentSegment &query, Location query_locus)
+  {
+    RichLocation r (query_locus);
+    ReportMultipleCandidateError visitor (r);
+    for (auto &c : candidates)
+      {
+	switch (c.type)
+	  {
+	  case PathProbeCandidate::CandidateType::ERROR:
+	  case PathProbeCandidate::CandidateType::ENUM_VARIANT:
+	    gcc_unreachable ();
+	    break;
+
+	  case PathProbeCandidate::CandidateType::IMPL_CONST:
+	  case PathProbeCandidate::CandidateType::IMPL_TYPE_ALIAS:
+	  case PathProbeCandidate::CandidateType::IMPL_FUNC:
+	    c.item.impl.impl_item->accept_vis (visitor);
+	    break;
+
+	  case PathProbeCandidate::CandidateType::TRAIT_ITEM_CONST:
+	  case PathProbeCandidate::CandidateType::TRAIT_TYPE_ALIAS:
+	  case PathProbeCandidate::CandidateType::TRAIT_FUNC:
+	    r.add_range (c.item.trait.item_ref->get_locus ());
+	    break;
+	  }
+      }
+
+    rust_error_at (r, "multiple applicable items in scope for: %s",
+		   query.as_string ().c_str ());
+  }
+
+  void visit (HIR::TypeAlias &alias) override
+  {
+    r.add_range (alias.get_locus ());
+  }
+
+  void visit (HIR::ConstantItem &constant) override
+  {
+    r.add_range (constant.get_locus ());
+  }
+
+  void visit (HIR::Function &function) override
+  {
+    r.add_range (function.get_locus ());
+  }
+
+private:
+  ReportMultipleCandidateError (RichLocation &r) : TypeCheckBase (), r (r) {}
+
+  RichLocation &r;
+};
+
+class PathProbeImplTrait : public PathProbeType
+{
+public:
+  static std::vector<PathProbeCandidate>
+  Probe (const TyTy::BaseType *receiver,
+	 const HIR::PathIdentSegment &segment_name,
+	 const TraitReference *trait_reference)
+  {
+    PathProbeImplTrait probe (receiver, segment_name, trait_reference);
+    // iterate all impls for this trait and receiver
+    // then search for possible candidates using base class behaviours
+    probe.process_trait_impl_items_for_candidates ();
+    return probe.candidates;
+  }
+
+private:
+  void process_trait_impl_items_for_candidates ();
+
+  PathProbeImplTrait (const TyTy::BaseType *receiver,
+		      const HIR::PathIdentSegment &query,
+		      const TraitReference *trait_reference)
+    : PathProbeType (receiver, query, UNKNOWN_DEFID),
+      trait_reference (trait_reference)
+  {}
+
+  const TraitReference *trait_reference;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_HIR_PATH_PROBE_H
diff --git a/gcc/rust/typecheck/rust-hir-trait-ref.h b/gcc/rust/typecheck/rust-hir-trait-ref.h
new file mode 100644
index 00000000000..6eec461e8a5
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-trait-ref.h
@@ -0,0 +1,472 @@
+// Copyright (C) 2021-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_HIR_TRAIT_REF_H
+#define RUST_HIR_TRAIT_REF_H
+
+#include "rust-hir-full.h"
+#include "rust-tyty-visitor.h"
+#include "rust-hir-type-check-util.h"
+
+namespace Rust {
+namespace Resolver {
+
+// Data Objects for the associated trait items in a structure we can work with
+// https://doc.rust-lang.org/edition-guide/rust-2018/trait-system/associated-constants.html
+class TypeCheckContext;
+class TraitItemReference
+{
+public:
+  enum TraitItemType
+  {
+    FN,
+    CONST,
+    TYPE,
+    ERROR
+  };
+
+  TraitItemReference (std::string identifier, bool optional, TraitItemType type,
+		      HIR::TraitItem *hir_trait_item, TyTy::BaseType *self,
+		      std::vector<TyTy::SubstitutionParamMapping> substitutions,
+		      Location locus);
+
+  TraitItemReference (TraitItemReference const &other);
+
+  TraitItemReference &operator= (TraitItemReference const &other);
+
+  static TraitItemReference error ()
+  {
+    return TraitItemReference ("", false, ERROR, nullptr, nullptr, {},
+			       Location ());
+  }
+
+  static TraitItemReference &error_node ()
+  {
+    static TraitItemReference error = TraitItemReference::error ();
+    return error;
+  }
+
+  bool is_error () const { return type == ERROR; }
+
+  std::string as_string () const
+  {
+    return "(" + trait_item_type_as_string (type) + " " + identifier + " "
+	   + ")";
+  }
+
+  static std::string trait_item_type_as_string (TraitItemType ty)
+  {
+    switch (ty)
+      {
+      case FN:
+	return "FN";
+      case CONST:
+	return "CONST";
+      case TYPE:
+	return "TYPE";
+      case ERROR:
+	return "ERROR";
+      }
+    return "ERROR";
+  }
+
+  bool is_optional () const { return optional_flag; }
+
+  std::string get_identifier () const { return identifier; }
+
+  TraitItemType get_trait_item_type () const { return type; }
+
+  HIR::TraitItem *get_hir_trait_item () const { return hir_trait_item; }
+
+  Location get_locus () const { return locus; }
+
+  const Analysis::NodeMapping get_mappings () const
+  {
+    return hir_trait_item->get_mappings ();
+  }
+
+  TyTy::BaseType *get_tyty () const
+  {
+    rust_assert (hir_trait_item != nullptr);
+
+    switch (type)
+      {
+      case CONST:
+	return get_type_from_constant (
+	  static_cast</*const*/ HIR::TraitItemConst &> (*hir_trait_item));
+	break;
+
+      case TYPE:
+	return get_type_from_typealias (
+	  static_cast</*const*/ HIR::TraitItemType &> (*hir_trait_item));
+
+      case FN:
+	return get_type_from_fn (
+	  static_cast</*const*/ HIR::TraitItemFunc &> (*hir_trait_item));
+	break;
+
+      default:
+	return get_error ();
+      }
+
+    gcc_unreachable ();
+    return get_error ();
+  }
+
+  Analysis::NodeMapping get_parent_trait_mappings () const;
+
+  // this is called when the trait is completed resolution and gives the items a
+  // chance to run their specific type resolution passes. If we call their
+  // resolution on construction it can lead to a case where the trait being
+  // resolved recursively trying to resolve the trait itself infinitely since
+  // the trait will not be stored in its own map yet
+  void on_resolved ();
+
+  void associated_type_set (TyTy::BaseType *ty) const;
+
+  void associated_type_reset () const;
+
+  bool is_object_safe () const;
+
+private:
+  TyTy::ErrorType *get_error () const
+  {
+    return new TyTy::ErrorType (get_mappings ().get_hirid ());
+  }
+
+  TyTy::BaseType *get_type_from_typealias (/*const*/
+					   HIR::TraitItemType &type) const;
+
+  TyTy::BaseType *
+  get_type_from_constant (/*const*/ HIR::TraitItemConst &constant) const;
+
+  TyTy::BaseType *get_type_from_fn (/*const*/ HIR::TraitItemFunc &fn) const;
+
+  bool is_item_resolved () const;
+  void resolve_item (HIR::TraitItemType &type);
+  void resolve_item (HIR::TraitItemConst &constant);
+  void resolve_item (HIR::TraitItemFunc &func);
+
+  std::string identifier;
+  bool optional_flag;
+  TraitItemType type;
+  HIR::TraitItem *hir_trait_item;
+  std::vector<TyTy::SubstitutionParamMapping> inherited_substitutions;
+  Location locus;
+
+  TyTy::BaseType
+    *self; // this is the implict Self TypeParam required for methods
+  Resolver::TypeCheckContext *context;
+};
+
+// this wraps up the HIR::Trait so we can do analysis on it
+
+class TraitReference
+{
+public:
+  TraitReference (const HIR::Trait *hir_trait_ref,
+		  std::vector<TraitItemReference> item_refs,
+		  std::vector<const TraitReference *> super_traits,
+		  std::vector<TyTy::SubstitutionParamMapping> substs)
+    : hir_trait_ref (hir_trait_ref), item_refs (item_refs),
+      super_traits (super_traits)
+  {
+    trait_substs.clear ();
+    trait_substs.reserve (substs.size ());
+    for (const auto &p : substs)
+      trait_substs.push_back (p.clone ());
+  }
+
+  TraitReference (TraitReference const &other)
+    : hir_trait_ref (other.hir_trait_ref), item_refs (other.item_refs),
+      super_traits (other.super_traits)
+  {
+    trait_substs.clear ();
+    trait_substs.reserve (other.trait_substs.size ());
+    for (const auto &p : other.trait_substs)
+      trait_substs.push_back (p.clone ());
+  }
+
+  TraitReference &operator= (TraitReference const &other)
+  {
+    hir_trait_ref = other.hir_trait_ref;
+    item_refs = other.item_refs;
+    super_traits = other.super_traits;
+
+    trait_substs.clear ();
+    trait_substs.reserve (other.trait_substs.size ());
+    for (const auto &p : other.trait_substs)
+      trait_substs.push_back (p.clone ());
+
+    return *this;
+  }
+
+  TraitReference (TraitReference &&other) = default;
+  TraitReference &operator= (TraitReference &&other) = default;
+
+  static TraitReference error ()
+  {
+    return TraitReference (nullptr, {}, {}, {});
+  }
+
+  bool is_error () const { return hir_trait_ref == nullptr; }
+
+  static TraitReference &error_node ()
+  {
+    static TraitReference trait_error_node = TraitReference::error ();
+    return trait_error_node;
+  }
+
+  Location get_locus () const { return hir_trait_ref->get_locus (); }
+
+  std::string get_name () const
+  {
+    rust_assert (!is_error ());
+    return hir_trait_ref->get_name ();
+  }
+
+  std::string as_string () const
+  {
+    if (is_error ())
+      return "<trait-ref-error-node>";
+
+    std::string item_buf;
+    for (auto &item : item_refs)
+      {
+	item_buf += item.as_string () + ", ";
+      }
+    return "HIR Trait: " + get_name () + "->"
+	   + hir_trait_ref->get_mappings ().as_string () + " [" + item_buf
+	   + "]";
+  }
+
+  const HIR::Trait *get_hir_trait_ref () const { return hir_trait_ref; }
+
+  const Analysis::NodeMapping &get_mappings () const
+  {
+    return hir_trait_ref->get_mappings ();
+  }
+
+  DefId get_defid () const { return get_mappings ().get_defid (); }
+
+  bool lookup_hir_trait_item (const HIR::TraitItem &item,
+			      TraitItemReference **ref)
+  {
+    return lookup_trait_item (item.trait_identifier (), ref);
+  }
+
+  bool lookup_trait_item (const std::string &ident, TraitItemReference **ref)
+  {
+    for (auto &item : item_refs)
+      {
+	if (ident.compare (item.get_identifier ()) == 0)
+	  {
+	    *ref = &item;
+	    return true;
+	  }
+      }
+    return false;
+  }
+
+  bool lookup_trait_item_by_type (const std::string &ident,
+				  TraitItemReference::TraitItemType type,
+				  TraitItemReference **ref)
+  {
+    for (auto &item : item_refs)
+      {
+	if (item.get_trait_item_type () != type)
+	  continue;
+
+	if (ident.compare (item.get_identifier ()) == 0)
+	  {
+	    *ref = &item;
+	    return true;
+	  }
+      }
+    return false;
+  }
+
+  bool lookup_trait_item_by_type (const std::string &ident,
+				  TraitItemReference::TraitItemType type,
+				  const TraitItemReference **ref) const
+  {
+    for (auto &item : item_refs)
+      {
+	if (item.get_trait_item_type () != type)
+	  continue;
+
+	if (ident.compare (item.get_identifier ()) == 0)
+	  {
+	    *ref = &item;
+	    return true;
+	  }
+      }
+    return false;
+  }
+
+  bool lookup_hir_trait_item (const HIR::TraitItem &item,
+			      const TraitItemReference **ref) const
+  {
+    return lookup_trait_item (item.trait_identifier (), ref);
+  }
+
+  bool lookup_trait_item (const std::string &ident,
+			  const TraitItemReference **ref) const
+  {
+    for (auto &item : item_refs)
+      {
+	if (ident.compare (item.get_identifier ()) == 0)
+	  {
+	    *ref = &item;
+	    return true;
+	  }
+      }
+    return false;
+  }
+
+  const TraitItemReference *
+  lookup_trait_item (const std::string &ident,
+		     TraitItemReference::TraitItemType type) const
+  {
+    for (auto &item : item_refs)
+      {
+	if (item.get_trait_item_type () != type)
+	  continue;
+
+	if (ident.compare (item.get_identifier ()) == 0)
+	  return &item;
+      }
+    return &TraitItemReference::error_node ();
+  }
+
+  size_t size () const { return item_refs.size (); }
+
+  const std::vector<TraitItemReference> &get_trait_items () const
+  {
+    return item_refs;
+  }
+
+  void on_resolved ()
+  {
+    for (auto &item : item_refs)
+      {
+	item.on_resolved ();
+      }
+  }
+
+  void clear_associated_types ()
+  {
+    for (auto &item : item_refs)
+      {
+	bool is_assoc_type = item.get_trait_item_type ()
+			     == TraitItemReference::TraitItemType::TYPE;
+	if (is_assoc_type)
+	  item.associated_type_reset ();
+      }
+  }
+
+  bool is_equal (const TraitReference &other) const
+  {
+    DefId this_id = get_mappings ().get_defid ();
+    DefId other_id = other.get_mappings ().get_defid ();
+    return this_id == other_id;
+  }
+
+  const std::vector<const TraitReference *> get_super_traits () const
+  {
+    return super_traits;
+  }
+
+  bool is_object_safe (bool emit_error, Location locus) const
+  {
+    // https: // doc.rust-lang.org/reference/items/traits.html#object-safety
+    std::vector<const TraitReference *> non_object_super_traits;
+    for (auto &item : super_traits)
+      {
+	if (!item->is_object_safe (false, Location ()))
+	  non_object_super_traits.push_back (item);
+      }
+
+    std::vector<const Resolver::TraitItemReference *> non_object_safe_items;
+    for (auto &item : get_trait_items ())
+      {
+	if (!item.is_object_safe ())
+	  non_object_safe_items.push_back (&item);
+      }
+
+    bool is_safe
+      = non_object_super_traits.empty () && non_object_safe_items.empty ();
+    if (emit_error && !is_safe)
+      {
+	RichLocation r (locus);
+	for (auto &item : non_object_super_traits)
+	  r.add_range (item->get_locus ());
+	for (auto &item : non_object_safe_items)
+	  r.add_range (item->get_locus ());
+
+	rust_error_at (r, "trait bound is not object safe");
+      }
+
+    return is_safe;
+  }
+
+  bool trait_has_generics () const { return !trait_substs.empty (); }
+
+  std::vector<TyTy::SubstitutionParamMapping> get_trait_substs () const
+  {
+    return trait_substs;
+  }
+
+private:
+  const HIR::Trait *hir_trait_ref;
+  std::vector<TraitItemReference> item_refs;
+  std::vector<const TraitReference *> super_traits;
+  std::vector<TyTy::SubstitutionParamMapping> trait_substs;
+};
+
+class AssociatedImplTrait
+{
+public:
+  AssociatedImplTrait (TraitReference *trait, HIR::ImplBlock *impl,
+		       TyTy::BaseType *self,
+		       Resolver::TypeCheckContext *context)
+    : trait (trait), impl (impl), self (self), context (context)
+  {}
+
+  TraitReference *get_trait () { return trait; }
+
+  HIR::ImplBlock *get_impl_block () { return impl; }
+
+  TyTy::BaseType *get_self () { return self; }
+
+  void setup_associated_types (const TyTy::BaseType *self,
+			       const TyTy::TypeBoundPredicate &bound);
+
+  void reset_associated_types ();
+
+private:
+  TraitReference *trait;
+  HIR::ImplBlock *impl;
+  TyTy::BaseType *self;
+  Resolver::TypeCheckContext *context;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_HIR_TRAIT_REF_H
diff --git a/gcc/rust/typecheck/rust-hir-trait-resolve.cc b/gcc/rust/typecheck/rust-hir-trait-resolve.cc
new file mode 100644
index 00000000000..5ad9540868c
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-trait-resolve.cc
@@ -0,0 +1,599 @@
+// Copyright (C) 2021-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/>.
+
+#include "rust-hir-trait-resolve.h"
+#include "rust-hir-type-check-expr.h"
+
+namespace Rust {
+namespace Resolver {
+
+void
+ResolveTraitItemToRef::visit (HIR::TraitItemType &type)
+{
+  // create trait-item-ref
+  Location locus = type.get_locus ();
+  bool is_optional = false;
+  std::string identifier = type.get_name ();
+
+  resolved = TraitItemReference (identifier, is_optional,
+				 TraitItemReference::TraitItemType::TYPE, &type,
+				 self, substitutions, locus);
+}
+
+void
+ResolveTraitItemToRef::visit (HIR::TraitItemConst &cst)
+{
+  // create trait-item-ref
+  Location locus = cst.get_locus ();
+  bool is_optional = cst.has_expr ();
+  std::string identifier = cst.get_name ();
+
+  resolved = TraitItemReference (identifier, is_optional,
+				 TraitItemReference::TraitItemType::CONST, &cst,
+				 self, substitutions, locus);
+}
+
+void
+ResolveTraitItemToRef::visit (HIR::TraitItemFunc &fn)
+{
+  // create trait-item-ref
+  Location locus = fn.get_locus ();
+  bool is_optional = fn.has_block_defined ();
+  std::string identifier = fn.get_decl ().get_function_name ();
+
+  resolved = TraitItemReference (identifier, is_optional,
+				 TraitItemReference::TraitItemType::FN, &fn,
+				 self, std::move (substitutions), locus);
+}
+
+ResolveTraitItemToRef::ResolveTraitItemToRef (
+  TyTy::BaseType *self,
+  std::vector<TyTy::SubstitutionParamMapping> &&substitutions)
+  : TypeCheckBase (), resolved (TraitItemReference::error ()), self (self),
+    substitutions (std::move (substitutions))
+{}
+
+// TraitItemReference items
+
+TraitReference *
+TraitResolver::Resolve (HIR::TypePath &path)
+{
+  TraitResolver resolver;
+  return resolver.resolve_path (path);
+}
+
+TraitReference *
+TraitResolver::Resolve (HIR::Trait &trait)
+{
+  TraitResolver resolver;
+  return resolver.resolve_trait (&trait);
+}
+
+TraitReference *
+TraitResolver::Lookup (HIR::TypePath &path)
+{
+  TraitResolver resolver;
+  return resolver.lookup_path (path);
+}
+
+TraitResolver::TraitResolver ()
+  : TypeCheckBase (), resolved_trait_reference (nullptr)
+{}
+
+TraitReference *
+TraitResolver::resolve_path (HIR::TypePath &path)
+{
+  NodeId ref;
+  if (!resolver->lookup_resolved_type (path.get_mappings ().get_nodeid (),
+				       &ref))
+    {
+      rust_error_at (path.get_locus (), "Failed to resolve path to node-id");
+      return &TraitReference::error_node ();
+    }
+
+  HirId hir_node = UNKNOWN_HIRID;
+  if (!mappings->lookup_node_to_hir (ref, &hir_node))
+    {
+      rust_error_at (path.get_locus (), "Failed to resolve path to hir-id");
+      return &TraitReference::error_node ();
+    }
+
+  HIR::Item *resolved_item = mappings->lookup_hir_item (hir_node);
+
+  rust_assert (resolved_item != nullptr);
+  resolved_item->accept_vis (*this);
+  rust_assert (resolved_trait_reference != nullptr);
+
+  return resolve_trait (resolved_trait_reference);
+}
+
+TraitReference *
+TraitResolver::resolve_trait (HIR::Trait *trait_reference)
+{
+  TraitReference *tref = &TraitReference::error_node ();
+  if (context->lookup_trait_reference (
+	trait_reference->get_mappings ().get_defid (), &tref))
+    {
+      return tref;
+    }
+
+  TyTy::BaseType *self = nullptr;
+  std::vector<TyTy::SubstitutionParamMapping> substitutions;
+  for (auto &generic_param : trait_reference->get_generic_params ())
+    {
+      switch (generic_param.get ()->get_kind ())
+	{
+	case HIR::GenericParam::GenericKind::LIFETIME:
+	case HIR::GenericParam::GenericKind::CONST:
+	  // FIXME: Skipping Lifetime and Const completely until better
+	  // handling.
+	  break;
+
+	  case HIR::GenericParam::GenericKind::TYPE: {
+	    auto param_type
+	      = TypeResolveGenericParam::Resolve (generic_param.get ());
+	    context->insert_type (generic_param->get_mappings (), param_type);
+
+	    auto &typaram = static_cast<HIR::TypeParam &> (*generic_param);
+	    substitutions.push_back (
+	      TyTy::SubstitutionParamMapping (typaram, param_type));
+
+	    if (typaram.get_type_representation ().compare ("Self") == 0)
+	      {
+		self = param_type;
+	      }
+	  }
+	  break;
+	}
+    }
+  rust_assert (self != nullptr);
+
+  // Check if there is a super-trait, and apply this bound to the Self
+  // TypeParam
+  std::vector<TyTy::TypeBoundPredicate> specified_bounds;
+
+  // copy the substitition mappings
+  std::vector<TyTy::SubstitutionParamMapping> self_subst_copy;
+  for (auto &sub : substitutions)
+    self_subst_copy.push_back (sub.clone ());
+
+  // They also inherit themselves as a bound this enables a trait item to
+  // reference other Self::trait_items
+  auto self_hrtb
+    = TyTy::TypeBoundPredicate (trait_reference->get_mappings ().get_defid (),
+				std::move (self_subst_copy),
+				trait_reference->get_locus ());
+  specified_bounds.push_back (self_hrtb);
+
+  // look for any
+  std::vector<const TraitReference *> super_traits;
+  if (trait_reference->has_type_param_bounds ())
+    {
+      for (auto &bound : trait_reference->get_type_param_bounds ())
+	{
+	  if (bound->get_bound_type ()
+	      == HIR::TypeParamBound::BoundType::TRAITBOUND)
+	    {
+	      HIR::TraitBound *b
+		= static_cast<HIR::TraitBound *> (bound.get ());
+
+	      // FIXME this might be recursive we need a check for that
+	      auto predicate = get_predicate_from_bound (b->get_path ());
+	      specified_bounds.push_back (predicate);
+	      super_traits.push_back (predicate.get ());
+	    }
+	}
+    }
+  self->inherit_bounds (specified_bounds);
+
+  std::vector<TraitItemReference> item_refs;
+  for (auto &item : trait_reference->get_trait_items ())
+    {
+      // make a copy of the substs
+      std::vector<TyTy::SubstitutionParamMapping> item_subst;
+      for (auto &sub : substitutions)
+	item_subst.push_back (sub.clone ());
+
+      TraitItemReference trait_item_ref
+	= ResolveTraitItemToRef::Resolve (*item.get (), self,
+					  std::move (item_subst));
+      item_refs.push_back (std::move (trait_item_ref));
+    }
+
+  TraitReference trait_object (trait_reference, item_refs,
+			       std::move (super_traits),
+			       std::move (substitutions));
+  context->insert_trait_reference (
+    trait_reference->get_mappings ().get_defid (), std::move (trait_object));
+
+  tref = &TraitReference::error_node ();
+  bool ok = context->lookup_trait_reference (
+    trait_reference->get_mappings ().get_defid (), &tref);
+  rust_assert (ok);
+
+  // hook to allow the trait to resolve its optional item blocks, we cant
+  // resolve the blocks of functions etc because it can end up in a recursive
+  // loop of trying to resolve traits as required by the types
+  tref->on_resolved ();
+
+  return tref;
+}
+
+TraitReference *
+TraitResolver::lookup_path (HIR::TypePath &path)
+{
+  NodeId ref;
+  if (!resolver->lookup_resolved_type (path.get_mappings ().get_nodeid (),
+				       &ref))
+    {
+      rust_error_at (path.get_locus (), "Failed to resolve path to node-id");
+      return &TraitReference::error_node ();
+    }
+
+  HirId hir_node = UNKNOWN_HIRID;
+  if (!mappings->lookup_node_to_hir (ref, &hir_node))
+    {
+      rust_error_at (path.get_locus (), "Failed to resolve path to hir-id");
+      return &TraitReference::error_node ();
+    }
+
+  HIR::Item *resolved_item = mappings->lookup_hir_item (hir_node);
+
+  rust_assert (resolved_item != nullptr);
+  resolved_item->accept_vis (*this);
+  rust_assert (resolved_trait_reference != nullptr);
+
+  TraitReference *tref = &TraitReference::error_node ();
+  if (context->lookup_trait_reference (
+	resolved_trait_reference->get_mappings ().get_defid (), &tref))
+    {
+      return tref;
+    }
+  return &TraitReference::error_node ();
+}
+
+void
+TraitItemReference::on_resolved ()
+{
+  switch (type)
+    {
+    case CONST:
+      resolve_item (static_cast<HIR::TraitItemConst &> (*hir_trait_item));
+      break;
+
+    case TYPE:
+      resolve_item (static_cast<HIR::TraitItemType &> (*hir_trait_item));
+      break;
+
+    case FN:
+      resolve_item (static_cast<HIR::TraitItemFunc &> (*hir_trait_item));
+      break;
+
+    default:
+      break;
+    }
+}
+
+void
+TraitItemReference::resolve_item (HIR::TraitItemType &type)
+{
+  TyTy::BaseType *ty
+    = new TyTy::PlaceholderType (type.get_name (),
+				 type.get_mappings ().get_hirid ());
+  context->insert_type (type.get_mappings (), ty);
+}
+
+void
+TraitItemReference::resolve_item (HIR::TraitItemConst &constant)
+{
+  // TODO
+}
+
+void
+TraitItemReference::resolve_item (HIR::TraitItemFunc &func)
+{
+  if (!is_optional ())
+    return;
+
+  TyTy::BaseType *item_tyty = get_tyty ();
+  if (item_tyty->get_kind () == TyTy::TypeKind::ERROR)
+    return;
+
+  // check the block and return types
+  rust_assert (item_tyty->get_kind () == TyTy::TypeKind::FNDEF);
+
+  // need to get the return type from this
+  TyTy::FnType *resolved_fn_type = static_cast<TyTy::FnType *> (item_tyty);
+  auto expected_ret_tyty = resolved_fn_type->get_return_type ();
+  context->push_return_type (TypeCheckContextItem (&func), expected_ret_tyty);
+
+  auto block_expr_ty = TypeCheckExpr::Resolve (func.get_block_expr ().get ());
+
+  context->pop_return_type ();
+
+  if (block_expr_ty->get_kind () != TyTy::NEVER)
+    expected_ret_tyty->unify (block_expr_ty);
+}
+
+void
+TraitItemReference::associated_type_set (TyTy::BaseType *ty) const
+{
+  rust_assert (get_trait_item_type () == TraitItemType::TYPE);
+
+  TyTy::BaseType *item_ty = get_tyty ();
+  rust_assert (item_ty->get_kind () == TyTy::TypeKind::PLACEHOLDER);
+  TyTy::PlaceholderType *placeholder
+    = static_cast<TyTy::PlaceholderType *> (item_ty);
+
+  placeholder->set_associated_type (ty->get_ty_ref ());
+}
+
+void
+TraitItemReference::associated_type_reset () const
+{
+  rust_assert (get_trait_item_type () == TraitItemType::TYPE);
+
+  TyTy::BaseType *item_ty = get_tyty ();
+  rust_assert (item_ty->get_kind () == TyTy::TypeKind::PLACEHOLDER);
+  TyTy::PlaceholderType *placeholder
+    = static_cast<TyTy::PlaceholderType *> (item_ty);
+
+  placeholder->clear_associated_type ();
+}
+
+void
+AssociatedImplTrait::setup_associated_types (
+  const TyTy::BaseType *self, const TyTy::TypeBoundPredicate &bound)
+{
+  // compute the constrained impl block generic arguments based on self and the
+  // higher ranked trait bound
+  TyTy::BaseType *receiver = self->clone ();
+
+  // impl<Y> SliceIndex<[Y]> for Range<usize>
+  // vs
+  // I: SliceIndex<[<integer>]> and Range<<integer>>
+  //
+  // we need to figure out what Y is
+
+  TyTy::BaseType *associated_self = get_self ();
+  rust_assert (associated_self->can_eq (self, false));
+
+  // grab the parameters
+  HIR::ImplBlock &impl_block = *get_impl_block ();
+  std::vector<TyTy::SubstitutionParamMapping> substitutions;
+  for (auto &generic_param : impl_block.get_generic_params ())
+    {
+      switch (generic_param.get ()->get_kind ())
+	{
+	case HIR::GenericParam::GenericKind::LIFETIME:
+	case HIR::GenericParam::GenericKind::CONST:
+	  // FIXME: Skipping Lifetime and Const completely until better
+	  // handling.
+	  break;
+
+	  case HIR::GenericParam::GenericKind::TYPE: {
+	    TyTy::BaseType *l = nullptr;
+	    bool ok = context->lookup_type (
+	      generic_param->get_mappings ().get_hirid (), &l);
+	    if (ok && l->get_kind () == TyTy::TypeKind::PARAM)
+	      {
+		substitutions.push_back (TyTy::SubstitutionParamMapping (
+		  static_cast<HIR::TypeParam &> (*generic_param),
+		  static_cast<TyTy::ParamType *> (l)));
+	      }
+	  }
+	  break;
+	}
+    }
+
+  // generate inference variables for these bound arguments so we can compute
+  // their values
+  Location locus;
+  std::vector<TyTy::SubstitutionArg> args;
+  for (auto &p : substitutions)
+    {
+      if (p.needs_substitution ())
+	{
+	  TyTy::TyVar infer_var = TyTy::TyVar::get_implicit_infer_var (locus);
+	  args.push_back (TyTy::SubstitutionArg (&p, infer_var.get_tyty ()));
+	}
+      else
+	{
+	  args.push_back (
+	    TyTy::SubstitutionArg (&p, p.get_param_ty ()->resolve ()));
+	}
+    }
+
+  // this callback gives us the parameters that get substituted so we can
+  // compute the constrained type parameters for this impl block
+  std::map<std::string, HirId> param_mappings;
+  TyTy::ParamSubstCb param_subst_cb
+    = [&] (const TyTy::ParamType &p, const TyTy::SubstitutionArg &a) {
+	param_mappings[p.get_symbol ()] = a.get_tyty ()->get_ref ();
+      };
+
+  TyTy::SubstitutionArgumentMappings infer_arguments (std::move (args), locus,
+						      param_subst_cb);
+  TyTy::BaseType *impl_self_infer
+    = (associated_self->needs_generic_substitutions ())
+	? SubstMapperInternal::Resolve (associated_self, infer_arguments)
+	: associated_self;
+
+  // FIXME this needs to do a lookup for the trait-reference DefId instead of
+  // assuming its the first one in the list
+  rust_assert (associated_self->num_specified_bounds () > 0);
+  TyTy::TypeBoundPredicate &impl_predicate
+    = associated_self->get_specified_bounds ().at (0);
+
+  // infer the arguments on the predicate
+  std::vector<TyTy::BaseType *> impl_trait_predicate_args;
+  for (const auto &arg : impl_predicate.get_substs ())
+    {
+      const TyTy::ParamType *p = arg.get_param_ty ();
+      if (p->get_symbol ().compare ("Self") == 0)
+	continue;
+
+      TyTy::BaseType *r = p->resolve ();
+      r = SubstMapperInternal::Resolve (r, infer_arguments);
+      impl_trait_predicate_args.push_back (r);
+    }
+
+  // we need to unify the receiver with the impl-block Self so that we compute
+  // the type correctly as our receiver may be generic and we are inferring its
+  // generic arguments and this Self might be the concrete version or vice
+  // versa.
+  auto result = receiver->unify (impl_self_infer);
+  rust_assert (result->get_kind () != TyTy::TypeKind::ERROR);
+
+  // unify the bounds arguments
+  std::vector<TyTy::BaseType *> hrtb_bound_arguments;
+  for (const auto &arg : bound.get_substs ())
+    {
+      const TyTy::ParamType *p = arg.get_param_ty ();
+      if (p->get_symbol ().compare ("Self") == 0)
+	continue;
+
+      TyTy::BaseType *r = p->resolve ();
+      hrtb_bound_arguments.push_back (r);
+    }
+
+  rust_assert (impl_trait_predicate_args.size ()
+	       == hrtb_bound_arguments.size ());
+  for (size_t i = 0; i < impl_trait_predicate_args.size (); i++)
+    {
+      TyTy::BaseType *a = impl_trait_predicate_args.at (i);
+      TyTy::BaseType *b = hrtb_bound_arguments.at (i);
+
+      result = a->unify (b);
+      rust_assert (result->get_kind () != TyTy::TypeKind::ERROR);
+    }
+
+  // create the argument list
+  std::vector<TyTy::SubstitutionArg> associated_arguments;
+  for (auto &p : substitutions)
+    {
+      std::string symbol = p.get_param_ty ()->get_symbol ();
+      auto it = param_mappings.find (symbol);
+      rust_assert (it != param_mappings.end ());
+
+      HirId id = it->second;
+      TyTy::BaseType *argument = nullptr;
+      bool ok = context->lookup_type (id, &argument);
+      rust_assert (ok);
+
+      TyTy::SubstitutionArg arg (&p, argument);
+      associated_arguments.push_back (arg);
+    }
+
+  TyTy::SubstitutionArgumentMappings associated_type_args (
+    std::move (associated_arguments), locus);
+
+  ImplTypeIterator iter (*impl, [&] (HIR::TypeAlias &type) {
+    TraitItemReference *resolved_trait_item = nullptr;
+    bool ok = trait->lookup_trait_item (type.get_new_type_name (),
+					&resolved_trait_item);
+    if (!ok)
+      return;
+    if (resolved_trait_item->get_trait_item_type ()
+	!= TraitItemReference::TraitItemType::TYPE)
+      return;
+
+    TyTy::BaseType *lookup;
+    if (!context->lookup_type (type.get_mappings ().get_hirid (), &lookup))
+      return;
+
+    // this might be generic
+    TyTy::BaseType *substituted
+      = SubstMapperInternal::Resolve (lookup, associated_type_args);
+    resolved_trait_item->associated_type_set (substituted);
+  });
+  iter.go ();
+}
+
+void
+AssociatedImplTrait::reset_associated_types ()
+{
+  trait->clear_associated_types ();
+}
+
+Analysis::NodeMapping
+TraitItemReference::get_parent_trait_mappings () const
+{
+  auto mappings = Analysis::Mappings::get ();
+
+  HIR::Trait *trait
+    = mappings->lookup_trait_item_mapping (get_mappings ().get_hirid ());
+  rust_assert (trait != nullptr);
+
+  return trait->get_mappings ();
+}
+
+bool
+TraitItemReference::is_object_safe () const
+{
+  // https://doc.rust-lang.org/reference/items/traits.html#object-safety
+  switch (get_trait_item_type ())
+    {
+      case TraitItemReference::TraitItemType::FN: {
+	// lets be boring and just check that this is indeed a method will do
+	// for now
+	const HIR::TraitItem *item = get_hir_trait_item ();
+	const HIR::TraitItemFunc *fn
+	  = static_cast<const HIR::TraitItemFunc *> (item);
+	return fn->get_decl ().is_method ();
+      }
+
+      // constants are not available via dyn dispatch and so is not object safe
+    case TraitItemReference::TraitItemType::CONST:
+      return false;
+
+      // types are object safe since they are not available via dyn dispatch
+    case TraitItemReference::TraitItemType::TYPE:
+      return true;
+
+      // this is just an error so lets just fail it
+    case TraitItemReference::TraitItemType::ERROR:
+      return false;
+    }
+  return false;
+}
+
+// rust-hir-path-probe.h
+
+void
+PathProbeImplTrait::process_trait_impl_items_for_candidates ()
+{
+  mappings->iterate_impl_items (
+    [&] (HirId id, HIR::ImplItem *item, HIR::ImplBlock *impl) mutable -> bool {
+      // just need to check if this is an impl block for this trait the next
+      // function checks the receiver
+      if (!impl->has_trait_ref ())
+	return true;
+
+      TraitReference *resolved
+	= TraitResolver::Lookup (*(impl->get_trait_ref ().get ()));
+      if (!trait_reference->is_equal (*resolved))
+	return true;
+
+      process_impl_item_candidate (id, item, impl);
+      return true;
+    });
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-hir-trait-resolve.h b/gcc/rust/typecheck/rust-hir-trait-resolve.h
new file mode 100644
index 00000000000..c4aaf42b141
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-trait-resolve.h
@@ -0,0 +1,87 @@
+// Copyright (C) 2021-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_HIR_TRAIT_RESOLVE_H
+#define RUST_HIR_TRAIT_RESOLVE_H
+
+#include "rust-hir-type-check-base.h"
+#include "rust-hir-type-check-type.h"
+#include "rust-hir-trait-ref.h"
+
+namespace Rust {
+namespace Resolver {
+
+class ResolveTraitItemToRef : public TypeCheckBase,
+			      private HIR::HIRTraitItemVisitor
+{
+public:
+  static TraitItemReference
+  Resolve (HIR::TraitItem &item, TyTy::BaseType *self,
+	   std::vector<TyTy::SubstitutionParamMapping> substitutions)
+  {
+    ResolveTraitItemToRef resolver (self, std::move (substitutions));
+    item.accept_vis (resolver);
+    return std::move (resolver.resolved);
+  }
+
+  void visit (HIR::TraitItemType &type) override;
+
+  void visit (HIR::TraitItemConst &cst) override;
+
+  void visit (HIR::TraitItemFunc &fn) override;
+
+private:
+  ResolveTraitItemToRef (
+    TyTy::BaseType *self,
+    std::vector<TyTy::SubstitutionParamMapping> &&substitutions);
+
+  TraitItemReference resolved;
+  TyTy::BaseType *self;
+  std::vector<TyTy::SubstitutionParamMapping> substitutions;
+};
+
+class TraitResolver : public TypeCheckBase, private HIR::HIRFullVisitorBase
+{
+  using HIR::HIRFullVisitorBase::visit;
+
+public:
+  static TraitReference *Resolve (HIR::TypePath &path);
+
+  static TraitReference *Resolve (HIR::Trait &trait);
+
+  static TraitReference *Lookup (HIR::TypePath &path);
+
+private:
+  TraitResolver ();
+
+  TraitReference *resolve_path (HIR::TypePath &path);
+
+  TraitReference *resolve_trait (HIR::Trait *trait_reference);
+
+  TraitReference *lookup_path (HIR::TypePath &path);
+
+  HIR::Trait *resolved_trait_reference;
+
+public:
+  void visit (HIR::Trait &trait) override { resolved_trait_reference = &trait; }
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_HIR_TRAIT_RESOLVE_H
diff --git a/gcc/rust/typecheck/rust-hir-type-bounds.h b/gcc/rust/typecheck/rust-hir-type-bounds.h
new file mode 100644
index 00000000000..44400efbbf7
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-bounds.h
@@ -0,0 +1,77 @@
+// Copyright (C) 2021-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_HIR_TYPE_BOUNDS_H
+#define RUST_HIR_TYPE_BOUNDS_H
+
+#include "rust-hir-type-check-base.h"
+#include "rust-hir-full.h"
+#include "rust-tyty.h"
+
+namespace Rust {
+namespace Resolver {
+
+class TypeBoundsProbe : public TypeCheckBase
+{
+public:
+  static std::vector<std::pair<TraitReference *, HIR::ImplBlock *>>
+  Probe (const TyTy::BaseType *receiver)
+  {
+    TypeBoundsProbe probe (receiver);
+    probe.scan ();
+    return probe.trait_references;
+  }
+
+  static bool is_bound_satisfied_for_type (TyTy::BaseType *receiver,
+					   TraitReference *ref)
+  {
+    for (auto &bound : receiver->get_specified_bounds ())
+      {
+	const TraitReference *b = bound.get ();
+	if (b->is_equal (*ref))
+	  return true;
+      }
+
+    std::vector<std::pair<TraitReference *, HIR::ImplBlock *>> bounds
+      = Probe (receiver);
+    for (auto &bound : bounds)
+      {
+	const TraitReference *b = bound.first;
+	if (b->is_equal (*ref))
+	  return true;
+      }
+
+    return false;
+  }
+
+private:
+  void scan ();
+
+private:
+  TypeBoundsProbe (const TyTy::BaseType *receiver)
+    : TypeCheckBase (), receiver (receiver)
+  {}
+
+  const TyTy::BaseType *receiver;
+  std::vector<std::pair<TraitReference *, HIR::ImplBlock *>> trait_references;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_HIR_TYPE_BOUNDS_H
diff --git a/gcc/rust/typecheck/rust-hir-type-check-base.cc b/gcc/rust/typecheck/rust-hir-type-check-base.cc
new file mode 100644
index 00000000000..ac5c3b97475
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-base.cc
@@ -0,0 +1,439 @@
+// 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/>.
+
+#include "rust-hir-type-check-base.h"
+#include "rust-hir-type-check-type.h"
+#include "rust-hir-type-check-expr.h"
+#include "rust-coercion.h"
+#include "rust-casts.h"
+
+namespace Rust {
+namespace Resolver {
+
+bool
+TypeCheckBase::check_for_unconstrained (
+  const std::vector<TyTy::SubstitutionParamMapping> &params_to_constrain,
+  const TyTy::SubstitutionArgumentMappings &constraint_a,
+  const TyTy::SubstitutionArgumentMappings &constraint_b,
+  const TyTy::BaseType *reference)
+{
+  std::set<HirId> symbols_to_constrain;
+  std::map<HirId, Location> symbol_to_location;
+  for (const auto &p : params_to_constrain)
+    {
+      HirId ref = p.get_param_ty ()->get_ref ();
+      symbols_to_constrain.insert (ref);
+      symbol_to_location.insert ({ref, p.get_param_locus ()});
+    }
+
+  // set up the set of constrained symbols
+  std::set<HirId> constrained_symbols;
+  for (const auto &c : constraint_a.get_mappings ())
+    {
+      const TyTy::BaseType *arg = c.get_tyty ();
+      if (arg != nullptr)
+	{
+	  const TyTy::BaseType *p = arg->get_root ();
+	  constrained_symbols.insert (p->get_ty_ref ());
+	}
+    }
+  for (const auto &c : constraint_b.get_mappings ())
+    {
+      const TyTy::BaseType *arg = c.get_tyty ();
+      if (arg != nullptr)
+	{
+	  const TyTy::BaseType *p = arg->get_root ();
+	  constrained_symbols.insert (p->get_ty_ref ());
+	}
+    }
+
+  const auto root = reference->get_root ();
+  if (root->get_kind () == TyTy::TypeKind::PARAM)
+    {
+      const TyTy::ParamType *p = static_cast<const TyTy::ParamType *> (root);
+      constrained_symbols.insert (p->get_ty_ref ());
+    }
+
+  // check for unconstrained
+  bool unconstrained = false;
+  for (auto &sym : symbols_to_constrain)
+    {
+      bool used = constrained_symbols.find (sym) != constrained_symbols.end ();
+      if (!used)
+	{
+	  Location locus = symbol_to_location.at (sym);
+	  rust_error_at (locus, "unconstrained type parameter");
+	  unconstrained = true;
+	}
+    }
+  return unconstrained;
+}
+
+TyTy::BaseType *
+TypeCheckBase::resolve_literal (const Analysis::NodeMapping &expr_mappings,
+				HIR::Literal &literal, Location locus)
+{
+  TyTy::BaseType *infered = nullptr;
+  switch (literal.get_lit_type ())
+    {
+      case HIR::Literal::LitType::INT: {
+	bool ok = false;
+
+	switch (literal.get_type_hint ())
+	  {
+	  case CORETYPE_I8:
+	    ok = context->lookup_builtin ("i8", &infered);
+	    break;
+	  case CORETYPE_I16:
+	    ok = context->lookup_builtin ("i16", &infered);
+	    break;
+	  case CORETYPE_I32:
+	    ok = context->lookup_builtin ("i32", &infered);
+	    break;
+	  case CORETYPE_I64:
+	    ok = context->lookup_builtin ("i64", &infered);
+	    break;
+	  case CORETYPE_I128:
+	    ok = context->lookup_builtin ("i128", &infered);
+	    break;
+
+	  case CORETYPE_U8:
+	    ok = context->lookup_builtin ("u8", &infered);
+	    break;
+	  case CORETYPE_U16:
+	    ok = context->lookup_builtin ("u16", &infered);
+	    break;
+	  case CORETYPE_U32:
+	    ok = context->lookup_builtin ("u32", &infered);
+	    break;
+	  case CORETYPE_U64:
+	    ok = context->lookup_builtin ("u64", &infered);
+	    break;
+	  case CORETYPE_U128:
+	    ok = context->lookup_builtin ("u128", &infered);
+	    break;
+
+	  case CORETYPE_F32:
+	    literal.set_lit_type (HIR::Literal::LitType::FLOAT);
+	    ok = context->lookup_builtin ("f32", &infered);
+	    break;
+	  case CORETYPE_F64:
+	    literal.set_lit_type (HIR::Literal::LitType::FLOAT);
+	    ok = context->lookup_builtin ("f64", &infered);
+	    break;
+
+	  case CORETYPE_ISIZE:
+	    ok = context->lookup_builtin ("isize", &infered);
+	    break;
+
+	  case CORETYPE_USIZE:
+	    ok = context->lookup_builtin ("usize", &infered);
+	    break;
+
+	  default:
+	    ok = true;
+	    infered
+	      = new TyTy::InferType (expr_mappings.get_hirid (),
+				     TyTy::InferType::InferTypeKind::INTEGRAL,
+				     locus);
+	    break;
+	  }
+	rust_assert (ok);
+      }
+      break;
+
+      case HIR::Literal::LitType::FLOAT: {
+	bool ok = false;
+
+	switch (literal.get_type_hint ())
+	  {
+	  case CORETYPE_F32:
+	    ok = context->lookup_builtin ("f32", &infered);
+	    break;
+	  case CORETYPE_F64:
+	    ok = context->lookup_builtin ("f64", &infered);
+	    break;
+
+	  default:
+	    ok = true;
+	    infered
+	      = new TyTy::InferType (expr_mappings.get_hirid (),
+				     TyTy::InferType::InferTypeKind::FLOAT,
+				     locus);
+	    break;
+	  }
+	rust_assert (ok);
+      }
+      break;
+
+      case HIR::Literal::LitType::BOOL: {
+	auto ok = context->lookup_builtin ("bool", &infered);
+	rust_assert (ok);
+      }
+      break;
+
+      case HIR::Literal::LitType::CHAR: {
+	auto ok = context->lookup_builtin ("char", &infered);
+	rust_assert (ok);
+      }
+      break;
+
+      case HIR::Literal::LitType::BYTE: {
+	auto ok = context->lookup_builtin ("u8", &infered);
+	rust_assert (ok);
+      }
+      break;
+
+      case HIR::Literal::LitType::STRING: {
+	TyTy::BaseType *base = nullptr;
+	auto ok = context->lookup_builtin ("str", &base);
+	rust_assert (ok);
+
+	infered = new TyTy::ReferenceType (expr_mappings.get_hirid (),
+					   TyTy::TyVar (base->get_ref ()),
+					   Mutability::Imm);
+      }
+      break;
+
+      case HIR::Literal::LitType::BYTE_STRING: {
+	/* This is an arraytype of u8 reference (&[u8;size]). It isn't in
+	   UTF-8, but really just a byte array. Code to construct the array
+	   reference copied from ArrayElemsValues and ArrayType. */
+	TyTy::BaseType *u8;
+	auto ok = context->lookup_builtin ("u8", &u8);
+	rust_assert (ok);
+
+	auto crate_num = mappings->get_current_crate ();
+	Analysis::NodeMapping capacity_mapping (crate_num, UNKNOWN_NODEID,
+						mappings->get_next_hir_id (
+						  crate_num),
+						UNKNOWN_LOCAL_DEFID);
+
+	/* Capacity is the size of the string (number of chars).
+	   It is a constant, but for fold it to get a tree.  */
+	std::string capacity_str
+	  = std::to_string (literal.as_string ().size ());
+	HIR::LiteralExpr *literal_capacity
+	  = new HIR::LiteralExpr (capacity_mapping, capacity_str,
+				  HIR::Literal::LitType::INT,
+				  PrimitiveCoreType::CORETYPE_USIZE, locus, {});
+
+	// mark the type for this implicit node
+	TyTy::BaseType *expected_ty = nullptr;
+	ok = context->lookup_builtin ("usize", &expected_ty);
+	rust_assert (ok);
+	context->insert_type (capacity_mapping, expected_ty);
+
+	Analysis::NodeMapping array_mapping (crate_num, UNKNOWN_NODEID,
+					     mappings->get_next_hir_id (
+					       crate_num),
+					     UNKNOWN_LOCAL_DEFID);
+
+	TyTy::ArrayType *array
+	  = new TyTy::ArrayType (array_mapping.get_hirid (), locus,
+				 *literal_capacity,
+				 TyTy::TyVar (u8->get_ref ()));
+	context->insert_type (array_mapping, array);
+
+	infered = new TyTy::ReferenceType (expr_mappings.get_hirid (),
+					   TyTy::TyVar (array->get_ref ()),
+					   Mutability::Imm);
+      }
+      break;
+
+    default:
+      gcc_unreachable ();
+      break;
+    }
+
+  return infered;
+}
+
+TyTy::ADTType::ReprOptions
+TypeCheckBase::parse_repr_options (const AST::AttrVec &attrs, Location locus)
+{
+  TyTy::ADTType::ReprOptions repr;
+  repr.pack = 0;
+  repr.align = 0;
+
+  for (const auto &attr : attrs)
+    {
+      bool is_repr = attr.get_path ().as_string ().compare ("repr") == 0;
+      if (is_repr)
+	{
+	  const AST::AttrInput &input = attr.get_attr_input ();
+	  bool is_token_tree = input.get_attr_input_type ()
+			       == AST::AttrInput::AttrInputType::TOKEN_TREE;
+	  rust_assert (is_token_tree);
+	  const auto &option = static_cast<const AST::DelimTokenTree &> (input);
+	  AST::AttrInputMetaItemContainer *meta_items
+	    = option.parse_to_meta_item ();
+
+	  const std::string inline_option
+	    = meta_items->get_items ().at (0)->as_string ();
+
+	  // TODO: it would probably be better to make the MetaItems more aware
+	  // of constructs with nesting like #[repr(packed(2))] rather than
+	  // manually parsing the string "packed(2)" here.
+
+	  size_t oparen = inline_option.find ('(', 0);
+	  bool is_pack = false, is_align = false;
+	  unsigned char value = 1;
+
+	  if (oparen == std::string::npos)
+	    {
+	      is_pack = inline_option.compare ("packed") == 0;
+	      is_align = inline_option.compare ("align") == 0;
+	    }
+
+	  else
+	    {
+	      std::string rep = inline_option.substr (0, oparen);
+	      is_pack = rep.compare ("packed") == 0;
+	      is_align = rep.compare ("align") == 0;
+
+	      size_t cparen = inline_option.find (')', oparen);
+	      if (cparen == std::string::npos)
+		{
+		  rust_error_at (locus, "malformed attribute");
+		}
+
+	      std::string value_str = inline_option.substr (oparen, cparen);
+	      value = strtoul (value_str.c_str () + 1, NULL, 10);
+	    }
+
+	  if (is_pack)
+	    repr.pack = value;
+	  else if (is_align)
+	    repr.align = value;
+
+	  // Multiple repr options must be specified with e.g. #[repr(C,
+	  // packed(2))].
+	  break;
+	}
+    }
+
+  return repr;
+}
+
+TyTy::BaseType *
+TypeCheckBase::coercion_site (HirId id, TyTy::BaseType *expected,
+			      TyTy::BaseType *expr, Location locus)
+{
+  rust_debug ("coercion_site id={%u} expected={%s} expr={%s}", id,
+	      expected->debug_str ().c_str (), expr->debug_str ().c_str ());
+
+  auto context = TypeCheckContext::get ();
+  if (expected->get_kind () == TyTy::TypeKind::ERROR
+      || expr->get_kind () == TyTy::TypeKind::ERROR)
+    return expr;
+
+  // can we autoderef it?
+  auto result = TypeCoercionRules::Coerce (expr, expected, locus);
+
+  // the result needs to be unified
+  TyTy::BaseType *receiver = expr;
+  if (!result.is_error ())
+    {
+      receiver = result.tyty;
+    }
+
+  rust_debug ("coerce_default_unify(a={%s}, b={%s})",
+	      receiver->debug_str ().c_str (), expected->debug_str ().c_str ());
+  TyTy::BaseType *coerced = expected->unify (receiver);
+  context->insert_autoderef_mappings (id, std::move (result.adjustments));
+  return coerced;
+}
+
+TyTy::BaseType *
+TypeCheckBase::cast_site (HirId id, TyTy::TyWithLocation from,
+			  TyTy::TyWithLocation to, Location cast_locus)
+{
+  rust_debug ("cast_site id={%u} from={%s} to={%s}", id,
+	      from.get_ty ()->debug_str ().c_str (),
+	      to.get_ty ()->debug_str ().c_str ());
+
+  auto context = TypeCheckContext::get ();
+  if (from.get_ty ()->get_kind () == TyTy::TypeKind::ERROR
+      || to.get_ty ()->get_kind () == TyTy::TypeKind::ERROR)
+    return to.get_ty ();
+
+  // do the cast
+  auto result = TypeCastRules::resolve (cast_locus, from, to);
+
+  // we assume error has already been emitted
+  if (result.is_error ())
+    return to.get_ty ();
+
+  // the result needs to be unified
+  TyTy::BaseType *casted_result = result.tyty;
+  rust_debug ("cast_default_unify(a={%s}, b={%s})",
+	      casted_result->debug_str ().c_str (),
+	      to.get_ty ()->debug_str ().c_str ());
+  TyTy::BaseType *casted = to.get_ty ()->unify (casted_result);
+  context->insert_cast_autoderef_mappings (id, std::move (result.adjustments));
+  return casted;
+}
+
+void
+TypeCheckBase::resolve_generic_params (
+  const std::vector<std::unique_ptr<HIR::GenericParam>> &generic_params,
+  std::vector<TyTy::SubstitutionParamMapping> &substitutions)
+{
+  for (auto &generic_param : generic_params)
+    {
+      switch (generic_param.get ()->get_kind ())
+	{
+	case HIR::GenericParam::GenericKind::LIFETIME:
+	  // FIXME: Skipping Lifetime completely until better
+	  // handling.
+	  break;
+	  case HIR::GenericParam::GenericKind::CONST: {
+	    auto param
+	      = static_cast<HIR::ConstGenericParam *> (generic_param.get ());
+	    auto specified_type
+	      = TypeCheckType::Resolve (param->get_type ().get ());
+
+	    if (param->has_default_expression ())
+	      {
+		auto expr_type = TypeCheckExpr::Resolve (
+		  param->get_default_expression ().get ());
+
+		specified_type->unify (expr_type);
+	      }
+
+	    context->insert_type (generic_param->get_mappings (),
+				  specified_type);
+	  }
+	  break;
+
+	  case HIR::GenericParam::GenericKind::TYPE: {
+	    auto param_type
+	      = TypeResolveGenericParam::Resolve (generic_param.get ());
+	    context->insert_type (generic_param->get_mappings (), param_type);
+
+	    substitutions.push_back (TyTy::SubstitutionParamMapping (
+	      static_cast<HIR::TypeParam &> (*generic_param), param_type));
+	  }
+	  break;
+	}
+    }
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-hir-type-check-base.h b/gcc/rust/typecheck/rust-hir-type-check-base.h
new file mode 100644
index 00000000000..aa42d9d6dfd
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-base.h
@@ -0,0 +1,80 @@
+// 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_HIR_TYPE_CHECK_BASE
+#define RUST_HIR_TYPE_CHECK_BASE
+
+#include "rust-diagnostics.h"
+#include "rust-hir-type-check.h"
+#include "rust-name-resolver.h"
+#include "rust-hir-visitor.h"
+#include "rust-hir-map.h"
+#include "rust-backend.h"
+
+namespace Rust {
+namespace Resolver {
+
+class TraitReference;
+class TypeCheckBase
+{
+public:
+  virtual ~TypeCheckBase () {}
+
+  static TyTy::BaseType *coercion_site (HirId id, TyTy::BaseType *lhs,
+					TyTy::BaseType *rhs,
+					Location coercion_locus);
+
+  static TyTy::BaseType *cast_site (HirId id, TyTy::TyWithLocation from,
+				    TyTy::TyWithLocation to,
+				    Location cast_locus);
+
+protected:
+  TypeCheckBase ()
+    : mappings (Analysis::Mappings::get ()), resolver (Resolver::get ()),
+      context (TypeCheckContext::get ())
+  {}
+
+  TraitReference *resolve_trait_path (HIR::TypePath &);
+
+  TyTy::TypeBoundPredicate get_predicate_from_bound (HIR::TypePath &path);
+
+  bool check_for_unconstrained (
+    const std::vector<TyTy::SubstitutionParamMapping> &params_to_constrain,
+    const TyTy::SubstitutionArgumentMappings &constraint_a,
+    const TyTy::SubstitutionArgumentMappings &constraint_b,
+    const TyTy::BaseType *reference);
+
+  TyTy::BaseType *resolve_literal (const Analysis::NodeMapping &mappings,
+				   HIR::Literal &literal, Location locus);
+
+  TyTy::ADTType::ReprOptions parse_repr_options (const AST::AttrVec &attrs,
+						 Location locus);
+
+  void resolve_generic_params (
+    const std::vector<std::unique_ptr<HIR::GenericParam>> &generic_params,
+    std::vector<TyTy::SubstitutionParamMapping> &substitutions);
+
+  Analysis::Mappings *mappings;
+  Resolver *resolver;
+  TypeCheckContext *context;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_HIR_TYPE_CHECK_BASE
diff --git a/gcc/rust/typecheck/rust-hir-type-check-enumitem.cc b/gcc/rust/typecheck/rust-hir-type-check-enumitem.cc
new file mode 100644
index 00000000000..e65b2011d36
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-enumitem.cc
@@ -0,0 +1,213 @@
+// 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/>.
+
+#include "rust-hir-full.h"
+#include "rust-hir-type-check-type.h"
+#include "rust-hir-type-check-expr.h"
+#include "rust-hir-type-check-enumitem.h"
+
+namespace Rust {
+namespace Resolver {
+
+TyTy::VariantDef *
+TypeCheckEnumItem::Resolve (HIR::EnumItem *item, int64_t last_discriminant)
+{
+  TypeCheckEnumItem resolver (last_discriminant);
+  switch (item->get_enum_item_kind ())
+    {
+    case HIR::EnumItem::EnumItemKind::Named:
+      resolver.visit (static_cast<HIR::EnumItem &> (*item));
+      break;
+
+    case HIR::EnumItem::EnumItemKind::Tuple:
+      resolver.visit (static_cast<HIR::EnumItemTuple &> (*item));
+      break;
+
+    case HIR::EnumItem::EnumItemKind::Struct:
+      resolver.visit (static_cast<HIR::EnumItemStruct &> (*item));
+      break;
+
+    case HIR::EnumItem::EnumItemKind::Discriminant:
+      resolver.visit (static_cast<HIR::EnumItemDiscriminant &> (*item));
+      break;
+    }
+  return resolver.variant;
+}
+
+TypeCheckEnumItem::TypeCheckEnumItem (int64_t last_discriminant)
+  : TypeCheckBase (), variant (nullptr), last_discriminant (last_discriminant)
+{}
+
+void
+TypeCheckEnumItem::visit (HIR::EnumItem &item)
+{
+  if (last_discriminant == INT64_MAX)
+    rust_error_at (item.get_locus (), "discriminant too big");
+
+  Analysis::NodeMapping mapping (item.get_mappings ().get_crate_num (),
+				 item.get_mappings ().get_nodeid (),
+				 mappings->get_next_hir_id (
+				   item.get_mappings ().get_crate_num ()),
+				 item.get_mappings ().get_local_defid ());
+  HIR::LiteralExpr *discim_expr
+    = new HIR::LiteralExpr (mapping, std::to_string (last_discriminant),
+			    HIR::Literal::LitType::INT,
+			    PrimitiveCoreType::CORETYPE_I64, item.get_locus (),
+			    {});
+
+  TyTy::BaseType *isize = nullptr;
+  bool ok = context->lookup_builtin ("isize", &isize);
+  rust_assert (ok);
+  context->insert_type (mapping, isize);
+
+  const CanonicalPath *canonical_path = nullptr;
+  ok = mappings->lookup_canonical_path (item.get_mappings ().get_nodeid (),
+					&canonical_path);
+  rust_assert (ok);
+
+  RustIdent ident{*canonical_path, item.get_locus ()};
+  variant = new TyTy::VariantDef (item.get_mappings ().get_hirid (),
+				  item.get_identifier (), ident, discim_expr);
+}
+
+void
+TypeCheckEnumItem::visit (HIR::EnumItemDiscriminant &item)
+{
+  if (last_discriminant == INT64_MAX)
+    rust_error_at (item.get_locus (), "discriminant too big");
+
+  auto &discriminant = item.get_discriminant_expression ();
+  auto capacity_type = TypeCheckExpr::Resolve (discriminant.get ());
+  if (capacity_type->get_kind () == TyTy::TypeKind::ERROR)
+    return;
+
+  TyTy::ISizeType *expected_ty
+    = new TyTy::ISizeType (discriminant->get_mappings ().get_hirid ());
+  context->insert_type (discriminant->get_mappings (), expected_ty);
+
+  auto unified = expected_ty->unify (capacity_type);
+  if (unified->get_kind () == TyTy::TypeKind::ERROR)
+    return;
+
+  const CanonicalPath *canonical_path = nullptr;
+  bool ok = mappings->lookup_canonical_path (item.get_mappings ().get_nodeid (),
+					     &canonical_path);
+  rust_assert (ok);
+
+  RustIdent ident{*canonical_path, item.get_locus ()};
+  variant = new TyTy::VariantDef (item.get_mappings ().get_hirid (),
+				  item.get_identifier (), ident,
+				  item.get_discriminant_expression ().get ());
+}
+
+void
+TypeCheckEnumItem::visit (HIR::EnumItemTuple &item)
+{
+  if (last_discriminant == INT64_MAX)
+    rust_error_at (item.get_locus (), "discriminant too big");
+
+  std::vector<TyTy::StructFieldType *> fields;
+  size_t idx = 0;
+  for (auto &field : item.get_tuple_fields ())
+    {
+      TyTy::BaseType *field_type
+	= TypeCheckType::Resolve (field.get_field_type ().get ());
+      TyTy::StructFieldType *ty_field
+	= new TyTy::StructFieldType (field.get_mappings ().get_hirid (),
+				     std::to_string (idx), field_type);
+      fields.push_back (ty_field);
+      context->insert_type (field.get_mappings (), ty_field->get_field_type ());
+      idx++;
+    }
+
+  Analysis::NodeMapping mapping (item.get_mappings ().get_crate_num (),
+				 item.get_mappings ().get_nodeid (),
+				 mappings->get_next_hir_id (
+				   item.get_mappings ().get_crate_num ()),
+				 item.get_mappings ().get_local_defid ());
+  HIR::LiteralExpr *discim_expr
+    = new HIR::LiteralExpr (mapping, std::to_string (last_discriminant),
+			    HIR::Literal::LitType::INT,
+			    PrimitiveCoreType::CORETYPE_I64, item.get_locus (),
+			    {});
+
+  TyTy::BaseType *isize = nullptr;
+  bool ok = context->lookup_builtin ("isize", &isize);
+  rust_assert (ok);
+  context->insert_type (mapping, isize);
+
+  const CanonicalPath *canonical_path = nullptr;
+  ok = mappings->lookup_canonical_path (item.get_mappings ().get_nodeid (),
+					&canonical_path);
+  rust_assert (ok);
+
+  RustIdent ident{*canonical_path, item.get_locus ()};
+  variant = new TyTy::VariantDef (item.get_mappings ().get_hirid (),
+				  item.get_identifier (), ident,
+				  TyTy::VariantDef::VariantType::TUPLE,
+				  discim_expr, fields);
+}
+
+void
+TypeCheckEnumItem::visit (HIR::EnumItemStruct &item)
+{
+  if (last_discriminant == INT64_MAX)
+    rust_error_at (item.get_locus (), "discriminant too big");
+
+  std::vector<TyTy::StructFieldType *> fields;
+  for (auto &field : item.get_struct_fields ())
+    {
+      TyTy::BaseType *field_type
+	= TypeCheckType::Resolve (field.get_field_type ().get ());
+      TyTy::StructFieldType *ty_field
+	= new TyTy::StructFieldType (field.get_mappings ().get_hirid (),
+				     field.get_field_name (), field_type);
+      fields.push_back (ty_field);
+      context->insert_type (field.get_mappings (), ty_field->get_field_type ());
+    }
+
+  Analysis::NodeMapping mapping (item.get_mappings ().get_crate_num (),
+				 item.get_mappings ().get_nodeid (),
+				 mappings->get_next_hir_id (
+				   item.get_mappings ().get_crate_num ()),
+				 item.get_mappings ().get_local_defid ());
+  HIR::LiteralExpr *discrim_expr
+    = new HIR::LiteralExpr (mapping, std::to_string (last_discriminant),
+			    HIR::Literal::LitType::INT,
+			    PrimitiveCoreType::CORETYPE_I64, item.get_locus (),
+			    {});
+
+  TyTy::BaseType *isize = nullptr;
+  bool ok = context->lookup_builtin ("isize", &isize);
+  rust_assert (ok);
+  context->insert_type (mapping, isize);
+
+  const CanonicalPath *canonical_path = nullptr;
+  ok = mappings->lookup_canonical_path (item.get_mappings ().get_nodeid (),
+					&canonical_path);
+  rust_assert (ok);
+
+  RustIdent ident{*canonical_path, item.get_locus ()};
+  variant = new TyTy::VariantDef (item.get_mappings ().get_hirid (),
+				  item.get_identifier (), ident,
+				  TyTy::VariantDef::VariantType::STRUCT,
+				  discrim_expr, fields);
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-hir-type-check-enumitem.h b/gcc/rust/typecheck/rust-hir-type-check-enumitem.h
new file mode 100644
index 00000000000..c771ea3782d
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-enumitem.h
@@ -0,0 +1,50 @@
+// 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_HIR_TYPE_CHECK_ENUMITEM
+#define RUST_HIR_TYPE_CHECK_ENUMITEM
+
+#include "rust-hir-type-check-base.h"
+#include "rust-hir-full.h"
+
+namespace Rust {
+namespace Resolver {
+
+class TypeCheckEnumItem : public TypeCheckBase
+{
+public:
+  static TyTy::VariantDef *Resolve (HIR::EnumItem *item,
+				    int64_t last_discriminant);
+
+protected:
+  void visit (HIR::EnumItem &item);
+  void visit (HIR::EnumItemDiscriminant &item);
+  void visit (HIR::EnumItemTuple &item);
+  void visit (HIR::EnumItemStruct &item);
+
+private:
+  TypeCheckEnumItem (int64_t last_discriminant);
+
+  TyTy::VariantDef *variant;
+  int64_t last_discriminant;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_HIR_TYPE_CHECK_ENUMITEM
diff --git a/gcc/rust/typecheck/rust-hir-type-check-expr.cc b/gcc/rust/typecheck/rust-hir-type-check-expr.cc
new file mode 100644
index 00000000000..4371f5a59a5
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-expr.cc
@@ -0,0 +1,1567 @@
+// 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/>.
+
+#include "rust-hir-full.h"
+#include "rust-tyty-call.h"
+#include "rust-hir-type-check-struct-field.h"
+#include "rust-hir-path-probe.h"
+#include "rust-substitution-mapper.h"
+#include "rust-hir-trait-resolve.h"
+#include "rust-hir-type-bounds.h"
+#include "rust-hir-dot-operator.h"
+#include "rust-hir-type-check-pattern.h"
+#include "rust-hir-type-check-expr.h"
+#include "rust-hir-type-check-stmt.h"
+
+namespace Rust {
+namespace Resolver {
+
+TypeCheckExpr::TypeCheckExpr () : TypeCheckBase (), infered (nullptr) {}
+
+// Perform type checking on expr. Also runs type unification algorithm.
+// Returns the unified type of expr
+TyTy::BaseType *
+TypeCheckExpr::Resolve (HIR::Expr *expr)
+{
+  TypeCheckExpr resolver;
+  expr->accept_vis (resolver);
+
+  if (resolver.infered == nullptr)
+    {
+      // FIXME
+      // this is an internal error message for debugging and should be removed
+      // at some point
+      rust_error_at (expr->get_locus (), "failed to type resolve expression");
+      return new TyTy::ErrorType (expr->get_mappings ().get_hirid ());
+    }
+
+  auto ref = expr->get_mappings ().get_hirid ();
+  resolver.infered->set_ref (ref);
+  resolver.context->insert_type (expr->get_mappings (), resolver.infered);
+
+  return resolver.infered;
+}
+
+void
+TypeCheckExpr::visit (HIR::TupleIndexExpr &expr)
+{
+  auto resolved = TypeCheckExpr::Resolve (expr.get_tuple_expr ().get ());
+  if (resolved->get_kind () == TyTy::TypeKind::ERROR)
+    {
+      rust_error_at (expr.get_tuple_expr ()->get_locus (),
+		     "failed to resolve TupleIndexExpr receiver");
+      return;
+    }
+
+  // FIXME does this require autoderef here?
+  if (resolved->get_kind () == TyTy::TypeKind::REF)
+    {
+      TyTy::ReferenceType *r = static_cast<TyTy::ReferenceType *> (resolved);
+      resolved = r->get_base ();
+    }
+
+  bool is_valid_type = resolved->get_kind () == TyTy::TypeKind::ADT
+		       || resolved->get_kind () == TyTy::TypeKind::TUPLE;
+  if (!is_valid_type)
+    {
+      rust_error_at (expr.get_tuple_expr ()->get_locus (),
+		     "Expected Tuple or ADT got: %s",
+		     resolved->as_string ().c_str ());
+      return;
+    }
+
+  if (resolved->get_kind () == TyTy::TypeKind::TUPLE)
+    {
+      TyTy::TupleType *tuple = static_cast<TyTy::TupleType *> (resolved);
+      TupleIndex index = expr.get_tuple_index ();
+      if ((size_t) index >= tuple->num_fields ())
+	{
+	  rust_error_at (expr.get_locus (), "unknown field at index %i", index);
+	  return;
+	}
+
+      auto field_tyty = tuple->get_field ((size_t) index);
+      if (field_tyty == nullptr)
+	{
+	  rust_error_at (expr.get_locus (),
+			 "failed to lookup field type at index %i", index);
+	  return;
+	}
+
+      infered = field_tyty;
+      return;
+    }
+
+  TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (resolved);
+  rust_assert (!adt->is_enum ());
+  rust_assert (adt->number_of_variants () == 1);
+
+  TyTy::VariantDef *variant = adt->get_variants ().at (0);
+  TupleIndex index = expr.get_tuple_index ();
+  if ((size_t) index >= variant->num_fields ())
+    {
+      rust_error_at (expr.get_locus (), "unknown field at index %i", index);
+      return;
+    }
+
+  auto field_tyty = variant->get_field_at_index ((size_t) index);
+  if (field_tyty == nullptr)
+    {
+      rust_error_at (expr.get_locus (),
+		     "failed to lookup field type at index %i", index);
+      return;
+    }
+
+  infered = field_tyty->get_field_type ();
+}
+
+void
+TypeCheckExpr::visit (HIR::TupleExpr &expr)
+{
+  if (expr.is_unit ())
+    {
+      auto unit_node_id = resolver->get_unit_type_node_id ();
+      if (!context->lookup_builtin (unit_node_id, &infered))
+	{
+	  rust_error_at (expr.get_locus (),
+			 "failed to lookup builtin unit type");
+	}
+      return;
+    }
+
+  std::vector<TyTy::TyVar> fields;
+  for (auto &elem : expr.get_tuple_elems ())
+    {
+      auto field_ty = TypeCheckExpr::Resolve (elem.get ());
+      fields.push_back (TyTy::TyVar (field_ty->get_ref ()));
+    }
+  infered = new TyTy::TupleType (expr.get_mappings ().get_hirid (),
+				 expr.get_locus (), fields);
+}
+
+void
+TypeCheckExpr::visit (HIR::ReturnExpr &expr)
+{
+  auto fn_return_tyty = context->peek_return_type ();
+  rust_assert (fn_return_tyty != nullptr);
+
+  TyTy::BaseType *expr_ty
+    = expr.has_return_expr ()
+	? TypeCheckExpr::Resolve (expr.get_expr ())
+	: TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ());
+
+  infered = fn_return_tyty->unify (expr_ty);
+  fn_return_tyty->append_reference (expr_ty->get_ref ());
+  for (auto &ref : infered->get_combined_refs ())
+    fn_return_tyty->append_reference (ref);
+
+  infered = new TyTy::NeverType (expr.get_mappings ().get_hirid ());
+}
+
+void
+TypeCheckExpr::visit (HIR::CallExpr &expr)
+{
+  TyTy::BaseType *function_tyty = TypeCheckExpr::Resolve (expr.get_fnexpr ());
+
+  bool valid_tyty = function_tyty->get_kind () == TyTy::TypeKind::ADT
+		    || function_tyty->get_kind () == TyTy::TypeKind::FNDEF
+		    || function_tyty->get_kind () == TyTy::TypeKind::FNPTR;
+  if (!valid_tyty)
+    {
+      rust_error_at (expr.get_locus (),
+		     "Failed to resolve expression of function call");
+      return;
+    }
+
+  TyTy::VariantDef &variant = TyTy::VariantDef::get_error_node ();
+  if (function_tyty->get_kind () == TyTy::TypeKind::ADT)
+    {
+      TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (function_tyty);
+      if (adt->is_enum ())
+	{
+	  // lookup variant id
+	  HirId variant_id;
+	  bool ok = context->lookup_variant_definition (
+	    expr.get_fnexpr ()->get_mappings ().get_hirid (), &variant_id);
+	  rust_assert (ok);
+
+	  TyTy::VariantDef *lookup_variant = nullptr;
+	  ok = adt->lookup_variant_by_id (variant_id, &lookup_variant);
+	  rust_assert (ok);
+
+	  variant = *lookup_variant;
+	}
+      else
+	{
+	  rust_assert (adt->number_of_variants () == 1);
+	  variant = *adt->get_variants ().at (0);
+	}
+    }
+
+  infered = TyTy::TypeCheckCallExpr::go (function_tyty, expr, variant, context);
+}
+
+void
+TypeCheckExpr::visit (HIR::AssignmentExpr &expr)
+{
+  infered = TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ());
+
+  auto lhs = TypeCheckExpr::Resolve (expr.get_lhs ());
+  auto rhs = TypeCheckExpr::Resolve (expr.get_rhs ());
+
+  coercion_site (expr.get_mappings ().get_hirid (), lhs, rhs,
+		 expr.get_locus ());
+}
+
+void
+TypeCheckExpr::visit (HIR::CompoundAssignmentExpr &expr)
+{
+  infered = TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ());
+
+  auto lhs = TypeCheckExpr::Resolve (expr.get_left_expr ().get ());
+  auto rhs = TypeCheckExpr::Resolve (expr.get_right_expr ().get ());
+
+  // we dont care about the result of the unify from a compound assignment
+  // since this is a unit-type expr
+  auto result = lhs->unify (rhs);
+  if (result->get_kind () == TyTy::TypeKind::ERROR)
+    return;
+
+  auto lang_item_type
+    = Analysis::RustLangItem::CompoundAssignmentOperatorToLangItem (
+      expr.get_expr_type ());
+  bool operator_overloaded
+    = resolve_operator_overload (lang_item_type, expr, lhs, rhs);
+  if (operator_overloaded)
+    return;
+
+  bool valid_lhs = validate_arithmetic_type (lhs, expr.get_expr_type ());
+  bool valid_rhs = validate_arithmetic_type (rhs, expr.get_expr_type ());
+  bool valid = valid_lhs && valid_rhs;
+  if (!valid)
+    {
+      rust_error_at (expr.get_locus (),
+		     "cannot apply this operator to types %s and %s",
+		     lhs->as_string ().c_str (), rhs->as_string ().c_str ());
+      return;
+    }
+}
+
+void
+TypeCheckExpr::visit (HIR::LiteralExpr &expr)
+{
+  infered = resolve_literal (expr.get_mappings (), expr.get_literal (),
+			     expr.get_locus ());
+}
+
+void
+TypeCheckExpr::visit (HIR::ArithmeticOrLogicalExpr &expr)
+{
+  auto lhs = TypeCheckExpr::Resolve (expr.get_lhs ());
+  auto rhs = TypeCheckExpr::Resolve (expr.get_rhs ());
+
+  auto lang_item_type
+    = Analysis::RustLangItem::OperatorToLangItem (expr.get_expr_type ());
+  bool operator_overloaded
+    = resolve_operator_overload (lang_item_type, expr, lhs, rhs);
+  if (operator_overloaded)
+    return;
+
+  bool valid_lhs = validate_arithmetic_type (lhs, expr.get_expr_type ());
+  bool valid_rhs = validate_arithmetic_type (rhs, expr.get_expr_type ());
+  bool valid = valid_lhs && valid_rhs;
+  if (!valid)
+    {
+      rust_error_at (expr.get_locus (),
+		     "cannot apply this operator to types %s and %s",
+		     lhs->as_string ().c_str (), rhs->as_string ().c_str ());
+      return;
+    }
+
+  switch (expr.get_expr_type ())
+    {
+    case ArithmeticOrLogicalOperator::LEFT_SHIFT:
+      case ArithmeticOrLogicalOperator::RIGHT_SHIFT: {
+	TyTy::TyWithLocation from (rhs, expr.get_rhs ()->get_locus ());
+	TyTy::TyWithLocation to (lhs, expr.get_lhs ()->get_locus ());
+	infered = cast_site (expr.get_mappings ().get_hirid (), from, to,
+			     expr.get_locus ());
+      }
+      break;
+
+    default:
+      infered = lhs->unify (rhs);
+      break;
+    }
+}
+
+void
+TypeCheckExpr::visit (HIR::ComparisonExpr &expr)
+{
+  auto lhs = TypeCheckExpr::Resolve (expr.get_lhs ());
+  auto rhs = TypeCheckExpr::Resolve (expr.get_rhs ());
+
+  auto result = lhs->unify (rhs);
+  if (result == nullptr || result->get_kind () == TyTy::TypeKind::ERROR)
+    return;
+
+  bool ok = context->lookup_builtin ("bool", &infered);
+  rust_assert (ok);
+}
+
+void
+TypeCheckExpr::visit (HIR::LazyBooleanExpr &expr)
+{
+  auto lhs = TypeCheckExpr::Resolve (expr.get_lhs ());
+  auto rhs = TypeCheckExpr::Resolve (expr.get_rhs ());
+
+  // we expect the lhs and rhs must be bools at this point
+  TyTy::BoolType elhs (expr.get_mappings ().get_hirid ());
+  lhs = elhs.unify (lhs);
+  if (lhs->get_kind () == TyTy::TypeKind::ERROR)
+    return;
+
+  TyTy::BoolType rlhs (expr.get_mappings ().get_hirid ());
+  rhs = elhs.unify (rhs);
+  if (lhs->get_kind () == TyTy::TypeKind::ERROR)
+    return;
+
+  infered = lhs->unify (rhs);
+}
+
+void
+TypeCheckExpr::visit (HIR::NegationExpr &expr)
+{
+  auto negated_expr_ty = TypeCheckExpr::Resolve (expr.get_expr ().get ());
+
+  // check for operator overload
+  auto lang_item_type = Analysis::RustLangItem::NegationOperatorToLangItem (
+    expr.get_expr_type ());
+  bool operator_overloaded
+    = resolve_operator_overload (lang_item_type, expr, negated_expr_ty,
+				 nullptr);
+  if (operator_overloaded)
+    return;
+
+  // https://doc.rust-lang.org/reference/expressions/operator-expr.html#negation-operators
+  switch (expr.get_expr_type ())
+    {
+      case NegationOperator::NEGATE: {
+	bool valid
+	  = (negated_expr_ty->get_kind () == TyTy::TypeKind::INT)
+	    || (negated_expr_ty->get_kind () == TyTy::TypeKind::UINT)
+	    || (negated_expr_ty->get_kind () == TyTy::TypeKind::FLOAT)
+	    || (negated_expr_ty->get_kind () == TyTy::TypeKind::ISIZE)
+	    || (negated_expr_ty->get_kind () == TyTy::TypeKind::USIZE)
+	    || (negated_expr_ty->get_kind () == TyTy::TypeKind::INFER
+		&& (((TyTy::InferType *) negated_expr_ty)->get_infer_kind ()
+		    == TyTy::InferType::INTEGRAL))
+	    || (negated_expr_ty->get_kind () == TyTy::TypeKind::INFER
+		&& (((TyTy::InferType *) negated_expr_ty)->get_infer_kind ()
+		    == TyTy::InferType::FLOAT));
+	if (!valid)
+	  {
+	    rust_error_at (expr.get_locus (), "cannot apply unary - to %s",
+			   negated_expr_ty->as_string ().c_str ());
+	    return;
+	  }
+      }
+      break;
+
+      case NegationOperator::NOT: {
+	bool valid
+	  = (negated_expr_ty->get_kind () == TyTy::TypeKind::BOOL)
+	    || (negated_expr_ty->get_kind () == TyTy::TypeKind::INT)
+	    || (negated_expr_ty->get_kind () == TyTy::TypeKind::UINT)
+	    || (negated_expr_ty->get_kind () == TyTy::TypeKind::INFER
+		&& (((TyTy::InferType *) negated_expr_ty)->get_infer_kind ()
+		    == TyTy::InferType::INTEGRAL));
+	if (!valid)
+	  {
+	    rust_error_at (expr.get_locus (), "cannot apply unary %<!%> to %s",
+			   negated_expr_ty->as_string ().c_str ());
+	    return;
+	  }
+      }
+      break;
+    }
+
+  infered = negated_expr_ty->clone ();
+  infered->append_reference (negated_expr_ty->get_ref ());
+}
+
+void
+TypeCheckExpr::visit (HIR::IfExpr &expr)
+{
+  TypeCheckExpr::Resolve (expr.get_if_condition ());
+  TypeCheckExpr::Resolve (expr.get_if_block ());
+
+  infered = TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ());
+}
+
+void
+TypeCheckExpr::visit (HIR::IfExprConseqElse &expr)
+{
+  TypeCheckExpr::Resolve (expr.get_if_condition ());
+  auto if_blk_resolved = TypeCheckExpr::Resolve (expr.get_if_block ());
+  auto else_blk_resolved = TypeCheckExpr::Resolve (expr.get_else_block ());
+
+  if (if_blk_resolved->get_kind () == TyTy::NEVER)
+    infered = else_blk_resolved;
+  else if (else_blk_resolved->get_kind () == TyTy::NEVER)
+    infered = if_blk_resolved;
+  else
+    infered = if_blk_resolved->unify (else_blk_resolved);
+}
+
+void
+TypeCheckExpr::visit (HIR::IfExprConseqIf &expr)
+{
+  TypeCheckExpr::Resolve (expr.get_if_condition ());
+  auto if_blk_resolved = TypeCheckExpr::Resolve (expr.get_if_block ());
+  auto else_blk_resolved = TypeCheckExpr::Resolve (expr.get_conseq_if_expr ());
+
+  if (if_blk_resolved->get_kind () == TyTy::NEVER)
+    infered = else_blk_resolved;
+  else if (else_blk_resolved->get_kind () == TyTy::NEVER)
+    infered = if_blk_resolved;
+  else
+    infered = if_blk_resolved->unify (else_blk_resolved);
+}
+
+void
+TypeCheckExpr::visit (HIR::IfLetExpr &expr)
+{
+  // this needs to perform a least upper bound coercion on the blocks and then
+  // unify the scruintee and arms
+  TyTy::BaseType *scrutinee_tyty
+    = TypeCheckExpr::Resolve (expr.get_scrutinee_expr ().get ());
+
+  for (auto &pattern : expr.get_patterns ())
+    {
+      TyTy::BaseType *kase_arm_ty
+	= TypeCheckPattern::Resolve (pattern.get (), scrutinee_tyty);
+
+      TyTy::BaseType *checked_kase = scrutinee_tyty->unify (kase_arm_ty);
+      if (checked_kase->get_kind () == TyTy::TypeKind::ERROR)
+	return;
+    }
+
+  TypeCheckExpr::Resolve (expr.get_if_block ());
+
+  infered = TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ());
+}
+
+void
+TypeCheckExpr::visit (HIR::UnsafeBlockExpr &expr)
+{
+  infered = TypeCheckExpr::Resolve (expr.get_block_expr ().get ());
+}
+
+void
+TypeCheckExpr::visit (HIR::BlockExpr &expr)
+{
+  for (auto &s : expr.get_statements ())
+    {
+      if (!s->is_item ())
+	continue;
+
+      TypeCheckStmt::Resolve (s.get ());
+    }
+
+  for (auto &s : expr.get_statements ())
+    {
+      if (s->is_item ())
+	continue;
+
+      auto resolved = TypeCheckStmt::Resolve (s.get ());
+      if (resolved == nullptr)
+	{
+	  rust_error_at (s->get_locus (), "failure to resolve type");
+	  return;
+	}
+
+      if (s->is_unit_check_needed () && !resolved->is_unit ())
+	{
+	  auto unit
+	    = TyTy::TupleType::get_unit_type (s->get_mappings ().get_hirid ());
+	  resolved = unit->unify (resolved);
+	}
+    }
+
+  if (expr.has_expr ())
+    infered = TypeCheckExpr::Resolve (expr.get_final_expr ().get ())->clone ();
+  else if (expr.is_tail_reachable ())
+    infered
+      = TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ());
+  else
+    infered = new TyTy::NeverType (expr.get_mappings ().get_hirid ());
+}
+
+void
+TypeCheckExpr::visit (HIR::RangeFromToExpr &expr)
+{
+  auto lang_item_type = Analysis::RustLangItem::ItemType::RANGE;
+
+  DefId respective_lang_item_id = UNKNOWN_DEFID;
+  bool lang_item_defined
+    = mappings->lookup_lang_item (lang_item_type, &respective_lang_item_id);
+
+  // we need to have it maybe
+  if (!lang_item_defined)
+    {
+      rust_internal_error_at (
+	expr.get_locus (), "unable to find relevant lang item: %s",
+	Analysis::RustLangItem::ToString (lang_item_type).c_str ());
+      return;
+    }
+
+  // look it up and it _must_ be a struct definition
+  HIR::Item *item = mappings->lookup_defid (respective_lang_item_id);
+  rust_assert (item != nullptr);
+
+  TyTy::BaseType *item_type = nullptr;
+  bool ok
+    = context->lookup_type (item->get_mappings ().get_hirid (), &item_type);
+  rust_assert (ok);
+  rust_assert (item_type->get_kind () == TyTy::TypeKind::ADT);
+  TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (item_type);
+
+  // this is a single generic item lets assert that
+  rust_assert (adt->get_num_substitutions () == 1);
+
+  // resolve the range expressions and these types must unify then we use that
+  // type to substitute into the ADT
+  TyTy::BaseType *from_ty
+    = TypeCheckExpr::Resolve (expr.get_from_expr ().get ());
+  TyTy::BaseType *to_ty = TypeCheckExpr::Resolve (expr.get_to_expr ().get ());
+  TyTy::BaseType *unified = from_ty->unify (to_ty);
+
+  // substitute it in
+  std::vector<TyTy::SubstitutionArg> subst_mappings;
+  const TyTy::SubstitutionParamMapping *param_ref = &adt->get_substs ().at (0);
+  subst_mappings.push_back (TyTy::SubstitutionArg (param_ref, unified));
+
+  TyTy::SubstitutionArgumentMappings subst (subst_mappings, expr.get_locus ());
+  infered = SubstMapperInternal::Resolve (adt, subst);
+}
+
+void
+TypeCheckExpr::visit (HIR::RangeFromExpr &expr)
+{
+  auto lang_item_type = Analysis::RustLangItem::ItemType::RANGE_FROM;
+
+  DefId respective_lang_item_id = UNKNOWN_DEFID;
+  bool lang_item_defined
+    = mappings->lookup_lang_item (lang_item_type, &respective_lang_item_id);
+
+  // we need to have it maybe
+  if (!lang_item_defined)
+    {
+      rust_internal_error_at (
+	expr.get_locus (), "unable to find relevant lang item: %s",
+	Analysis::RustLangItem::ToString (lang_item_type).c_str ());
+      return;
+    }
+
+  // look it up and it _must_ be a struct definition
+  HIR::Item *item = mappings->lookup_defid (respective_lang_item_id);
+  rust_assert (item != nullptr);
+
+  TyTy::BaseType *item_type = nullptr;
+  bool ok
+    = context->lookup_type (item->get_mappings ().get_hirid (), &item_type);
+  rust_assert (ok);
+  rust_assert (item_type->get_kind () == TyTy::TypeKind::ADT);
+  TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (item_type);
+
+  // this is a single generic item lets assert that
+  rust_assert (adt->get_num_substitutions () == 1);
+
+  // resolve the range expressions and these types must unify then we use that
+  // type to substitute into the ADT
+  TyTy::BaseType *from_ty
+    = TypeCheckExpr::Resolve (expr.get_from_expr ().get ());
+
+  // substitute it in
+  std::vector<TyTy::SubstitutionArg> subst_mappings;
+  const TyTy::SubstitutionParamMapping *param_ref = &adt->get_substs ().at (0);
+  subst_mappings.push_back (TyTy::SubstitutionArg (param_ref, from_ty));
+
+  TyTy::SubstitutionArgumentMappings subst (subst_mappings, expr.get_locus ());
+  infered = SubstMapperInternal::Resolve (adt, subst);
+}
+
+void
+TypeCheckExpr::visit (HIR::RangeToExpr &expr)
+{
+  auto lang_item_type = Analysis::RustLangItem::ItemType::RANGE_TO;
+
+  DefId respective_lang_item_id = UNKNOWN_DEFID;
+  bool lang_item_defined
+    = mappings->lookup_lang_item (lang_item_type, &respective_lang_item_id);
+
+  // we need to have it maybe
+  if (!lang_item_defined)
+    {
+      rust_internal_error_at (
+	expr.get_locus (), "unable to find relevant lang item: %s",
+	Analysis::RustLangItem::ToString (lang_item_type).c_str ());
+      return;
+    }
+
+  // look it up and it _must_ be a struct definition
+  HIR::Item *item = mappings->lookup_defid (respective_lang_item_id);
+  rust_assert (item != nullptr);
+
+  TyTy::BaseType *item_type = nullptr;
+  bool ok
+    = context->lookup_type (item->get_mappings ().get_hirid (), &item_type);
+  rust_assert (ok);
+  rust_assert (item_type->get_kind () == TyTy::TypeKind::ADT);
+  TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (item_type);
+
+  // this is a single generic item lets assert that
+  rust_assert (adt->get_num_substitutions () == 1);
+
+  // resolve the range expressions and these types must unify then we use that
+  // type to substitute into the ADT
+  TyTy::BaseType *from_ty = TypeCheckExpr::Resolve (expr.get_to_expr ().get ());
+
+  // substitute it in
+  std::vector<TyTy::SubstitutionArg> subst_mappings;
+  const TyTy::SubstitutionParamMapping *param_ref = &adt->get_substs ().at (0);
+  subst_mappings.push_back (TyTy::SubstitutionArg (param_ref, from_ty));
+
+  TyTy::SubstitutionArgumentMappings subst (subst_mappings, expr.get_locus ());
+  infered = SubstMapperInternal::Resolve (adt, subst);
+}
+
+void
+TypeCheckExpr::visit (HIR::RangeFullExpr &expr)
+{
+  auto lang_item_type = Analysis::RustLangItem::ItemType::RANGE_FULL;
+
+  DefId respective_lang_item_id = UNKNOWN_DEFID;
+  bool lang_item_defined
+    = mappings->lookup_lang_item (lang_item_type, &respective_lang_item_id);
+
+  // we need to have it maybe
+  if (!lang_item_defined)
+    {
+      rust_internal_error_at (
+	expr.get_locus (), "unable to find relevant lang item: %s",
+	Analysis::RustLangItem::ToString (lang_item_type).c_str ());
+      return;
+    }
+
+  // look it up and it _must_ be a struct definition
+  HIR::Item *item = mappings->lookup_defid (respective_lang_item_id);
+  rust_assert (item != nullptr);
+
+  TyTy::BaseType *item_type = nullptr;
+  bool ok
+    = context->lookup_type (item->get_mappings ().get_hirid (), &item_type);
+  rust_assert (ok);
+  rust_assert (item_type->is_unit ());
+
+  infered = item_type;
+}
+
+void
+TypeCheckExpr::visit (HIR::RangeFromToInclExpr &expr)
+{
+  auto lang_item_type = Analysis::RustLangItem::ItemType::RANGE_INCLUSIVE;
+
+  DefId respective_lang_item_id = UNKNOWN_DEFID;
+  bool lang_item_defined
+    = mappings->lookup_lang_item (lang_item_type, &respective_lang_item_id);
+
+  // we need to have it maybe
+  if (!lang_item_defined)
+    {
+      rust_internal_error_at (
+	expr.get_locus (), "unable to find relevant lang item: %s",
+	Analysis::RustLangItem::ToString (lang_item_type).c_str ());
+      return;
+    }
+
+  // look it up and it _must_ be a struct definition
+  HIR::Item *item = mappings->lookup_defid (respective_lang_item_id);
+  rust_assert (item != nullptr);
+
+  TyTy::BaseType *item_type = nullptr;
+  bool ok
+    = context->lookup_type (item->get_mappings ().get_hirid (), &item_type);
+  rust_assert (ok);
+  rust_assert (item_type->get_kind () == TyTy::TypeKind::ADT);
+  TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (item_type);
+
+  // this is a single generic item lets assert that
+  rust_assert (adt->get_num_substitutions () == 1);
+
+  // resolve the range expressions and these types must unify then we use that
+  // type to substitute into the ADT
+  TyTy::BaseType *from_ty
+    = TypeCheckExpr::Resolve (expr.get_from_expr ().get ());
+  TyTy::BaseType *to_ty = TypeCheckExpr::Resolve (expr.get_to_expr ().get ());
+  TyTy::BaseType *unified = from_ty->unify (to_ty);
+
+  // substitute it in
+  std::vector<TyTy::SubstitutionArg> subst_mappings;
+  const TyTy::SubstitutionParamMapping *param_ref = &adt->get_substs ().at (0);
+  subst_mappings.push_back (TyTy::SubstitutionArg (param_ref, unified));
+
+  TyTy::SubstitutionArgumentMappings subst (subst_mappings, expr.get_locus ());
+  infered = SubstMapperInternal::Resolve (adt, subst);
+}
+
+void
+TypeCheckExpr::visit (HIR::ArrayIndexExpr &expr)
+{
+  auto array_expr_ty = TypeCheckExpr::Resolve (expr.get_array_expr ());
+  if (array_expr_ty->get_kind () == TyTy::TypeKind::ERROR)
+    return;
+
+  auto index_expr_ty = TypeCheckExpr::Resolve (expr.get_index_expr ());
+  if (index_expr_ty->get_kind () == TyTy::TypeKind::ERROR)
+    return;
+
+  // first attempt to use direct array index logic
+  auto direct_array_expr_ty = array_expr_ty;
+  if (direct_array_expr_ty->get_kind () == TyTy::TypeKind::REF)
+    {
+      // lets try and deref it since rust allows this
+      auto ref = static_cast<TyTy::ReferenceType *> (direct_array_expr_ty);
+      auto base = ref->get_base ();
+      if (base->get_kind () == TyTy::TypeKind::ARRAY)
+	direct_array_expr_ty = base;
+    }
+
+  TyTy::BaseType *size_ty;
+  bool ok = context->lookup_builtin ("usize", &size_ty);
+  rust_assert (ok);
+
+  bool maybe_simple_array_access = index_expr_ty->can_eq (size_ty, false);
+  if (maybe_simple_array_access
+      && direct_array_expr_ty->get_kind () == TyTy::TypeKind::ARRAY)
+    {
+      auto resolved_index_expr = size_ty->unify (index_expr_ty);
+      if (resolved_index_expr->get_kind () == TyTy::TypeKind::ERROR)
+	return;
+
+      TyTy::ArrayType *array_type
+	= static_cast<TyTy::ArrayType *> (direct_array_expr_ty);
+      infered = array_type->get_element_type ()->clone ();
+      return;
+    }
+
+  // is this a case of core::ops::index?
+  auto lang_item_type = Analysis::RustLangItem::ItemType::INDEX;
+  bool operator_overloaded
+    = resolve_operator_overload (lang_item_type, expr, array_expr_ty,
+				 index_expr_ty);
+  if (operator_overloaded)
+    {
+      // index and index mut always return a reference to the element
+      TyTy::BaseType *resolved = infered;
+      rust_assert (resolved->get_kind () == TyTy::TypeKind::REF);
+      TyTy::ReferenceType *ref = static_cast<TyTy::ReferenceType *> (resolved);
+
+      infered = ref->get_base ()->clone ();
+      return;
+    }
+
+  // error[E0277]: the type `[{integer}]` cannot be indexed by `u32`
+  RichLocation r (expr.get_locus ());
+  r.add_range (expr.get_array_expr ()->get_locus ());
+  r.add_range (expr.get_index_expr ()->get_locus ());
+  rust_error_at (r, "the type %<%s%> cannot be indexed by %<%s%>",
+		 array_expr_ty->get_name ().c_str (),
+		 index_expr_ty->get_name ().c_str ());
+}
+
+void
+TypeCheckExpr::visit (HIR::ArrayExpr &expr)
+{
+  HIR::ArrayElems &elements = *expr.get_internal_elements ();
+
+  HIR::Expr *capacity_expr = nullptr;
+  TyTy::BaseType *element_type = nullptr;
+  switch (elements.get_array_expr_type ())
+    {
+      case HIR::ArrayElems::ArrayExprType::COPIED: {
+	HIR::ArrayElemsCopied &elems
+	  = static_cast<HIR::ArrayElemsCopied &> (elements);
+	element_type = TypeCheckExpr::Resolve (elems.get_elem_to_copy ());
+
+	auto capacity_type
+	  = TypeCheckExpr::Resolve (elems.get_num_copies_expr ());
+
+	TyTy::BaseType *expected_ty = nullptr;
+	bool ok = context->lookup_builtin ("usize", &expected_ty);
+	rust_assert (ok);
+	context->insert_type (elems.get_num_copies_expr ()->get_mappings (),
+			      expected_ty);
+
+	auto unified = expected_ty->unify (capacity_type);
+	if (unified->get_kind () == TyTy::TypeKind::ERROR)
+	  return;
+
+	capacity_expr = elems.get_num_copies_expr ();
+      }
+      break;
+
+      case HIR::ArrayElems::ArrayExprType::VALUES: {
+	HIR::ArrayElemsValues &elems
+	  = static_cast<HIR::ArrayElemsValues &> (elements);
+
+	std::vector<TyTy::BaseType *> types;
+	for (auto &elem : elems.get_values ())
+	  {
+	    types.push_back (TypeCheckExpr::Resolve (elem.get ()));
+	  }
+
+	element_type
+	  = TyTy::TyVar::get_implicit_infer_var (expr.get_locus ()).get_tyty ();
+	for (auto &type : types)
+	  {
+	    element_type = element_type->unify (type);
+	  }
+
+	auto crate_num = mappings->get_current_crate ();
+	Analysis::NodeMapping mapping (crate_num, UNKNOWN_NODEID,
+				       mappings->get_next_hir_id (crate_num),
+				       UNKNOWN_LOCAL_DEFID);
+	std::string capacity_str = std::to_string (elems.get_num_elements ());
+	capacity_expr = new HIR::LiteralExpr (mapping, capacity_str,
+					      HIR::Literal::LitType::INT,
+					      PrimitiveCoreType::CORETYPE_USIZE,
+					      Location (), {});
+
+	// mark the type for this implicit node
+	TyTy::BaseType *expected_ty = nullptr;
+	bool ok = context->lookup_builtin ("usize", &expected_ty);
+	rust_assert (ok);
+	context->insert_type (mapping, expected_ty);
+      }
+      break;
+    }
+
+  infered = new TyTy::ArrayType (expr.get_mappings ().get_hirid (),
+				 expr.get_locus (), *capacity_expr,
+				 TyTy::TyVar (element_type->get_ref ()));
+}
+
+// empty struct
+void
+TypeCheckExpr::visit (HIR::StructExprStruct &struct_expr)
+{
+  TyTy::BaseType *struct_path_ty
+    = TypeCheckExpr::Resolve (&struct_expr.get_struct_name ());
+  if (struct_path_ty->get_kind () != TyTy::TypeKind::ADT)
+    {
+      rust_error_at (struct_expr.get_struct_name ().get_locus (),
+		     "expected an ADT type for constructor");
+      return;
+    }
+
+  infered = struct_path_ty;
+}
+
+void
+TypeCheckExpr::visit (HIR::StructExprStructFields &struct_expr)
+{
+  infered = TypeCheckStructExpr::Resolve (&struct_expr);
+}
+
+void
+TypeCheckExpr::visit (HIR::GroupedExpr &expr)
+{
+  infered = TypeCheckExpr::Resolve (expr.get_expr_in_parens ().get ());
+}
+
+void
+TypeCheckExpr::visit (HIR::FieldAccessExpr &expr)
+{
+  auto struct_base = TypeCheckExpr::Resolve (expr.get_receiver_expr ().get ());
+
+  // FIXME does this require autoderef here?
+  if (struct_base->get_kind () == TyTy::TypeKind::REF)
+    {
+      TyTy::ReferenceType *r = static_cast<TyTy::ReferenceType *> (struct_base);
+      struct_base = r->get_base ();
+    }
+
+  bool is_valid_type = struct_base->get_kind () == TyTy::TypeKind::ADT;
+  if (!is_valid_type)
+    {
+      rust_error_at (expr.get_locus (),
+		     "expected algebraic data type got: [%s]",
+		     struct_base->as_string ().c_str ());
+      return;
+    }
+
+  TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (struct_base);
+  rust_assert (!adt->is_enum ());
+  rust_assert (adt->number_of_variants () == 1);
+
+  TyTy::VariantDef *vaiant = adt->get_variants ().at (0);
+
+  TyTy::StructFieldType *lookup = nullptr;
+  bool found = vaiant->lookup_field (expr.get_field_name (), &lookup, nullptr);
+  if (!found)
+    {
+      rust_error_at (expr.get_locus (), "unknown field [%s] for type [%s]",
+		     expr.get_field_name ().c_str (),
+		     adt->as_string ().c_str ());
+      return;
+    }
+
+  infered = lookup->get_field_type ();
+}
+
+void
+TypeCheckExpr::visit (HIR::MethodCallExpr &expr)
+{
+  auto receiver_tyty = TypeCheckExpr::Resolve (expr.get_receiver ().get ());
+  if (receiver_tyty->get_kind () == TyTy::TypeKind::ERROR)
+    {
+      rust_error_at (expr.get_receiver ()->get_locus (),
+		     "failed to resolve receiver in MethodCallExpr");
+      return;
+    }
+
+  context->insert_receiver (expr.get_mappings ().get_hirid (), receiver_tyty);
+
+  auto candidate
+    = MethodResolver::Probe (receiver_tyty,
+			     expr.get_method_name ().get_segment ());
+  if (candidate.is_error ())
+    {
+      rust_error_at (
+	expr.get_method_name ().get_locus (),
+	"failed to resolve method for %<%s%>",
+	expr.get_method_name ().get_segment ().as_string ().c_str ());
+      return;
+    }
+
+  // Get the adjusted self
+  Adjuster adj (receiver_tyty);
+  TyTy::BaseType *adjusted_self = adj.adjust_type (candidate.adjustments);
+
+  // store the adjustments for code-generation to know what to do which must be
+  // stored onto the receiver to so as we don't trigger duplicate deref mappings
+  // ICE when an argument is a method call
+  HirId autoderef_mappings_id
+    = expr.get_receiver ()->get_mappings ().get_hirid ();
+  context->insert_autoderef_mappings (autoderef_mappings_id,
+				      std::move (candidate.adjustments));
+
+  PathProbeCandidate &resolved_candidate = candidate.candidate;
+  TyTy::BaseType *lookup_tyty = candidate.candidate.ty;
+  NodeId resolved_node_id
+    = resolved_candidate.is_impl_candidate ()
+	? resolved_candidate.item.impl.impl_item->get_impl_mappings ()
+	    .get_nodeid ()
+	: resolved_candidate.item.trait.item_ref->get_mappings ().get_nodeid ();
+
+  if (lookup_tyty->get_kind () != TyTy::TypeKind::FNDEF)
+    {
+      RichLocation r (expr.get_method_name ().get_locus ());
+      r.add_range (resolved_candidate.locus);
+      rust_error_at (r, "associated impl item is not a method");
+      return;
+    }
+
+  TyTy::BaseType *lookup = lookup_tyty;
+  TyTy::FnType *fn = static_cast<TyTy::FnType *> (lookup);
+  if (!fn->is_method ())
+    {
+      RichLocation r (expr.get_method_name ().get_locus ());
+      r.add_range (resolved_candidate.locus);
+      rust_error_at (r, "associated function is not a method");
+      return;
+    }
+
+  auto root = receiver_tyty->get_root ();
+  if (root->get_kind () == TyTy::TypeKind::ADT)
+    {
+      const TyTy::ADTType *adt = static_cast<const TyTy::ADTType *> (root);
+      if (adt->has_substitutions () && fn->needs_substitution ())
+	{
+	  // consider the case where we have:
+	  //
+	  // struct Foo<X,Y>(X,Y);
+	  //
+	  // impl<T> Foo<T, i32> {
+	  //   fn test<X>(self, a:X) -> (T,X) { (self.0, a) }
+	  // }
+	  //
+	  // In this case we end up with an fn type of:
+	  //
+	  // fn <T,X> test(self:Foo<T,i32>, a:X) -> (T,X)
+	  //
+	  // This means the instance or self we are calling this method for
+	  // will be substituted such that we can get the inherited type
+	  // arguments but then need to use the turbo fish if available or
+	  // infer the remaining arguments. Luckily rust does not allow for
+	  // default types GenericParams on impl blocks since these must
+	  // always be at the end of the list
+
+	  auto s = fn->get_self_type ()->get_root ();
+	  rust_assert (s->can_eq (adt, false));
+	  rust_assert (s->get_kind () == TyTy::TypeKind::ADT);
+	  const TyTy::ADTType *self_adt
+	    = static_cast<const TyTy::ADTType *> (s);
+
+	  // we need to grab the Self substitutions as the inherit type
+	  // parameters for this
+	  if (self_adt->needs_substitution ())
+	    {
+	      rust_assert (adt->was_substituted ());
+
+	      TyTy::SubstitutionArgumentMappings used_args_in_prev_segment
+		= GetUsedSubstArgs::From (adt);
+
+	      TyTy::SubstitutionArgumentMappings inherit_type_args
+		= self_adt->solve_mappings_from_receiver_for_self (
+		  used_args_in_prev_segment);
+
+	      // there may or may not be inherited type arguments
+	      if (!inherit_type_args.is_error ())
+		{
+		  // need to apply the inherited type arguments to the
+		  // function
+		  lookup = fn->handle_substitions (inherit_type_args);
+		}
+	    }
+	}
+    }
+
+  // apply any remaining generic arguments
+  if (expr.get_method_name ().has_generic_args ())
+    {
+      HIR::GenericArgs &args = expr.get_method_name ().get_generic_args ();
+      lookup
+	= SubstMapper::Resolve (lookup, expr.get_method_name ().get_locus (),
+				&args);
+      if (lookup->get_kind () == TyTy::TypeKind::ERROR)
+	return;
+    }
+  else if (lookup->needs_generic_substitutions ())
+    {
+      lookup = SubstMapper::InferSubst (lookup,
+					expr.get_method_name ().get_locus ());
+    }
+
+  TyTy::BaseType *function_ret_tyty
+    = TyTy::TypeCheckMethodCallExpr::go (lookup, expr, adjusted_self, context);
+  if (function_ret_tyty == nullptr
+      || function_ret_tyty->get_kind () == TyTy::TypeKind::ERROR)
+    {
+      rust_error_at (expr.get_locus (),
+		     "failed to lookup type to MethodCallExpr");
+      return;
+    }
+
+  // store the expected fntype
+  context->insert_type (expr.get_method_name ().get_mappings (), lookup);
+
+  // set up the resolved name on the path
+  resolver->insert_resolved_name (expr.get_mappings ().get_nodeid (),
+				  resolved_node_id);
+
+  // return the result of the function back
+  infered = function_ret_tyty;
+}
+
+void
+TypeCheckExpr::visit (HIR::LoopExpr &expr)
+{
+  context->push_new_loop_context (expr.get_mappings ().get_hirid (),
+				  expr.get_locus ());
+  TyTy::BaseType *block_expr
+    = TypeCheckExpr::Resolve (expr.get_loop_block ().get ());
+  if (!block_expr->is_unit ())
+    {
+      rust_error_at (expr.get_loop_block ()->get_locus (),
+		     "expected %<()%> got %s",
+		     block_expr->as_string ().c_str ());
+      return;
+    }
+
+  TyTy::BaseType *loop_context_type = context->pop_loop_context ();
+
+  bool loop_context_type_infered
+    = (loop_context_type->get_kind () != TyTy::TypeKind::INFER)
+      || ((loop_context_type->get_kind () == TyTy::TypeKind::INFER)
+	  && (((TyTy::InferType *) loop_context_type)->get_infer_kind ()
+	      != TyTy::InferType::GENERAL));
+
+  infered
+    = loop_context_type_infered
+	? loop_context_type
+	: TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ());
+}
+
+void
+TypeCheckExpr::visit (HIR::WhileLoopExpr &expr)
+{
+  context->push_new_while_loop_context (expr.get_mappings ().get_hirid ());
+
+  TypeCheckExpr::Resolve (expr.get_predicate_expr ().get ());
+  TyTy::BaseType *block_expr
+    = TypeCheckExpr::Resolve (expr.get_loop_block ().get ());
+
+  if (!block_expr->is_unit ())
+    {
+      rust_error_at (expr.get_loop_block ()->get_locus (),
+		     "expected %<()%> got %s",
+		     block_expr->as_string ().c_str ());
+      return;
+    }
+
+  context->pop_loop_context ();
+  infered = TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ());
+}
+
+void
+TypeCheckExpr::visit (HIR::BreakExpr &expr)
+{
+  if (!context->have_loop_context ())
+    {
+      rust_error_at (expr.get_locus (), "cannot %<break%> outside of a loop");
+      return;
+    }
+
+  if (expr.has_break_expr ())
+    {
+      TyTy::BaseType *break_expr_tyty
+	= TypeCheckExpr::Resolve (expr.get_expr ().get ());
+
+      TyTy::BaseType *loop_context = context->peek_loop_context ();
+      if (loop_context->get_kind () == TyTy::TypeKind::ERROR)
+	{
+	  rust_error_at (expr.get_locus (),
+			 "can only break with a value inside %<loop%>");
+	  return;
+	}
+
+      TyTy::BaseType *unified_ty = loop_context->unify (break_expr_tyty);
+      context->swap_head_loop_context (unified_ty);
+    }
+
+  infered = new TyTy::NeverType (expr.get_mappings ().get_hirid ());
+}
+
+void
+TypeCheckExpr::visit (HIR::ContinueExpr &expr)
+{
+  if (!context->have_loop_context ())
+    {
+      rust_error_at (expr.get_locus (),
+		     "cannot %<continue%> outside of a loop");
+      return;
+    }
+
+  infered = new TyTy::NeverType (expr.get_mappings ().get_hirid ());
+}
+
+void
+TypeCheckExpr::visit (HIR::BorrowExpr &expr)
+{
+  TyTy::BaseType *resolved_base
+    = TypeCheckExpr::Resolve (expr.get_expr ().get ());
+
+  // In Rust this is valid because of DST's
+  //
+  // fn test() {
+  //     let a:&str = "TEST 1";
+  //     let b:&str = &"TEST 2";
+  // }
+  if (resolved_base->get_kind () == TyTy::TypeKind::REF)
+    {
+      const TyTy::ReferenceType *ref
+	= static_cast<const TyTy::ReferenceType *> (resolved_base);
+
+      // this might end up being a more generic is_dyn object check but lets
+      // double check dyn traits type-layout first
+      if (ref->is_dyn_str_type ())
+	{
+	  infered = resolved_base;
+	  return;
+	}
+    }
+
+  if (expr.get_is_double_borrow ())
+    {
+      // FIXME double_reference
+      gcc_unreachable ();
+    }
+
+  infered = new TyTy::ReferenceType (expr.get_mappings ().get_hirid (),
+				     TyTy::TyVar (resolved_base->get_ref ()),
+				     expr.get_mut ());
+}
+
+void
+TypeCheckExpr::visit (HIR::DereferenceExpr &expr)
+{
+  TyTy::BaseType *resolved_base
+    = TypeCheckExpr::Resolve (expr.get_expr ().get ());
+
+  auto lang_item_type = Analysis::RustLangItem::ItemType::DEREF;
+  bool operator_overloaded
+    = resolve_operator_overload (lang_item_type, expr, resolved_base, nullptr);
+  if (operator_overloaded)
+    {
+      // operator overloaded deref always refurns a reference type lets assert
+      // this
+      rust_assert (infered->get_kind () == TyTy::TypeKind::REF);
+      resolved_base = infered;
+    }
+
+  bool is_valid_type = resolved_base->get_kind () == TyTy::TypeKind::REF
+		       || resolved_base->get_kind () == TyTy::TypeKind::POINTER;
+  if (!is_valid_type)
+    {
+      rust_error_at (expr.get_locus (), "expected reference type got %s",
+		     resolved_base->as_string ().c_str ());
+      return;
+    }
+
+  if (resolved_base->get_kind () == TyTy::TypeKind::REF)
+    {
+      TyTy::ReferenceType *ref_base
+	= static_cast<TyTy::ReferenceType *> (resolved_base);
+      infered = ref_base->get_base ()->clone ();
+    }
+  else
+    {
+      TyTy::PointerType *ref_base
+	= static_cast<TyTy::PointerType *> (resolved_base);
+      infered = ref_base->get_base ()->clone ();
+    }
+}
+
+void
+TypeCheckExpr::visit (HIR::TypeCastExpr &expr)
+{
+  TyTy::BaseType *expr_to_convert
+    = TypeCheckExpr::Resolve (expr.get_casted_expr ().get ());
+  TyTy::BaseType *tyty_to_convert_to
+    = TypeCheckType::Resolve (expr.get_type_to_convert_to ().get ());
+
+  TyTy::TyWithLocation from (expr_to_convert,
+			     expr.get_casted_expr ()->get_locus ());
+  TyTy::TyWithLocation to (tyty_to_convert_to,
+			   expr.get_type_to_convert_to ()->get_locus ());
+  infered = cast_site (expr.get_mappings ().get_hirid (), from, to,
+		       expr.get_locus ());
+}
+
+void
+TypeCheckExpr::visit (HIR::MatchExpr &expr)
+{
+  // this needs to perform a least upper bound coercion on the blocks and then
+  // unify the scruintee and arms
+  TyTy::BaseType *scrutinee_tyty
+    = TypeCheckExpr::Resolve (expr.get_scrutinee_expr ().get ());
+
+  std::vector<TyTy::BaseType *> kase_block_tys;
+  for (auto &kase : expr.get_match_cases ())
+    {
+      // lets check the arms
+      HIR::MatchArm &kase_arm = kase.get_arm ();
+      for (auto &pattern : kase_arm.get_patterns ())
+	{
+	  TyTy::BaseType *kase_arm_ty
+	    = TypeCheckPattern::Resolve (pattern.get (), scrutinee_tyty);
+
+	  TyTy::BaseType *checked_kase = scrutinee_tyty->unify (kase_arm_ty);
+	  if (checked_kase->get_kind () == TyTy::TypeKind::ERROR)
+	    return;
+	}
+
+      // check the kase type
+      TyTy::BaseType *kase_block_ty
+	= TypeCheckExpr::Resolve (kase.get_expr ().get ());
+      kase_block_tys.push_back (kase_block_ty);
+    }
+
+  if (kase_block_tys.size () == 0)
+    {
+      infered
+	= TyTy::TupleType::get_unit_type (expr.get_mappings ().get_hirid ());
+      return;
+    }
+
+  infered = kase_block_tys.at (0);
+  for (size_t i = 1; i < kase_block_tys.size (); i++)
+    {
+      TyTy::BaseType *kase_ty = kase_block_tys.at (i);
+      infered = infered->unify (kase_ty);
+      if (infered->get_kind () == TyTy::TypeKind::ERROR)
+	return;
+    }
+}
+
+bool
+TypeCheckExpr::resolve_operator_overload (
+  Analysis::RustLangItem::ItemType lang_item_type, HIR::OperatorExprMeta expr,
+  TyTy::BaseType *lhs, TyTy::BaseType *rhs)
+{
+  // look up lang item for arithmetic type
+  std::string associated_item_name
+    = Analysis::RustLangItem::ToString (lang_item_type);
+  DefId respective_lang_item_id = UNKNOWN_DEFID;
+  bool lang_item_defined
+    = mappings->lookup_lang_item (lang_item_type, &respective_lang_item_id);
+
+  // probe for the lang-item
+  if (!lang_item_defined)
+    return false;
+
+  auto segment = HIR::PathIdentSegment (associated_item_name);
+  auto candidate
+    = MethodResolver::Probe (lhs, HIR::PathIdentSegment (associated_item_name));
+
+  bool have_implementation_for_lang_item = !candidate.is_error ();
+  if (!have_implementation_for_lang_item)
+    return false;
+
+  // Get the adjusted self
+  Adjuster adj (lhs);
+  TyTy::BaseType *adjusted_self = adj.adjust_type (candidate.adjustments);
+
+  // is this the case we are recursive
+  // handle the case where we are within the impl block for this lang_item
+  // otherwise we end up with a recursive operator overload such as the i32
+  // operator overload trait
+  TypeCheckContextItem &fn_context = context->peek_context ();
+  if (fn_context.get_type () == TypeCheckContextItem::ItemType::IMPL_ITEM)
+    {
+      auto &impl_item = fn_context.get_impl_item ();
+      HIR::ImplBlock *parent = impl_item.first;
+      HIR::Function *fn = impl_item.second;
+
+      if (parent->has_trait_ref ()
+	  && fn->get_function_name ().compare (associated_item_name) == 0)
+	{
+	  TraitReference *trait_reference
+	    = TraitResolver::Lookup (*parent->get_trait_ref ().get ());
+	  if (!trait_reference->is_error ())
+	    {
+	      TyTy::BaseType *lookup = nullptr;
+	      bool ok = context->lookup_type (fn->get_mappings ().get_hirid (),
+					      &lookup);
+	      rust_assert (ok);
+	      rust_assert (lookup->get_kind () == TyTy::TypeKind::FNDEF);
+
+	      TyTy::FnType *fntype = static_cast<TyTy::FnType *> (lookup);
+	      rust_assert (fntype->is_method ());
+
+	      bool is_lang_item_impl
+		= trait_reference->get_mappings ().get_defid ()
+		  == respective_lang_item_id;
+	      bool self_is_lang_item_self
+		= fntype->get_self_type ()->is_equal (*adjusted_self);
+	      bool recursive_operator_overload
+		= is_lang_item_impl && self_is_lang_item_self;
+
+	      if (recursive_operator_overload)
+		return false;
+	    }
+	}
+    }
+
+  // store the adjustments for code-generation to know what to do
+  context->insert_autoderef_mappings (expr.get_lvalue_mappings ().get_hirid (),
+				      std::move (candidate.adjustments));
+
+  // now its just like a method-call-expr
+  context->insert_receiver (expr.get_mappings ().get_hirid (), lhs);
+
+  PathProbeCandidate &resolved_candidate = candidate.candidate;
+  TyTy::BaseType *lookup_tyty = candidate.candidate.ty;
+  NodeId resolved_node_id
+    = resolved_candidate.is_impl_candidate ()
+	? resolved_candidate.item.impl.impl_item->get_impl_mappings ()
+	    .get_nodeid ()
+	: resolved_candidate.item.trait.item_ref->get_mappings ().get_nodeid ();
+
+  rust_assert (lookup_tyty->get_kind () == TyTy::TypeKind::FNDEF);
+  TyTy::BaseType *lookup = lookup_tyty;
+  TyTy::FnType *fn = static_cast<TyTy::FnType *> (lookup);
+  rust_assert (fn->is_method ());
+
+  auto root = lhs->get_root ();
+  if (root->get_kind () == TyTy::TypeKind::ADT)
+    {
+      const TyTy::ADTType *adt = static_cast<const TyTy::ADTType *> (root);
+      if (adt->has_substitutions () && fn->needs_substitution ())
+	{
+	  // consider the case where we have:
+	  //
+	  // struct Foo<X,Y>(X,Y);
+	  //
+	  // impl<T> Foo<T, i32> {
+	  //   fn test<X>(self, a:X) -> (T,X) { (self.0, a) }
+	  // }
+	  //
+	  // In this case we end up with an fn type of:
+	  //
+	  // fn <T,X> test(self:Foo<T,i32>, a:X) -> (T,X)
+	  //
+	  // This means the instance or self we are calling this method for
+	  // will be substituted such that we can get the inherited type
+	  // arguments but then need to use the turbo fish if available or
+	  // infer the remaining arguments. Luckily rust does not allow for
+	  // default types GenericParams on impl blocks since these must
+	  // always be at the end of the list
+
+	  auto s = fn->get_self_type ()->get_root ();
+	  rust_assert (s->can_eq (adt, false));
+	  rust_assert (s->get_kind () == TyTy::TypeKind::ADT);
+	  const TyTy::ADTType *self_adt
+	    = static_cast<const TyTy::ADTType *> (s);
+
+	  // we need to grab the Self substitutions as the inherit type
+	  // parameters for this
+	  if (self_adt->needs_substitution ())
+	    {
+	      rust_assert (adt->was_substituted ());
+
+	      TyTy::SubstitutionArgumentMappings used_args_in_prev_segment
+		= GetUsedSubstArgs::From (adt);
+
+	      TyTy::SubstitutionArgumentMappings inherit_type_args
+		= self_adt->solve_mappings_from_receiver_for_self (
+		  used_args_in_prev_segment);
+
+	      // there may or may not be inherited type arguments
+	      if (!inherit_type_args.is_error ())
+		{
+		  // need to apply the inherited type arguments to the
+		  // function
+		  lookup = fn->handle_substitions (inherit_type_args);
+		}
+	    }
+	}
+    }
+
+  // handle generics
+  if (lookup->needs_generic_substitutions ())
+    lookup = SubstMapper::InferSubst (lookup, expr.get_locus ());
+
+  // type check the arguments if required
+  TyTy::FnType *type = static_cast<TyTy::FnType *> (lookup);
+  rust_assert (type->num_params () > 0);
+  auto fnparam = type->param_at (0);
+  fnparam.second->unify (adjusted_self); // typecheck the self
+  if (rhs == nullptr)
+    {
+      rust_assert (type->num_params () == 1);
+    }
+  else
+    {
+      rust_assert (type->num_params () == 2);
+      auto fnparam = type->param_at (1);
+      fnparam.second->unify (rhs); // typecheck the rhs
+    }
+
+  rust_assert (lookup->get_kind () == TyTy::TypeKind::FNDEF);
+  fn = static_cast<TyTy::FnType *> (lookup);
+  fn->monomorphize ();
+
+  // get the return type
+  TyTy::BaseType *function_ret_tyty
+    = type->get_return_type ()->monomorphized_clone ();
+
+  // store the expected fntype
+  context->insert_operator_overload (expr.get_mappings ().get_hirid (), type);
+
+  // set up the resolved name on the path
+  resolver->insert_resolved_name (expr.get_mappings ().get_nodeid (),
+				  resolved_node_id);
+
+  // return the result of the function back
+  infered = function_ret_tyty;
+
+  return true;
+}
+
+bool
+TypeCheckExpr::validate_arithmetic_type (
+  const TyTy::BaseType *tyty, HIR::ArithmeticOrLogicalExpr::ExprType expr_type)
+{
+  const TyTy::BaseType *type = tyty->destructure ();
+
+  // https://doc.rust-lang.org/reference/expressions/operator-expr.html#arithmetic-and-logical-binary-operators
+  // this will change later when traits are added
+  switch (expr_type)
+    {
+    case ArithmeticOrLogicalOperator::ADD:
+    case ArithmeticOrLogicalOperator::SUBTRACT:
+    case ArithmeticOrLogicalOperator::MULTIPLY:
+    case ArithmeticOrLogicalOperator::DIVIDE:
+    case ArithmeticOrLogicalOperator::MODULUS:
+      return (type->get_kind () == TyTy::TypeKind::INT)
+	     || (type->get_kind () == TyTy::TypeKind::UINT)
+	     || (type->get_kind () == TyTy::TypeKind::FLOAT)
+	     || (type->get_kind () == TyTy::TypeKind::USIZE)
+	     || (type->get_kind () == TyTy::TypeKind::ISIZE)
+	     || (type->get_kind () == TyTy::TypeKind::INFER
+		 && (((const TyTy::InferType *) type)->get_infer_kind ()
+		     == TyTy::InferType::INTEGRAL))
+	     || (type->get_kind () == TyTy::TypeKind::INFER
+		 && (((const TyTy::InferType *) type)->get_infer_kind ()
+		     == TyTy::InferType::FLOAT));
+
+      // integers or bools
+    case ArithmeticOrLogicalOperator::BITWISE_AND:
+    case ArithmeticOrLogicalOperator::BITWISE_OR:
+    case ArithmeticOrLogicalOperator::BITWISE_XOR:
+      return (type->get_kind () == TyTy::TypeKind::INT)
+	     || (type->get_kind () == TyTy::TypeKind::UINT)
+	     || (type->get_kind () == TyTy::TypeKind::USIZE)
+	     || (type->get_kind () == TyTy::TypeKind::ISIZE)
+	     || (type->get_kind () == TyTy::TypeKind::BOOL)
+	     || (type->get_kind () == TyTy::TypeKind::INFER
+		 && (((const TyTy::InferType *) type)->get_infer_kind ()
+		     == TyTy::InferType::INTEGRAL));
+
+      // integers only
+    case ArithmeticOrLogicalOperator::LEFT_SHIFT:
+    case ArithmeticOrLogicalOperator::RIGHT_SHIFT:
+      return (type->get_kind () == TyTy::TypeKind::INT)
+	     || (type->get_kind () == TyTy::TypeKind::UINT)
+	     || (type->get_kind () == TyTy::TypeKind::USIZE)
+	     || (type->get_kind () == TyTy::TypeKind::ISIZE)
+	     || (type->get_kind () == TyTy::TypeKind::INFER
+		 && (((const TyTy::InferType *) type)->get_infer_kind ()
+		     == TyTy::InferType::INTEGRAL));
+    }
+
+  gcc_unreachable ();
+  return false;
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-hir-type-check-expr.h b/gcc/rust/typecheck/rust-hir-type-check-expr.h
new file mode 100644
index 00000000000..19a6c791a9d
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-expr.h
@@ -0,0 +1,131 @@
+// 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_HIR_TYPE_CHECK_EXPR
+#define RUST_HIR_TYPE_CHECK_EXPR
+
+#include "rust-hir-type-check-base.h"
+#include "rust-tyty.h"
+
+namespace Rust {
+namespace Resolver {
+
+class TypeCheckExpr : public TypeCheckBase, private HIR::HIRExpressionVisitor
+{
+public:
+  static TyTy::BaseType *Resolve (HIR::Expr *expr);
+
+  void visit (HIR::TupleIndexExpr &expr) override;
+  void visit (HIR::TupleExpr &expr) override;
+  void visit (HIR::ReturnExpr &expr) override;
+  void visit (HIR::CallExpr &expr) override;
+  void visit (HIR::MethodCallExpr &expr) override;
+  void visit (HIR::AssignmentExpr &expr) override;
+  void visit (HIR::CompoundAssignmentExpr &expr) override;
+  void visit (HIR::LiteralExpr &expr) override;
+  void visit (HIR::ArithmeticOrLogicalExpr &expr) override;
+  void visit (HIR::ComparisonExpr &expr) override;
+  void visit (HIR::LazyBooleanExpr &expr) override;
+  void visit (HIR::NegationExpr &expr) override;
+  void visit (HIR::IfExpr &expr) override;
+  void visit (HIR::IfExprConseqElse &expr) override;
+  void visit (HIR::IfExprConseqIf &expr) override;
+  void visit (HIR::IfLetExpr &expr) override;
+  void visit (HIR::BlockExpr &expr) override;
+  void visit (HIR::UnsafeBlockExpr &expr) override;
+  void visit (HIR::ArrayIndexExpr &expr) override;
+  void visit (HIR::ArrayExpr &expr) override;
+  void visit (HIR::StructExprStruct &struct_expr) override;
+  void visit (HIR::StructExprStructFields &struct_expr) override;
+  void visit (HIR::GroupedExpr &expr) override;
+  void visit (HIR::FieldAccessExpr &expr) override;
+  void visit (HIR::QualifiedPathInExpression &expr) override;
+  void visit (HIR::PathInExpression &expr) override;
+  void visit (HIR::LoopExpr &expr) override;
+  void visit (HIR::BreakExpr &expr) override;
+  void visit (HIR::ContinueExpr &expr) override;
+  void visit (HIR::BorrowExpr &expr) override;
+  void visit (HIR::DereferenceExpr &expr) override;
+  void visit (HIR::TypeCastExpr &expr) override;
+  void visit (HIR::MatchExpr &expr) override;
+  void visit (HIR::RangeFromToExpr &expr) override;
+  void visit (HIR::RangeFromExpr &expr) override;
+  void visit (HIR::RangeToExpr &expr) override;
+  void visit (HIR::RangeFullExpr &expr) override;
+  void visit (HIR::RangeFromToInclExpr &expr) override;
+  void visit (HIR::WhileLoopExpr &expr) override;
+
+  // TODO
+  void visit (HIR::ClosureExprInnerTyped &) override {}
+  void visit (HIR::ClosureExprInner &expr) override {}
+  void visit (HIR::ErrorPropagationExpr &expr) override {}
+  void visit (HIR::RangeToInclExpr &expr) override {}
+  void visit (HIR::WhileLetLoopExpr &expr) override {}
+  void visit (HIR::ForLoopExpr &expr) override {}
+  void visit (HIR::IfExprConseqIfLet &expr) override {}
+  void visit (HIR::IfLetExprConseqElse &expr) override {}
+  void visit (HIR::IfLetExprConseqIf &expr) override {}
+  void visit (HIR::IfLetExprConseqIfLet &expr) override {}
+  void visit (HIR::AwaitExpr &expr) override {}
+  void visit (HIR::AsyncBlockExpr &expr) override {}
+
+  // don't need to implement these see rust-hir-type-check-struct-field.h
+  void visit (HIR::StructExprFieldIdentifier &field) override
+  {
+    gcc_unreachable ();
+  }
+  void visit (HIR::StructExprFieldIdentifierValue &field) override
+  {
+    gcc_unreachable ();
+  }
+  void visit (HIR::StructExprFieldIndexValue &field) override
+  {
+    gcc_unreachable ();
+  }
+
+protected:
+  bool
+  resolve_operator_overload (Analysis::RustLangItem::ItemType lang_item_type,
+			     HIR::OperatorExprMeta expr, TyTy::BaseType *lhs,
+			     TyTy::BaseType *rhs);
+
+private:
+  TypeCheckExpr ();
+
+  TyTy::BaseType *resolve_root_path (HIR::PathInExpression &expr,
+				     size_t *offset,
+				     NodeId *root_resolved_node_id);
+
+  void resolve_segments (NodeId root_resolved_node_id,
+			 std::vector<HIR::PathExprSegment> &segments,
+			 size_t offset, TyTy::BaseType *tyseg,
+			 const Analysis::NodeMapping &expr_mappings,
+			 Location expr_locus);
+
+  bool
+  validate_arithmetic_type (const TyTy::BaseType *tyty,
+			    HIR::ArithmeticOrLogicalExpr::ExprType expr_type);
+
+  /* The return value of TypeCheckExpr::Resolve */
+  TyTy::BaseType *infered;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_HIR_TYPE_CHECK_EXPR
diff --git a/gcc/rust/typecheck/rust-hir-type-check-implitem.cc b/gcc/rust/typecheck/rust-hir-type-check-implitem.cc
new file mode 100644
index 00000000000..784e4990409
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-implitem.cc
@@ -0,0 +1,583 @@
+// 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/>.
+
+#include "rust-hir-type-check-implitem.h"
+#include "rust-hir-type-check-base.h"
+#include "rust-hir-full.h"
+#include "rust-hir-type-check-type.h"
+#include "rust-hir-type-check-expr.h"
+#include "rust-hir-type-check-pattern.h"
+#include "rust-tyty.h"
+
+namespace Rust {
+namespace Resolver {
+
+TypeCheckTopLevelExternItem::TypeCheckTopLevelExternItem (
+  const HIR::ExternBlock &parent)
+  : TypeCheckBase (), parent (parent)
+{}
+
+void
+TypeCheckTopLevelExternItem::Resolve (HIR::ExternalItem *item,
+				      const HIR::ExternBlock &parent)
+{
+  TypeCheckTopLevelExternItem resolver (parent);
+  item->accept_vis (resolver);
+}
+
+void
+TypeCheckTopLevelExternItem::visit (HIR::ExternalStaticItem &item)
+{
+  TyTy::BaseType *actual_type
+    = TypeCheckType::Resolve (item.get_item_type ().get ());
+
+  context->insert_type (item.get_mappings (), actual_type);
+}
+
+void
+TypeCheckTopLevelExternItem::visit (HIR::ExternalFunctionItem &function)
+{
+  std::vector<TyTy::SubstitutionParamMapping> substitutions;
+  if (function.has_generics ())
+    {
+      for (auto &generic_param : function.get_generic_params ())
+	{
+	  switch (generic_param.get ()->get_kind ())
+	    {
+	    case HIR::GenericParam::GenericKind::LIFETIME:
+	    case HIR::GenericParam::GenericKind::CONST:
+	      // FIXME: Skipping Lifetime and Const completely until better
+	      // handling.
+	      break;
+
+	      case HIR::GenericParam::GenericKind::TYPE: {
+		auto param_type
+		  = TypeResolveGenericParam::Resolve (generic_param.get ());
+		context->insert_type (generic_param->get_mappings (),
+				      param_type);
+
+		substitutions.push_back (TyTy::SubstitutionParamMapping (
+		  static_cast<HIR::TypeParam &> (*generic_param), param_type));
+	      }
+	      break;
+	    }
+	}
+    }
+
+  TyTy::BaseType *ret_type = nullptr;
+  if (!function.has_return_type ())
+    ret_type
+      = TyTy::TupleType::get_unit_type (function.get_mappings ().get_hirid ());
+  else
+    {
+      auto resolved
+	= TypeCheckType::Resolve (function.get_return_type ().get ());
+      if (resolved == nullptr)
+	{
+	  rust_error_at (function.get_locus (),
+			 "failed to resolve return type");
+	  return;
+	}
+
+      ret_type = resolved->clone ();
+      ret_type->set_ref (
+	function.get_return_type ()->get_mappings ().get_hirid ());
+    }
+
+  std::vector<std::pair<HIR::Pattern *, TyTy::BaseType *> > params;
+  for (auto &param : function.get_function_params ())
+    {
+      // get the name as well required for later on
+      auto param_tyty = TypeCheckType::Resolve (param.get_type ().get ());
+
+      // these are implicit mappings and not used
+      auto crate_num = mappings->get_current_crate ();
+      Analysis::NodeMapping mapping (crate_num, mappings->get_next_node_id (),
+				     mappings->get_next_hir_id (crate_num),
+				     UNKNOWN_LOCAL_DEFID);
+
+      HIR::IdentifierPattern *param_pattern
+	= new HIR::IdentifierPattern (mapping, param.get_param_name (),
+				      Location (), false, Mutability::Imm,
+				      std::unique_ptr<HIR::Pattern> (nullptr));
+
+      params.push_back (
+	std::pair<HIR::Pattern *, TyTy::BaseType *> (param_pattern,
+						     param_tyty));
+
+      context->insert_type (param.get_mappings (), param_tyty);
+
+      // FIXME do we need error checking for patterns here?
+      // see https://github.com/Rust-GCC/gccrs/issues/995
+    }
+
+  uint8_t flags = TyTy::FnType::FNTYPE_IS_EXTERN_FLAG;
+  if (function.is_variadic ())
+    flags |= TyTy::FnType::FNTYPE_IS_VARADIC_FLAG;
+
+  RustIdent ident{
+    CanonicalPath::new_seg (function.get_mappings ().get_nodeid (),
+			    function.get_item_name ()),
+    function.get_locus ()};
+
+  auto fnType = new TyTy::FnType (function.get_mappings ().get_hirid (),
+				  function.get_mappings ().get_defid (),
+				  function.get_item_name (), ident, flags,
+				  parent.get_abi (), std::move (params),
+				  ret_type, std::move (substitutions));
+
+  context->insert_type (function.get_mappings (), fnType);
+}
+
+TypeCheckTopLevelImplItem::TypeCheckTopLevelImplItem (
+  TyTy::BaseType *self,
+  std::vector<TyTy::SubstitutionParamMapping> substitutions)
+  : TypeCheckBase (), self (self), substitutions (substitutions)
+{}
+
+void
+TypeCheckTopLevelImplItem::Resolve (
+  HIR::ImplItem *item, TyTy::BaseType *self,
+  std::vector<TyTy::SubstitutionParamMapping> substitutions)
+{
+  TypeCheckTopLevelImplItem resolver (self, substitutions);
+  item->accept_vis (resolver);
+}
+
+void
+TypeCheckTopLevelImplItem::visit (HIR::TypeAlias &alias)
+{
+  TyTy::BaseType *actual_type
+    = TypeCheckType::Resolve (alias.get_type_aliased ().get ());
+
+  context->insert_type (alias.get_mappings (), actual_type);
+
+  for (auto &where_clause_item : alias.get_where_clause ().get_items ())
+    {
+      ResolveWhereClauseItem::Resolve (*where_clause_item.get ());
+    }
+}
+
+void
+TypeCheckTopLevelImplItem::visit (HIR::ConstantItem &constant)
+{
+  TyTy::BaseType *type = TypeCheckType::Resolve (constant.get_type ());
+  TyTy::BaseType *expr_type = TypeCheckExpr::Resolve (constant.get_expr ());
+
+  context->insert_type (constant.get_mappings (), type->unify (expr_type));
+}
+
+void
+TypeCheckTopLevelImplItem::visit (HIR::Function &function)
+{
+  if (function.has_generics ())
+    {
+      for (auto &generic_param : function.get_generic_params ())
+	{
+	  switch (generic_param.get ()->get_kind ())
+	    {
+	    case HIR::GenericParam::GenericKind::LIFETIME:
+	    case HIR::GenericParam::GenericKind::CONST:
+	      // FIXME: Skipping Lifetime and Const completely until better
+	      // handling.
+	      break;
+
+	      case HIR::GenericParam::GenericKind::TYPE: {
+		auto param_type
+		  = TypeResolveGenericParam::Resolve (generic_param.get ());
+		context->insert_type (generic_param->get_mappings (),
+				      param_type);
+
+		substitutions.push_back (TyTy::SubstitutionParamMapping (
+		  static_cast<HIR::TypeParam &> (*generic_param), param_type));
+	      }
+	      break;
+	    }
+	}
+    }
+
+  for (auto &where_clause_item : function.get_where_clause ().get_items ())
+    {
+      ResolveWhereClauseItem::Resolve (*where_clause_item.get ());
+    }
+
+  TyTy::BaseType *ret_type = nullptr;
+  if (!function.has_function_return_type ())
+    ret_type
+      = TyTy::TupleType::get_unit_type (function.get_mappings ().get_hirid ());
+  else
+    {
+      auto resolved
+	= TypeCheckType::Resolve (function.get_return_type ().get ());
+      if (resolved == nullptr)
+	{
+	  rust_error_at (function.get_locus (),
+			 "failed to resolve return type");
+	  return;
+	}
+
+      ret_type = resolved->clone ();
+      ret_type->set_ref (
+	function.get_return_type ()->get_mappings ().get_hirid ());
+    }
+
+  std::vector<std::pair<HIR::Pattern *, TyTy::BaseType *> > params;
+  if (function.is_method ())
+    {
+      // these are implicit mappings and not used
+      auto crate_num = mappings->get_current_crate ();
+      Analysis::NodeMapping mapping (crate_num, mappings->get_next_node_id (),
+				     mappings->get_next_hir_id (crate_num),
+				     UNKNOWN_LOCAL_DEFID);
+
+      // add the synthetic self param at the front, this is a placeholder for
+      // compilation to know parameter names. The types are ignored but we
+      // reuse the HIR identifier pattern which requires it
+      HIR::SelfParam &self_param = function.get_self_param ();
+      HIR::IdentifierPattern *self_pattern
+	= new HIR::IdentifierPattern (mapping, "self", self_param.get_locus (),
+				      self_param.is_ref (),
+				      self_param.get_mut (),
+				      std::unique_ptr<HIR::Pattern> (nullptr));
+
+      // might have a specified type
+      TyTy::BaseType *self_type = nullptr;
+      if (self_param.has_type ())
+	{
+	  std::unique_ptr<HIR::Type> &specified_type = self_param.get_type ();
+	  self_type = TypeCheckType::Resolve (specified_type.get ());
+	}
+      else
+	{
+	  switch (self_param.get_self_kind ())
+	    {
+	    case HIR::SelfParam::IMM:
+	    case HIR::SelfParam::MUT:
+	      self_type = self->clone ();
+	      break;
+
+	    case HIR::SelfParam::IMM_REF:
+	      self_type = new TyTy::ReferenceType (
+		self_param.get_mappings ().get_hirid (),
+		TyTy::TyVar (self->get_ref ()), Mutability::Imm);
+	      break;
+
+	    case HIR::SelfParam::MUT_REF:
+	      self_type = new TyTy::ReferenceType (
+		self_param.get_mappings ().get_hirid (),
+		TyTy::TyVar (self->get_ref ()), Mutability::Mut);
+	      break;
+
+	    default:
+	      gcc_unreachable ();
+	      return;
+	    }
+	}
+
+      context->insert_type (self_param.get_mappings (), self_type);
+      params.push_back (
+	std::pair<HIR::Pattern *, TyTy::BaseType *> (self_pattern, self_type));
+    }
+
+  for (auto &param : function.get_function_params ())
+    {
+      // get the name as well required for later on
+      auto param_tyty = TypeCheckType::Resolve (param.get_type ());
+      params.push_back (
+	std::pair<HIR::Pattern *, TyTy::BaseType *> (param.get_param_name (),
+						     param_tyty));
+
+      context->insert_type (param.get_mappings (), param_tyty);
+      TypeCheckPattern::Resolve (param.get_param_name (), param_tyty);
+    }
+
+  const CanonicalPath *canonical_path = nullptr;
+  bool ok
+    = mappings->lookup_canonical_path (function.get_mappings ().get_nodeid (),
+				       &canonical_path);
+  rust_assert (ok);
+
+  RustIdent ident{*canonical_path, function.get_locus ()};
+  auto fnType = new TyTy::FnType (function.get_mappings ().get_hirid (),
+				  function.get_mappings ().get_defid (),
+				  function.get_function_name (), ident,
+				  function.is_method ()
+				    ? TyTy::FnType::FNTYPE_IS_METHOD_FLAG
+				    : TyTy::FnType::FNTYPE_DEFAULT_FLAGS,
+				  ABI::RUST, std::move (params), ret_type,
+				  std::move (substitutions));
+
+  context->insert_type (function.get_mappings (), fnType);
+}
+
+TypeCheckImplItem::TypeCheckImplItem (HIR::ImplBlock *parent,
+				      TyTy::BaseType *self)
+  : TypeCheckBase (), parent (parent), self (self)
+{}
+
+void
+TypeCheckImplItem::Resolve (HIR::ImplBlock *parent, HIR::ImplItem *item,
+			    TyTy::BaseType *self)
+{
+  TypeCheckImplItem resolver (parent, self);
+  item->accept_vis (resolver);
+}
+
+void
+TypeCheckImplItem::visit (HIR::Function &function)
+{
+  TyTy::BaseType *lookup;
+  if (!context->lookup_type (function.get_mappings ().get_hirid (), &lookup))
+    {
+      rust_error_at (function.get_locus (), "failed to lookup function type");
+      return;
+    }
+
+  if (lookup->get_kind () != TyTy::TypeKind::FNDEF)
+    {
+      rust_error_at (function.get_locus (),
+		     "found invalid type for function [%s]",
+		     lookup->as_string ().c_str ());
+      return;
+    }
+
+  // need to get the return type from this
+  TyTy::FnType *resolve_fn_type = static_cast<TyTy::FnType *> (lookup);
+  auto expected_ret_tyty = resolve_fn_type->get_return_type ();
+  context->push_return_type (TypeCheckContextItem (parent, &function),
+			     expected_ret_tyty);
+
+  auto block_expr_ty
+    = TypeCheckExpr::Resolve (function.get_definition ().get ());
+
+  context->pop_return_type ();
+  expected_ret_tyty->unify (block_expr_ty);
+}
+
+void
+TypeCheckImplItem::visit (HIR::ConstantItem &const_item)
+{}
+
+void
+TypeCheckImplItem::visit (HIR::TypeAlias &type_alias)
+{}
+
+TypeCheckImplItemWithTrait::TypeCheckImplItemWithTrait (
+  HIR::ImplBlock *parent, TyTy::BaseType *self,
+  TyTy::TypeBoundPredicate &trait_reference,
+  std::vector<TyTy::SubstitutionParamMapping> substitutions)
+  : TypeCheckImplItem (parent, self), trait_reference (trait_reference),
+    resolved_trait_item (TyTy::TypeBoundPredicateItem::error ()),
+    substitutions (substitutions)
+{
+  rust_assert (is_trait_impl_block ());
+}
+
+TyTy::TypeBoundPredicateItem
+TypeCheckImplItemWithTrait::Resolve (
+  HIR::ImplBlock *parent, HIR::ImplItem *item, TyTy::BaseType *self,
+  TyTy::TypeBoundPredicate &trait_reference,
+  std::vector<TyTy::SubstitutionParamMapping> substitutions)
+{
+  TypeCheckImplItemWithTrait resolver (parent, self, trait_reference,
+				       substitutions);
+  item->accept_vis (resolver);
+  return resolver.resolved_trait_item;
+}
+
+void
+TypeCheckImplItemWithTrait::visit (HIR::ConstantItem &constant)
+{
+  // normal resolution of the item
+  TypeCheckImplItem::visit (constant);
+  TyTy::BaseType *lookup;
+  if (!context->lookup_type (constant.get_mappings ().get_hirid (), &lookup))
+    return;
+
+  // map the impl item to the associated trait item
+  const auto tref = trait_reference.get ();
+  const TraitItemReference *raw_trait_item = nullptr;
+  bool found
+    = tref->lookup_trait_item_by_type (constant.get_identifier (),
+				       TraitItemReference::TraitItemType::CONST,
+				       &raw_trait_item);
+
+  // unknown trait item
+  if (!found || raw_trait_item->is_error ())
+    {
+      RichLocation r (constant.get_locus ());
+      r.add_range (trait_reference.get_locus ());
+      rust_error_at (r, "constant %<%s%> is not a member of trait %<%s%>",
+		     constant.get_identifier ().c_str (),
+		     trait_reference.get_name ().c_str ());
+      return;
+    }
+
+  // get the item from the predicate
+  resolved_trait_item = trait_reference.lookup_associated_item (raw_trait_item);
+  rust_assert (!resolved_trait_item.is_error ());
+
+  // merge the attributes
+  const HIR::TraitItem *hir_trait_item
+    = resolved_trait_item.get_raw_item ()->get_hir_trait_item ();
+  merge_attributes (constant.get_outer_attrs (), *hir_trait_item);
+
+  // check the types are compatible
+  auto trait_item_type = resolved_trait_item.get_tyty_for_receiver (self);
+  if (!trait_item_type->can_eq (lookup, true))
+    {
+      RichLocation r (constant.get_locus ());
+      r.add_range (resolved_trait_item.get_locus ());
+
+      rust_error_at (
+	r, "constant %<%s%> has an incompatible type for trait %<%s%>",
+	constant.get_identifier ().c_str (),
+	trait_reference.get_name ().c_str ());
+    }
+}
+
+void
+TypeCheckImplItemWithTrait::visit (HIR::TypeAlias &type)
+{
+  // normal resolution of the item
+  TypeCheckImplItem::visit (type);
+  TyTy::BaseType *lookup;
+  if (!context->lookup_type (type.get_mappings ().get_hirid (), &lookup))
+    return;
+
+  // map the impl item to the associated trait item
+  const auto tref = trait_reference.get ();
+  const TraitItemReference *raw_trait_item = nullptr;
+  bool found
+    = tref->lookup_trait_item_by_type (type.get_new_type_name (),
+				       TraitItemReference::TraitItemType::TYPE,
+				       &raw_trait_item);
+
+  // unknown trait item
+  if (!found || raw_trait_item->is_error ())
+    {
+      RichLocation r (type.get_locus ());
+      r.add_range (trait_reference.get_locus ());
+      rust_error_at (r, "type alias %<%s%> is not a member of trait %<%s%>",
+		     type.get_new_type_name ().c_str (),
+		     trait_reference.get_name ().c_str ());
+      return;
+    }
+
+  // get the item from the predicate
+  resolved_trait_item = trait_reference.lookup_associated_item (raw_trait_item);
+  rust_assert (!resolved_trait_item.is_error ());
+
+  // merge the attributes
+  const HIR::TraitItem *hir_trait_item
+    = resolved_trait_item.get_raw_item ()->get_hir_trait_item ();
+  merge_attributes (type.get_outer_attrs (), *hir_trait_item);
+
+  // check the types are compatible
+  auto trait_item_type = resolved_trait_item.get_tyty_for_receiver (self);
+  if (!trait_item_type->can_eq (lookup, true))
+    {
+      RichLocation r (type.get_locus ());
+      r.add_range (resolved_trait_item.get_locus ());
+
+      rust_error_at (
+	r, "type alias %<%s%> has an incompatible type for trait %<%s%>",
+	type.get_new_type_name ().c_str (),
+	trait_reference.get_name ().c_str ());
+    }
+
+  // its actually a projection, since we need a way to actually bind the
+  // generic substitutions to the type itself
+  TyTy::ProjectionType *projection
+    = new TyTy::ProjectionType (type.get_mappings ().get_hirid (), lookup, tref,
+				raw_trait_item->get_mappings ().get_defid (),
+				substitutions);
+
+  context->insert_type (type.get_mappings (), projection);
+  raw_trait_item->associated_type_set (projection);
+}
+
+void
+TypeCheckImplItemWithTrait::visit (HIR::Function &function)
+{
+  // we get the error checking from the base method here
+  TypeCheckImplItem::visit (function);
+  TyTy::BaseType *lookup;
+  if (!context->lookup_type (function.get_mappings ().get_hirid (), &lookup))
+    return;
+
+  // map the impl item to the associated trait item
+  const auto tref = trait_reference.get ();
+  const TraitItemReference *raw_trait_item = nullptr;
+  bool found
+    = tref->lookup_trait_item_by_type (function.get_function_name (),
+				       TraitItemReference::TraitItemType::FN,
+				       &raw_trait_item);
+
+  // unknown trait item
+  if (!found || raw_trait_item->is_error ())
+    {
+      RichLocation r (function.get_locus ());
+      r.add_range (trait_reference.get_locus ());
+      rust_error_at (r, "method %<%s%> is not a member of trait %<%s%>",
+		     function.get_function_name ().c_str (),
+		     trait_reference.get_name ().c_str ());
+      return;
+    }
+
+  // get the item from the predicate
+  resolved_trait_item = trait_reference.lookup_associated_item (raw_trait_item);
+  rust_assert (!resolved_trait_item.is_error ());
+
+  // merge the attributes
+  const HIR::TraitItem *hir_trait_item
+    = resolved_trait_item.get_raw_item ()->get_hir_trait_item ();
+  merge_attributes (function.get_outer_attrs (), *hir_trait_item);
+
+  // check the types are compatible
+  auto trait_item_type = resolved_trait_item.get_tyty_for_receiver (self);
+  if (!trait_item_type->can_eq (lookup, true))
+    {
+      RichLocation r (function.get_locus ());
+      r.add_range (resolved_trait_item.get_locus ());
+
+      rust_error_at (r,
+		     "method %<%s%> has an incompatible type for trait %<%s%>",
+		     function.get_function_name ().c_str (),
+		     trait_reference.get_name ().c_str ());
+    }
+}
+
+void
+TypeCheckImplItemWithTrait::merge_attributes (AST::AttrVec &impl_item_attrs,
+					      const HIR::TraitItem &trait_item)
+{
+  for (const auto &attr : trait_item.get_outer_attrs ())
+    {
+      impl_item_attrs.push_back (attr);
+    }
+}
+
+bool
+TypeCheckImplItemWithTrait::is_trait_impl_block () const
+{
+  return !trait_reference.is_error ();
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-hir-type-check-implitem.h b/gcc/rust/typecheck/rust-hir-type-check-implitem.h
new file mode 100644
index 00000000000..f2f3faab9e0
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-implitem.h
@@ -0,0 +1,114 @@
+// 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_HIR_TYPE_CHECK_IMPLITEM_H
+#define RUST_HIR_TYPE_CHECK_IMPLITEM_H
+
+#include "rust-hir-type-check-base.h"
+
+namespace Rust {
+namespace Resolver {
+
+class TypeCheckTopLevelExternItem : public TypeCheckBase,
+				    public HIR::HIRExternalItemVisitor
+{
+public:
+  static void Resolve (HIR::ExternalItem *item, const HIR::ExternBlock &parent);
+
+  void visit (HIR::ExternalStaticItem &item) override;
+  void visit (HIR::ExternalFunctionItem &function) override;
+
+private:
+  TypeCheckTopLevelExternItem (const HIR::ExternBlock &parent);
+
+  const HIR::ExternBlock &parent;
+};
+
+class TypeCheckTopLevelImplItem : public TypeCheckBase,
+				  public HIR::HIRImplVisitor
+{
+public:
+  static void
+  Resolve (HIR::ImplItem *item, TyTy::BaseType *self,
+	   std::vector<TyTy::SubstitutionParamMapping> substitutions);
+
+  void visit (HIR::TypeAlias &alias) override;
+  void visit (HIR::ConstantItem &constant) override;
+  void visit (HIR::Function &function) override;
+
+private:
+  TypeCheckTopLevelImplItem (
+    TyTy::BaseType *self,
+    std::vector<TyTy::SubstitutionParamMapping> substitutions);
+
+  TyTy::BaseType *self;
+  std::vector<TyTy::SubstitutionParamMapping> substitutions;
+};
+
+class TypeCheckImplItem : public TypeCheckBase, public HIR::HIRImplVisitor
+{
+public:
+  static void Resolve (HIR::ImplBlock *parent, HIR::ImplItem *item,
+		       TyTy::BaseType *self);
+
+  void visit (HIR::Function &function) override;
+  void visit (HIR::ConstantItem &const_item) override;
+  void visit (HIR::TypeAlias &type_alias) override;
+
+protected:
+  TypeCheckImplItem (HIR::ImplBlock *parent, TyTy::BaseType *self);
+
+  HIR::ImplBlock *parent;
+  TyTy::BaseType *self;
+};
+
+class TypeCheckImplItemWithTrait : public TypeCheckImplItem
+{
+public:
+  static TyTy::TypeBoundPredicateItem
+  Resolve (HIR::ImplBlock *parent, HIR::ImplItem *item, TyTy::BaseType *self,
+	   TyTy::TypeBoundPredicate &trait_reference,
+	   std::vector<TyTy::SubstitutionParamMapping> substitutions);
+
+  void visit (HIR::ConstantItem &constant) override;
+  void visit (HIR::TypeAlias &type) override;
+  void visit (HIR::Function &function) override;
+
+protected:
+  // this allows us to inherit the must_use specified on a trait definition onto
+  // its implementation
+  void merge_attributes (AST::AttrVec &impl_item_attrs,
+			 const HIR::TraitItem &trait_item);
+
+private:
+  TypeCheckImplItemWithTrait (
+    HIR::ImplBlock *parent, TyTy::BaseType *self,
+    TyTy::TypeBoundPredicate &trait_reference,
+    std::vector<TyTy::SubstitutionParamMapping> substitutions);
+
+  bool is_trait_impl_block () const;
+
+  TyTy::TypeBoundPredicate &trait_reference;
+  TyTy::TypeBoundPredicateItem resolved_trait_item;
+  std::vector<TyTy::SubstitutionParamMapping> substitutions;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_HIR_TYPE_CHECK_IMPLITEM_H
diff --git a/gcc/rust/typecheck/rust-hir-type-check-item.cc b/gcc/rust/typecheck/rust-hir-type-check-item.cc
new file mode 100644
index 00000000000..d31a6df4777
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-item.cc
@@ -0,0 +1,237 @@
+// 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/>.
+
+#include "rust-hir-type-check-item.h"
+#include "rust-hir-full.h"
+#include "rust-hir-type-check-implitem.h"
+#include "rust-hir-type-check-type.h"
+#include "rust-hir-type-check-stmt.h"
+#include "rust-hir-type-check-expr.h"
+#include "rust-hir-trait-resolve.h"
+
+namespace Rust {
+namespace Resolver {
+
+TypeCheckItem::TypeCheckItem () : TypeCheckBase () {}
+
+void
+TypeCheckItem::Resolve (HIR::Item &item)
+{
+  rust_assert (item.get_hir_kind () == HIR::Node::BaseKind::VIS_ITEM);
+  HIR::VisItem &vis_item = static_cast<HIR::VisItem &> (item);
+
+  TypeCheckItem resolver;
+  vis_item.accept_vis (resolver);
+}
+
+void
+TypeCheckItem::visit (HIR::ImplBlock &impl_block)
+{
+  std::vector<TyTy::SubstitutionParamMapping> substitutions;
+  if (impl_block.has_generics ())
+    {
+      for (auto &generic_param : impl_block.get_generic_params ())
+	{
+	  switch (generic_param.get ()->get_kind ())
+	    {
+	    case HIR::GenericParam::GenericKind::LIFETIME:
+	    case HIR::GenericParam::GenericKind::CONST:
+	      // FIXME: Skipping Lifetime and Const completely until better
+	      // handling.
+	      break;
+
+	      case HIR::GenericParam::GenericKind::TYPE: {
+		TyTy::BaseType *l = nullptr;
+		bool ok = context->lookup_type (
+		  generic_param->get_mappings ().get_hirid (), &l);
+		if (ok && l->get_kind () == TyTy::TypeKind::PARAM)
+		  {
+		    substitutions.push_back (TyTy::SubstitutionParamMapping (
+		      static_cast<HIR::TypeParam &> (*generic_param),
+		      static_cast<TyTy::ParamType *> (l)));
+		  }
+	      }
+	      break;
+	    }
+	}
+    }
+
+  auto specified_bound = TyTy::TypeBoundPredicate::error ();
+  TraitReference *trait_reference = &TraitReference::error_node ();
+  if (impl_block.has_trait_ref ())
+    {
+      std::unique_ptr<HIR::TypePath> &ref = impl_block.get_trait_ref ();
+      trait_reference = TraitResolver::Resolve (*ref.get ());
+      rust_assert (!trait_reference->is_error ());
+
+      // we don't error out here see: gcc/testsuite/rust/compile/traits2.rs
+      // for example
+      specified_bound = get_predicate_from_bound (*ref.get ());
+    }
+
+  TyTy::BaseType *self = nullptr;
+  if (!context->lookup_type (
+	impl_block.get_type ()->get_mappings ().get_hirid (), &self))
+    {
+      rust_error_at (impl_block.get_locus (),
+		     "failed to resolve Self for ImplBlock");
+      return;
+    }
+
+  // inherit the bounds
+  if (!specified_bound.is_error ())
+    self->inherit_bounds ({specified_bound});
+
+  // check for any unconstrained type-params
+  const TyTy::SubstitutionArgumentMappings trait_constraints
+    = specified_bound.get_substitution_arguments ();
+  const TyTy::SubstitutionArgumentMappings impl_constraints
+    = GetUsedSubstArgs::From (self);
+
+  bool impl_block_has_unconstrained_typarams
+    = check_for_unconstrained (substitutions, trait_constraints,
+			       impl_constraints, self);
+  if (impl_block_has_unconstrained_typarams)
+    return;
+
+  // validate the impl items
+  bool is_trait_impl_block = !trait_reference->is_error ();
+  std::vector<const TraitItemReference *> trait_item_refs;
+  for (auto &impl_item : impl_block.get_impl_items ())
+    {
+      if (!is_trait_impl_block)
+	TypeCheckImplItem::Resolve (&impl_block, impl_item.get (), self);
+      else
+	{
+	  auto trait_item_ref
+	    = TypeCheckImplItemWithTrait::Resolve (&impl_block,
+						   impl_item.get (), self,
+						   specified_bound,
+						   substitutions);
+	  trait_item_refs.push_back (trait_item_ref.get_raw_item ());
+	}
+    }
+
+  bool impl_block_missing_trait_items
+    = is_trait_impl_block
+      && trait_reference->size () != trait_item_refs.size ();
+  if (impl_block_missing_trait_items)
+    {
+      // filter the missing impl_items
+      std::vector<std::reference_wrapper<const TraitItemReference>>
+	missing_trait_items;
+      for (const auto &trait_item_ref : trait_reference->get_trait_items ())
+	{
+	  bool found = false;
+	  for (auto implemented_trait_item : trait_item_refs)
+	    {
+	      std::string trait_item_name = trait_item_ref.get_identifier ();
+	      std::string impl_item_name
+		= implemented_trait_item->get_identifier ();
+	      found = trait_item_name.compare (impl_item_name) == 0;
+	      if (found)
+		break;
+	    }
+
+	  bool is_required_trait_item = !trait_item_ref.is_optional ();
+	  if (!found && is_required_trait_item)
+	    missing_trait_items.push_back (trait_item_ref);
+	}
+
+      if (missing_trait_items.size () > 0)
+	{
+	  std::string missing_items_buf;
+	  RichLocation r (impl_block.get_locus ());
+	  for (size_t i = 0; i < missing_trait_items.size (); i++)
+	    {
+	      bool has_more = (i + 1) < missing_trait_items.size ();
+	      const TraitItemReference &missing_trait_item
+		= missing_trait_items.at (i);
+	      missing_items_buf += missing_trait_item.get_identifier ()
+				   + (has_more ? ", " : "");
+	      r.add_range (missing_trait_item.get_locus ());
+	    }
+
+	  rust_error_at (r, "missing %s in implementation of trait %<%s%>",
+			 missing_items_buf.c_str (),
+			 trait_reference->get_name ().c_str ());
+	}
+    }
+
+  if (is_trait_impl_block)
+    {
+      trait_reference->clear_associated_types ();
+
+      AssociatedImplTrait associated (trait_reference, &impl_block, self,
+				      context);
+      context->insert_associated_trait_impl (
+	impl_block.get_mappings ().get_hirid (), std::move (associated));
+      context->insert_associated_impl_mapping (
+	trait_reference->get_mappings ().get_hirid (), self,
+	impl_block.get_mappings ().get_hirid ());
+    }
+}
+
+void
+TypeCheckItem::visit (HIR::Function &function)
+{
+  TyTy::BaseType *lookup;
+  if (!context->lookup_type (function.get_mappings ().get_hirid (), &lookup))
+    {
+      rust_error_at (function.get_locus (), "failed to lookup function type");
+      return;
+    }
+
+  if (lookup->get_kind () != TyTy::TypeKind::FNDEF)
+    {
+      rust_error_at (function.get_locus (),
+		     "found invalid type for function [%s]",
+		     lookup->as_string ().c_str ());
+      return;
+    }
+
+  // need to get the return type from this
+  TyTy::FnType *resolved_fn_type = static_cast<TyTy::FnType *> (lookup);
+  auto expected_ret_tyty = resolved_fn_type->get_return_type ();
+  context->push_return_type (TypeCheckContextItem (&function),
+			     expected_ret_tyty);
+
+  auto block_expr_ty
+    = TypeCheckExpr::Resolve (function.get_definition ().get ());
+
+  context->pop_return_type ();
+
+  if (block_expr_ty->get_kind () != TyTy::NEVER)
+    expected_ret_tyty->unify (block_expr_ty);
+}
+
+void
+TypeCheckItem::visit (HIR::Module &module)
+{
+  for (auto &item : module.get_items ())
+    TypeCheckItem::Resolve (*item.get ());
+}
+
+void
+TypeCheckItem::visit (HIR::Trait &trait)
+{
+  TraitResolver::Resolve (trait);
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-hir-type-check-item.h b/gcc/rust/typecheck/rust-hir-type-check-item.h
new file mode 100644
index 00000000000..ba4de19c9c7
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-item.h
@@ -0,0 +1,58 @@
+// 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_HIR_TYPE_CHECK_ITEM
+#define RUST_HIR_TYPE_CHECK_ITEM
+
+#include "rust-hir-type-check-base.h"
+
+namespace Rust {
+namespace Resolver {
+
+class TypeCheckItem : private TypeCheckBase, private HIR::HIRVisItemVisitor
+{
+public:
+  static void Resolve (HIR::Item &item);
+
+  void visit (HIR::ImplBlock &impl_block) override;
+  void visit (HIR::Function &function) override;
+  void visit (HIR::Module &module) override;
+  void visit (HIR::Trait &trait) override;
+
+  // FIXME - get rid of toplevel pass
+  void visit (HIR::TypeAlias &alias) override{};
+  void visit (HIR::TupleStruct &struct_decl) override{};
+  void visit (HIR::StructStruct &struct_decl) override{};
+  void visit (HIR::Enum &enum_decl) override{};
+  void visit (HIR::Union &union_decl) override{};
+  void visit (HIR::StaticItem &var) override{};
+  void visit (HIR::ConstantItem &constant) override{};
+  void visit (HIR::ExternBlock &extern_block) override{};
+
+  // nothing to do
+  void visit (HIR::ExternCrate &crate) override {}
+  void visit (HIR::UseDeclaration &use_decl) override {}
+
+private:
+  TypeCheckItem ();
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_HIR_TYPE_CHECK_ITEM
diff --git a/gcc/rust/typecheck/rust-hir-type-check-path.cc b/gcc/rust/typecheck/rust-hir-type-check-path.cc
new file mode 100644
index 00000000000..84f3b6ea6e6
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-path.cc
@@ -0,0 +1,467 @@
+// 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/>.
+
+#include "rust-hir-type-check-expr.h"
+#include "rust-hir-type-check-type.h"
+#include "rust-hir-trait-resolve.h"
+
+namespace Rust {
+namespace Resolver {
+
+void
+TypeCheckExpr::visit (HIR::QualifiedPathInExpression &expr)
+{
+  HIR::QualifiedPathType qual_path_type = expr.get_path_type ();
+  TyTy::BaseType *root
+    = TypeCheckType::Resolve (qual_path_type.get_type ().get ());
+  if (root->get_kind () == TyTy::TypeKind::ERROR)
+    return;
+
+  if (!qual_path_type.has_as_clause ())
+    {
+      NodeId root_resolved_node_id = UNKNOWN_NODEID;
+      resolve_segments (root_resolved_node_id, expr.get_segments (), 0, root,
+			expr.get_mappings (), expr.get_locus ());
+      return;
+    }
+
+  // Resolve the trait now
+  std::unique_ptr<HIR::TypePath> &trait_path_ref = qual_path_type.get_trait ();
+  TraitReference *trait_ref = TraitResolver::Resolve (*trait_path_ref.get ());
+  if (trait_ref->is_error ())
+    return;
+
+  // does this type actually implement this type-bound?
+  if (!TypeBoundsProbe::is_bound_satisfied_for_type (root, trait_ref))
+    return;
+
+  // then we need to look at the next segment to create perform the correct
+  // projection type
+  if (expr.get_segments ().empty ())
+    return;
+
+  // get the predicate for the bound
+  auto specified_bound = get_predicate_from_bound (*trait_path_ref.get ());
+  if (specified_bound.is_error ())
+    return;
+
+  // inherit the bound
+  root->inherit_bounds ({specified_bound});
+
+  // setup the associated types
+  const TraitReference *specified_bound_ref = specified_bound.get ();
+  auto candidates = TypeBoundsProbe::Probe (root);
+  AssociatedImplTrait *associated_impl_trait = nullptr;
+  for (auto &probed_bound : candidates)
+    {
+      const TraitReference *bound_trait_ref = probed_bound.first;
+      const HIR::ImplBlock *associated_impl = probed_bound.second;
+
+      HirId impl_block_id = associated_impl->get_mappings ().get_hirid ();
+      AssociatedImplTrait *associated = nullptr;
+      bool found_impl_trait
+	= context->lookup_associated_trait_impl (impl_block_id, &associated);
+      if (found_impl_trait)
+	{
+	  bool found_trait = specified_bound_ref->is_equal (*bound_trait_ref);
+	  bool found_self = associated->get_self ()->can_eq (root, false);
+	  if (found_trait && found_self)
+	    {
+	      associated_impl_trait = associated;
+	      break;
+	    }
+	}
+    }
+
+  if (associated_impl_trait != nullptr)
+    {
+      associated_impl_trait->setup_associated_types (root, specified_bound);
+    }
+
+  // lookup the associated item from the specified bound
+  HIR::PathExprSegment &item_seg = expr.get_segments ().at (0);
+  HIR::PathIdentSegment item_seg_identifier = item_seg.get_segment ();
+  TyTy::TypeBoundPredicateItem item
+    = specified_bound.lookup_associated_item (item_seg_identifier.as_string ());
+  if (item.is_error ())
+    {
+      rust_error_at (item_seg.get_locus (), "unknown associated item");
+      return;
+    }
+
+  // infer the root type
+  infered = item.get_tyty_for_receiver (root);
+
+  // turbo-fish segment path::<ty>
+  if (item_seg.has_generic_args ())
+    {
+      if (!infered->can_substitute ())
+	{
+	  rust_error_at (item_seg.get_locus (),
+			 "substitutions not supported for %s",
+			 infered->as_string ().c_str ());
+	  infered = new TyTy::ErrorType (expr.get_mappings ().get_hirid ());
+	  return;
+	}
+      infered = SubstMapper::Resolve (infered, expr.get_locus (),
+				      &item_seg.get_generic_args ());
+    }
+
+  // continue on as a path-in-expression
+  const TraitItemReference *trait_item_ref = item.get_raw_item ();
+  NodeId root_resolved_node_id = trait_item_ref->get_mappings ().get_nodeid ();
+  bool fully_resolved = expr.get_segments ().size () <= 1;
+
+  if (fully_resolved)
+    {
+      resolver->insert_resolved_name (expr.get_mappings ().get_nodeid (),
+				      root_resolved_node_id);
+      context->insert_receiver (expr.get_mappings ().get_hirid (), root);
+      return;
+    }
+
+  resolve_segments (root_resolved_node_id, expr.get_segments (), 1, infered,
+		    expr.get_mappings (), expr.get_locus ());
+}
+
+void
+TypeCheckExpr::visit (HIR::PathInExpression &expr)
+{
+  NodeId resolved_node_id = UNKNOWN_NODEID;
+  size_t offset = -1;
+  TyTy::BaseType *tyseg = resolve_root_path (expr, &offset, &resolved_node_id);
+  if (tyseg->get_kind () == TyTy::TypeKind::ERROR)
+    return;
+
+  if (tyseg->needs_generic_substitutions ())
+    {
+      tyseg = SubstMapper::InferSubst (tyseg, expr.get_locus ());
+    }
+
+  bool fully_resolved = offset == expr.get_segments ().size ();
+  if (fully_resolved)
+    {
+      infered = tyseg;
+      return;
+    }
+
+  resolve_segments (resolved_node_id, expr.get_segments (), offset, tyseg,
+		    expr.get_mappings (), expr.get_locus ());
+}
+
+TyTy::BaseType *
+TypeCheckExpr::resolve_root_path (HIR::PathInExpression &expr, size_t *offset,
+				  NodeId *root_resolved_node_id)
+{
+  TyTy::BaseType *root_tyty = nullptr;
+  *offset = 0;
+  for (size_t i = 0; i < expr.get_num_segments (); i++)
+    {
+      HIR::PathExprSegment &seg = expr.get_segments ().at (i);
+
+      bool have_more_segments = (expr.get_num_segments () - 1 != i);
+      bool is_root = *offset == 0;
+      NodeId ast_node_id = seg.get_mappings ().get_nodeid ();
+
+      // then lookup the reference_node_id
+      NodeId ref_node_id = UNKNOWN_NODEID;
+      if (!resolver->lookup_resolved_name (ast_node_id, &ref_node_id))
+	{
+	  resolver->lookup_resolved_type (ast_node_id, &ref_node_id);
+	}
+
+      // ref_node_id is the NodeId that the segments refers to.
+      if (ref_node_id == UNKNOWN_NODEID)
+	{
+	  if (root_tyty != nullptr && *offset > 0)
+	    {
+	      // then we can let the impl path probe take over now
+	      return root_tyty;
+	    }
+
+	  rust_error_at (seg.get_locus (),
+			 "failed to type resolve root segment");
+	  return new TyTy::ErrorType (expr.get_mappings ().get_hirid ());
+	}
+
+      // node back to HIR
+      HirId ref;
+      if (!mappings->lookup_node_to_hir (ref_node_id, &ref))
+	{
+	  rust_error_at (seg.get_locus (), "456 reverse lookup failure");
+	  rust_debug_loc (seg.get_locus (),
+			  "failure with [%s] mappings [%s] ref_node_id [%u]",
+			  seg.as_string ().c_str (),
+			  seg.get_mappings ().as_string ().c_str (),
+			  ref_node_id);
+
+	  return new TyTy::ErrorType (expr.get_mappings ().get_hirid ());
+	}
+
+      auto seg_is_module = (nullptr != mappings->lookup_module (ref));
+      auto seg_is_crate = mappings->is_local_hirid_crate (ref);
+      if (seg_is_module || seg_is_crate)
+	{
+	  // A::B::C::this_is_a_module::D::E::F
+	  //          ^^^^^^^^^^^^^^^^
+	  //          Currently handling this.
+	  if (have_more_segments)
+	    {
+	      (*offset)++;
+	      continue;
+	    }
+
+	  // In the case of :
+	  // A::B::C::this_is_a_module
+	  //          ^^^^^^^^^^^^^^^^
+	  // This is an error, we are not expecting a module.
+	  rust_error_at (seg.get_locus (), "expected value");
+	  return new TyTy::ErrorType (expr.get_mappings ().get_hirid ());
+	}
+
+      TyTy::BaseType *lookup = nullptr;
+      if (!context->lookup_type (ref, &lookup))
+	{
+	  if (is_root)
+	    {
+	      rust_error_at (seg.get_locus (),
+			     "failed to resolve root segment");
+	      return new TyTy::ErrorType (expr.get_mappings ().get_hirid ());
+	    }
+	  return root_tyty;
+	}
+
+      // if we have a previous segment type
+      if (root_tyty != nullptr)
+	{
+	  // if this next segment needs substitution we must apply the
+	  // previous type arguments
+	  //
+	  // such as: GenericStruct::<_>::new(123, 456)
+	  if (lookup->needs_generic_substitutions ())
+	    {
+	      if (!root_tyty->needs_generic_substitutions ())
+		{
+		  auto used_args_in_prev_segment
+		    = GetUsedSubstArgs::From (root_tyty);
+		  lookup
+		    = SubstMapperInternal::Resolve (lookup,
+						    used_args_in_prev_segment);
+		}
+	    }
+	}
+
+      // turbo-fish segment path::<ty>
+      if (seg.has_generic_args ())
+	{
+	  if (!lookup->can_substitute ())
+	    {
+	      rust_error_at (expr.get_locus (),
+			     "substitutions not supported for %s",
+			     root_tyty->as_string ().c_str ());
+	      return new TyTy::ErrorType (expr.get_mappings ().get_hirid ());
+	    }
+
+	  lookup = SubstMapper::Resolve (lookup, expr.get_locus (),
+					 &seg.get_generic_args ());
+	  if (lookup->get_kind () == TyTy::TypeKind::ERROR)
+	    return new TyTy::ErrorType (expr.get_mappings ().get_hirid ());
+	}
+
+      *root_resolved_node_id = ref_node_id;
+      *offset = *offset + 1;
+      root_tyty = lookup;
+    }
+
+  return root_tyty;
+}
+
+void
+TypeCheckExpr::resolve_segments (NodeId root_resolved_node_id,
+				 std::vector<HIR::PathExprSegment> &segments,
+				 size_t offset, TyTy::BaseType *tyseg,
+				 const Analysis::NodeMapping &expr_mappings,
+				 Location expr_locus)
+{
+  NodeId resolved_node_id = root_resolved_node_id;
+  TyTy::BaseType *prev_segment = tyseg;
+  bool reciever_is_generic = prev_segment->get_kind () == TyTy::TypeKind::PARAM;
+
+  for (size_t i = offset; i < segments.size (); i++)
+    {
+      HIR::PathExprSegment &seg = segments.at (i);
+
+      bool probe_bounds = true;
+      bool probe_impls = !reciever_is_generic;
+      bool ignore_mandatory_trait_items = !reciever_is_generic;
+
+      // probe the path is done in two parts one where we search impls if no
+      // candidate is found then we search extensions from traits
+      auto candidates
+	= PathProbeType::Probe (prev_segment, seg.get_segment (), probe_impls,
+				false, ignore_mandatory_trait_items);
+      if (candidates.size () == 0)
+	{
+	  candidates
+	    = PathProbeType::Probe (prev_segment, seg.get_segment (), false,
+				    probe_bounds, ignore_mandatory_trait_items);
+
+	  if (candidates.size () == 0)
+	    {
+	      rust_error_at (
+		seg.get_locus (),
+		"failed to resolve path segment using an impl Probe");
+	      return;
+	    }
+	}
+
+      if (candidates.size () > 1)
+	{
+	  ReportMultipleCandidateError::Report (candidates, seg.get_segment (),
+						seg.get_locus ());
+	  return;
+	}
+
+      auto &candidate = candidates.at (0);
+      prev_segment = tyseg;
+      tyseg = candidate.ty;
+
+      HIR::ImplBlock *associated_impl_block = nullptr;
+      if (candidate.is_enum_candidate ())
+	{
+	  const TyTy::VariantDef *variant = candidate.item.enum_field.variant;
+
+	  HirId variant_id = variant->get_id ();
+	  HIR::Item *enum_item = mappings->lookup_hir_item (variant_id);
+	  rust_assert (enum_item != nullptr);
+
+	  resolved_node_id = enum_item->get_mappings ().get_nodeid ();
+
+	  // insert the id of the variant we are resolved to
+	  context->insert_variant_definition (expr_mappings.get_hirid (),
+					      variant_id);
+	}
+      else if (candidate.is_impl_candidate ())
+	{
+	  resolved_node_id
+	    = candidate.item.impl.impl_item->get_impl_mappings ().get_nodeid ();
+
+	  associated_impl_block = candidate.item.impl.parent;
+	}
+      else
+	{
+	  resolved_node_id
+	    = candidate.item.trait.item_ref->get_mappings ().get_nodeid ();
+
+	  // lookup the associated-impl-trait
+	  HIR::ImplBlock *impl = candidate.item.trait.impl;
+	  if (impl != nullptr)
+	    {
+	      // get the associated impl block
+	      associated_impl_block = impl;
+	    }
+	}
+
+      if (associated_impl_block != nullptr)
+	{
+	  // get the type of the parent Self
+	  HirId impl_ty_id
+	    = associated_impl_block->get_type ()->get_mappings ().get_hirid ();
+	  TyTy::BaseType *impl_block_ty = nullptr;
+	  bool ok = context->lookup_type (impl_ty_id, &impl_block_ty);
+	  rust_assert (ok);
+
+	  if (impl_block_ty->needs_generic_substitutions ())
+	    impl_block_ty
+	      = SubstMapper::InferSubst (impl_block_ty, seg.get_locus ());
+
+	  prev_segment = prev_segment->unify (impl_block_ty);
+	}
+
+      if (tyseg->needs_generic_substitutions ())
+	{
+	  if (!prev_segment->needs_generic_substitutions ())
+	    {
+	      auto used_args_in_prev_segment
+		= GetUsedSubstArgs::From (prev_segment);
+
+	      if (!used_args_in_prev_segment.is_error ())
+		{
+		  if (SubstMapperInternal::mappings_are_bound (
+			tyseg, used_args_in_prev_segment))
+		    {
+		      tyseg = SubstMapperInternal::Resolve (
+			tyseg, used_args_in_prev_segment);
+		    }
+		}
+	    }
+	}
+
+      if (seg.has_generic_args ())
+	{
+	  if (!tyseg->can_substitute ())
+	    {
+	      rust_error_at (expr_locus, "substitutions not supported for %s",
+			     tyseg->as_string ().c_str ());
+	      return;
+	    }
+
+	  tyseg = SubstMapper::Resolve (tyseg, expr_locus,
+					&seg.get_generic_args ());
+	  if (tyseg->get_kind () == TyTy::TypeKind::ERROR)
+	    return;
+	}
+      else if (tyseg->needs_generic_substitutions () && !reciever_is_generic)
+	{
+	  Location locus = seg.get_locus ();
+	  tyseg = SubstMapper::InferSubst (tyseg, locus);
+	  if (tyseg->get_kind () == TyTy::TypeKind::ERROR)
+	    return;
+	}
+    }
+
+  rust_assert (resolved_node_id != UNKNOWN_NODEID);
+  if (tyseg->needs_generic_substitutions () && !reciever_is_generic)
+    {
+      Location locus = segments.back ().get_locus ();
+      tyseg = SubstMapper::InferSubst (tyseg, locus);
+      if (tyseg->get_kind () == TyTy::TypeKind::ERROR)
+	return;
+    }
+
+  context->insert_receiver (expr_mappings.get_hirid (), prev_segment);
+
+  // name scope first
+  if (resolver->get_name_scope ().decl_was_declared_here (resolved_node_id))
+    {
+      resolver->insert_resolved_name (expr_mappings.get_nodeid (),
+				      resolved_node_id);
+    }
+  // check the type scope
+  else if (resolver->get_type_scope ().decl_was_declared_here (
+	     resolved_node_id))
+    {
+      resolver->insert_resolved_type (expr_mappings.get_nodeid (),
+				      resolved_node_id);
+    }
+
+  infered = tyseg;
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-hir-type-check-pattern.cc b/gcc/rust/typecheck/rust-hir-type-check-pattern.cc
new file mode 100644
index 00000000000..429511d0292
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-pattern.cc
@@ -0,0 +1,416 @@
+// 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/>.
+
+#include "rust-hir-type-check-pattern.h"
+#include "rust-hir-type-check-expr.h"
+
+namespace Rust {
+namespace Resolver {
+
+TypeCheckPattern::TypeCheckPattern (TyTy::BaseType *parent)
+  : TypeCheckBase (), parent (parent), infered (nullptr)
+{}
+
+TyTy::BaseType *
+TypeCheckPattern::Resolve (HIR::Pattern *pattern, TyTy::BaseType *parent)
+{
+  TypeCheckPattern resolver (parent);
+  pattern->accept_vis (resolver);
+
+  if (resolver.infered == nullptr)
+    return new TyTy::ErrorType (pattern->get_pattern_mappings ().get_hirid ());
+
+  resolver.context->insert_type (pattern->get_pattern_mappings (),
+				 resolver.infered);
+  return resolver.infered;
+}
+
+void
+TypeCheckPattern::visit (HIR::PathInExpression &pattern)
+{
+  infered = TypeCheckExpr::Resolve (&pattern);
+}
+
+void
+TypeCheckPattern::visit (HIR::TupleStructPattern &pattern)
+{
+  infered = TypeCheckExpr::Resolve (&pattern.get_path ());
+  if (infered->get_kind () == TyTy::TypeKind::ERROR)
+    return;
+
+  rust_assert (infered->get_kind () == TyTy::TypeKind::ADT);
+  TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (infered);
+  rust_assert (adt->number_of_variants () > 0);
+
+  TyTy::VariantDef *variant = adt->get_variants ().at (0);
+  if (adt->is_enum ())
+    {
+      HirId variant_id = UNKNOWN_HIRID;
+      bool ok = context->lookup_variant_definition (
+	pattern.get_path ().get_mappings ().get_hirid (), &variant_id);
+      rust_assert (ok);
+
+      ok = adt->lookup_variant_by_id (variant_id, &variant);
+      rust_assert (ok);
+    }
+
+  // error[E0532]: expected tuple struct or tuple variant, found struct variant
+  // `Foo::D`
+  if (variant->get_variant_type () != TyTy::VariantDef::VariantType::TUPLE)
+    {
+      std::string variant_type
+	= TyTy::VariantDef::variant_type_string (variant->get_variant_type ());
+
+      rust_error_at (
+	pattern.get_locus (),
+	"expected tuple struct or tuple variant, found %s variant %<%s::%s%>",
+	variant_type.c_str (), adt->get_name ().c_str (),
+	variant->get_identifier ().c_str ());
+      return;
+    }
+
+  // check the elements
+  // error[E0023]: this pattern has 2 fields, but the corresponding tuple
+  // variant has 1 field
+  // error[E0023]: this pattern has 0 fields, but the corresponding tuple
+  // variant has 1 field
+
+  std::unique_ptr<HIR::TupleStructItems> &items = pattern.get_items ();
+  switch (items->get_item_type ())
+    {
+      case HIR::TupleStructItems::RANGE: {
+	// TODO
+	gcc_unreachable ();
+      }
+      break;
+
+      case HIR::TupleStructItems::NO_RANGE: {
+	HIR::TupleStructItemsNoRange &items_no_range
+	  = static_cast<HIR::TupleStructItemsNoRange &> (*items.get ());
+
+	if (items_no_range.get_patterns ().size () != variant->num_fields ())
+	  {
+	    rust_error_at (
+	      pattern.get_locus (),
+	      "this pattern has %lu fields but the corresponding "
+	      "tuple variant has %lu field",
+	      (unsigned long) items_no_range.get_patterns ().size (),
+	      (unsigned long) variant->num_fields ());
+	    // we continue on to try and setup the types as best we can for
+	    // type checking
+	  }
+
+	// iterate the fields and set them up, I wish we had ZIP
+	size_t i = 0;
+	for (auto &pattern : items_no_range.get_patterns ())
+	  {
+	    if (i >= variant->num_fields ())
+	      break;
+
+	    TyTy::StructFieldType *field = variant->get_field_at_index (i++);
+	    TyTy::BaseType *fty = field->get_field_type ();
+
+	    // setup the type on this pattern type
+	    context->insert_type (pattern->get_pattern_mappings (), fty);
+	  }
+      }
+      break;
+    }
+}
+
+void
+TypeCheckPattern::visit (HIR::StructPattern &pattern)
+{
+  infered = TypeCheckExpr::Resolve (&pattern.get_path ());
+  if (infered->get_kind () == TyTy::TypeKind::ERROR)
+    return;
+
+  rust_assert (infered->get_kind () == TyTy::TypeKind::ADT);
+  TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (infered);
+  rust_assert (adt->number_of_variants () > 0);
+
+  TyTy::VariantDef *variant = adt->get_variants ().at (0);
+  if (adt->is_enum ())
+    {
+      HirId variant_id = UNKNOWN_HIRID;
+      bool ok = context->lookup_variant_definition (
+	pattern.get_path ().get_mappings ().get_hirid (), &variant_id);
+      rust_assert (ok);
+
+      ok = adt->lookup_variant_by_id (variant_id, &variant);
+      rust_assert (ok);
+    }
+
+  // error[E0532]: expected tuple struct or tuple variant, found struct variant
+  // `Foo::D`
+  if (variant->get_variant_type () != TyTy::VariantDef::VariantType::STRUCT)
+    {
+      std::string variant_type
+	= TyTy::VariantDef::variant_type_string (variant->get_variant_type ());
+      rust_error_at (pattern.get_locus (),
+		     "expected struct variant, found %s variant %s",
+		     variant_type.c_str (),
+		     variant->get_identifier ().c_str ());
+      return;
+    }
+
+  // check the elements
+  // error[E0027]: pattern does not mention fields `x`, `y`
+  // error[E0026]: variant `Foo::D` does not have a field named `b`
+
+  std::vector<std::string> named_fields;
+  auto &struct_pattern_elems = pattern.get_struct_pattern_elems ();
+  for (auto &field : struct_pattern_elems.get_struct_pattern_fields ())
+    {
+      switch (field->get_item_type ())
+	{
+	  case HIR::StructPatternField::ItemType::TUPLE_PAT: {
+	    // TODO
+	    gcc_unreachable ();
+	  }
+	  break;
+
+	  case HIR::StructPatternField::ItemType::IDENT_PAT: {
+	    // TODO
+	    gcc_unreachable ();
+	  }
+	  break;
+
+	  case HIR::StructPatternField::ItemType::IDENT: {
+	    HIR::StructPatternFieldIdent &ident
+	      = static_cast<HIR::StructPatternFieldIdent &> (*field.get ());
+
+	    TyTy::StructFieldType *field = nullptr;
+	    if (!variant->lookup_field (ident.get_identifier (), &field,
+					nullptr))
+	      {
+		rust_error_at (ident.get_locus (),
+			       "variant %s does not have a field named %s",
+			       variant->get_identifier ().c_str (),
+			       ident.get_identifier ().c_str ());
+		break;
+	      }
+	    named_fields.push_back (ident.get_identifier ());
+
+	    // setup the type on this pattern
+	    TyTy::BaseType *fty = field->get_field_type ();
+	    context->insert_type (ident.get_mappings (), fty);
+	  }
+	  break;
+	}
+    }
+
+  if (named_fields.size () != variant->num_fields ())
+    {
+      std::map<std::string, bool> missing_names;
+
+      // populate with all fields
+      for (auto &field : variant->get_fields ())
+	missing_names[field->get_name ()] = true;
+
+      // then eliminate with named_fields
+      for (auto &named : named_fields)
+	missing_names.erase (named);
+
+      // then get the list of missing names
+      size_t i = 0;
+      std::string missing_fields_str;
+      for (auto it = missing_names.begin (); it != missing_names.end (); it++)
+	{
+	  bool has_next = (i + 1) < missing_names.size ();
+	  missing_fields_str += it->first + (has_next ? ", " : "");
+	  i++;
+	}
+
+      rust_error_at (pattern.get_locus (), "pattern does not mention fields %s",
+		     missing_fields_str.c_str ());
+    }
+}
+
+void
+TypeCheckPattern::visit (HIR::WildcardPattern &pattern)
+{
+  // wildcard patterns within the MatchArm's are simply just the same type as
+  // the parent
+  infered = parent->clone ();
+  infered->set_ref (pattern.get_pattern_mappings ().get_hirid ());
+}
+
+void
+TypeCheckPattern::visit (HIR::TuplePattern &pattern)
+{
+  std::unique_ptr<HIR::TuplePatternItems> items;
+  switch (pattern.get_items ()->get_pattern_type ())
+    {
+      case HIR::TuplePatternItems::TuplePatternItemType::MULTIPLE: {
+	HIR::TuplePatternItemsMultiple &ref
+	  = *static_cast<HIR::TuplePatternItemsMultiple *> (
+	    pattern.get_items ().get ());
+
+	std::vector<TyTy::TyVar> pattern_elems;
+	for (size_t i = 0; i < ref.get_patterns ().size (); i++)
+	  {
+	    auto &p = ref.get_patterns ()[i];
+	    TyTy::BaseType *par_type = parent;
+	    if (parent->get_kind () == TyTy::TUPLE)
+	      {
+		TyTy::TupleType &par = *static_cast<TyTy::TupleType *> (parent);
+		par_type = par.get_field (i);
+	      }
+
+	    TyTy::BaseType *elem
+	      = TypeCheckPattern::Resolve (p.get (), par_type);
+	    pattern_elems.push_back (TyTy::TyVar (elem->get_ref ()));
+	  }
+	infered
+	  = new TyTy::TupleType (pattern.get_pattern_mappings ().get_hirid (),
+				 pattern.get_locus (), pattern_elems);
+      }
+      break;
+
+      case HIR::TuplePatternItems::TuplePatternItemType::RANGED: {
+	// HIR::TuplePatternItemsRanged &ref
+	//   = *static_cast<HIR::TuplePatternItemsRanged *> (
+	//     pattern.get_items ().get ());
+	// TODO
+	gcc_unreachable ();
+      }
+      break;
+    }
+}
+
+void
+TypeCheckPattern::visit (HIR::LiteralPattern &pattern)
+{
+  infered = resolve_literal (pattern.get_pattern_mappings (),
+			     pattern.get_literal (), pattern.get_locus ());
+}
+
+void
+TypeCheckPattern::visit (HIR::RangePattern &pattern)
+{
+  // Resolve the upper and lower bounds, and ensure they are compatible types
+  TyTy::BaseType *upper = nullptr, *lower = nullptr;
+
+  // TODO: It would be nice to factor this out into a helper since the logic for
+  // both bounds is exactly the same...
+  switch (pattern.get_upper_bound ()->get_bound_type ())
+    {
+      case HIR::RangePatternBound::RangePatternBoundType::LITERAL: {
+	HIR::RangePatternBoundLiteral &ref
+	  = *static_cast<HIR::RangePatternBoundLiteral *> (
+	    pattern.get_upper_bound ().get ());
+
+	HIR::Literal lit = ref.get_literal ();
+
+	upper = resolve_literal (pattern.get_pattern_mappings (), lit,
+				 pattern.get_locus ());
+      }
+      break;
+
+      case HIR::RangePatternBound::RangePatternBoundType::PATH: {
+	HIR::RangePatternBoundPath &ref
+	  = *static_cast<HIR::RangePatternBoundPath *> (
+	    pattern.get_upper_bound ().get ());
+
+	upper = TypeCheckExpr::Resolve (&ref.get_path ());
+      }
+      break;
+
+      case HIR::RangePatternBound::RangePatternBoundType::QUALPATH: {
+	HIR::RangePatternBoundQualPath &ref
+	  = *static_cast<HIR::RangePatternBoundQualPath *> (
+	    pattern.get_upper_bound ().get ());
+
+	upper = TypeCheckExpr::Resolve (&ref.get_qualified_path ());
+      }
+      break;
+    }
+
+  switch (pattern.get_lower_bound ()->get_bound_type ())
+    {
+      case HIR::RangePatternBound::RangePatternBoundType::LITERAL: {
+	HIR::RangePatternBoundLiteral &ref
+	  = *static_cast<HIR::RangePatternBoundLiteral *> (
+	    pattern.get_lower_bound ().get ());
+
+	HIR::Literal lit = ref.get_literal ();
+
+	lower = resolve_literal (pattern.get_pattern_mappings (), lit,
+				 pattern.get_locus ());
+      }
+      break;
+
+      case HIR::RangePatternBound::RangePatternBoundType::PATH: {
+	HIR::RangePatternBoundPath &ref
+	  = *static_cast<HIR::RangePatternBoundPath *> (
+	    pattern.get_lower_bound ().get ());
+
+	lower = TypeCheckExpr::Resolve (&ref.get_path ());
+      }
+      break;
+
+      case HIR::RangePatternBound::RangePatternBoundType::QUALPATH: {
+	HIR::RangePatternBoundQualPath &ref
+	  = *static_cast<HIR::RangePatternBoundQualPath *> (
+	    pattern.get_lower_bound ().get ());
+
+	lower = TypeCheckExpr::Resolve (&ref.get_qualified_path ());
+      }
+      break;
+    }
+
+  infered = upper->unify (lower);
+}
+
+void
+TypeCheckPattern::visit (HIR::IdentifierPattern &pattern)
+{
+  infered = parent;
+}
+
+void
+TypeCheckPattern::visit (HIR::GroupedPattern &pattern)
+{
+  // TODO
+  gcc_unreachable ();
+}
+
+void
+TypeCheckPattern::visit (HIR::QualifiedPathInExpression &pattern)
+{
+  // TODO
+  gcc_unreachable ();
+}
+
+void
+TypeCheckPattern::visit (HIR::ReferencePattern &pattern)
+{
+  // TODO
+  gcc_unreachable ();
+}
+
+void
+TypeCheckPattern::visit (HIR::SlicePattern &pattern)
+{
+  // TODO
+  gcc_unreachable ();
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-hir-type-check-pattern.h b/gcc/rust/typecheck/rust-hir-type-check-pattern.h
new file mode 100644
index 00000000000..8af106033b7
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-pattern.h
@@ -0,0 +1,62 @@
+// 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_HIR_TYPE_CHECK_PATTERN
+#define RUST_HIR_TYPE_CHECK_PATTERN
+
+#include "rust-hir-type-check-base.h"
+#include "rust-hir-full.h"
+
+namespace Rust {
+namespace Resolver {
+
+class TypeCheckPattern : public TypeCheckBase, public HIR::HIRPatternVisitor
+{
+public:
+  static TyTy::BaseType *Resolve (HIR::Pattern *pattern,
+				  TyTy::BaseType *parent);
+
+  void visit (HIR::PathInExpression &pattern) override;
+  void visit (HIR::StructPattern &pattern) override;
+  void visit (HIR::TupleStructPattern &pattern) override;
+  void visit (HIR::WildcardPattern &pattern) override;
+  void visit (HIR::TuplePattern &pattern) override;
+  void visit (HIR::LiteralPattern &pattern) override;
+  void visit (HIR::RangePattern &pattern) override;
+  void visit (HIR::IdentifierPattern &pattern) override;
+  void visit (HIR::GroupedPattern &pattern) override;
+  void visit (HIR::QualifiedPathInExpression &pattern) override;
+  void visit (HIR::ReferencePattern &pattern) override;
+  void visit (HIR::SlicePattern &pattern) override;
+
+private:
+  TypeCheckPattern (TyTy::BaseType *parent);
+
+  static TyTy::BaseType *
+  typecheck_range_pattern_bound (HIR::RangePatternBound *bound,
+				 Analysis::NodeMapping mappings,
+				 Location locus);
+
+  TyTy::BaseType *parent;
+  TyTy::BaseType *infered;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_HIR_TYPE_CHECK_PATTERN
diff --git a/gcc/rust/typecheck/rust-hir-type-check-stmt.cc b/gcc/rust/typecheck/rust-hir-type-check-stmt.cc
new file mode 100644
index 00000000000..9f34ed49165
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-stmt.cc
@@ -0,0 +1,498 @@
+// 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/>.
+
+#include "rust-hir-type-check-stmt.h"
+#include "rust-hir-full.h"
+#include "rust-hir-type-check-type.h"
+#include "rust-hir-type-check-expr.h"
+#include "rust-hir-type-check-enumitem.h"
+#include "rust-hir-type-check-implitem.h"
+#include "rust-hir-type-check-pattern.h"
+
+namespace Rust {
+namespace Resolver {
+
+TyTy::BaseType *
+TypeCheckStmt::Resolve (HIR::Stmt *stmt)
+{
+  TypeCheckStmt resolver;
+  stmt->accept_vis (resolver);
+  return resolver.infered;
+}
+
+void
+TypeCheckStmt::visit (HIR::ExprStmtWithBlock &stmt)
+{
+  infered = TypeCheckExpr::Resolve (stmt.get_expr ());
+}
+
+void
+TypeCheckStmt::visit (HIR::ExprStmtWithoutBlock &stmt)
+{
+  infered = TypeCheckExpr::Resolve (stmt.get_expr ());
+}
+
+void
+TypeCheckStmt::visit (HIR::EmptyStmt &stmt)
+{
+  infered = TyTy::TupleType::get_unit_type (stmt.get_mappings ().get_hirid ());
+}
+
+void
+TypeCheckStmt::visit (HIR::ExternBlock &extern_block)
+{
+  for (auto &item : extern_block.get_extern_items ())
+    {
+      TypeCheckTopLevelExternItem::Resolve (item.get (), extern_block);
+    }
+}
+
+void
+TypeCheckStmt::visit (HIR::ConstantItem &constant)
+{
+  TyTy::BaseType *type = TypeCheckType::Resolve (constant.get_type ());
+  TyTy::BaseType *expr_type = TypeCheckExpr::Resolve (constant.get_expr ());
+
+  infered = type->unify (expr_type);
+  context->insert_type (constant.get_mappings (), infered);
+}
+
+void
+TypeCheckStmt::visit (HIR::LetStmt &stmt)
+{
+  infered = TyTy::TupleType::get_unit_type (stmt.get_mappings ().get_hirid ());
+
+  const HIR::Pattern &stmt_pattern = *stmt.get_pattern ();
+  TyTy::BaseType *init_expr_ty = nullptr;
+  if (stmt.has_init_expr ())
+    {
+      init_expr_ty = TypeCheckExpr::Resolve (stmt.get_init_expr ());
+      if (init_expr_ty->get_kind () == TyTy::TypeKind::ERROR)
+	return;
+
+      init_expr_ty->append_reference (
+	stmt_pattern.get_pattern_mappings ().get_hirid ());
+    }
+
+  TyTy::BaseType *specified_ty = nullptr;
+  if (stmt.has_type ())
+    specified_ty = TypeCheckType::Resolve (stmt.get_type ());
+
+  // let x:i32 = 123;
+  if (specified_ty != nullptr && init_expr_ty != nullptr)
+    {
+      // FIXME use this result and look at the regressions
+      coercion_site (stmt.get_mappings ().get_hirid (), specified_ty,
+		     init_expr_ty, stmt.get_locus ());
+      context->insert_type (stmt_pattern.get_pattern_mappings (), specified_ty);
+    }
+  else
+    {
+      // let x:i32;
+      if (specified_ty != nullptr)
+	{
+	  context->insert_type (stmt_pattern.get_pattern_mappings (),
+				specified_ty);
+	}
+      // let x = 123;
+      else if (init_expr_ty != nullptr)
+	{
+	  context->insert_type (stmt_pattern.get_pattern_mappings (),
+				init_expr_ty);
+	}
+      // let x;
+      else
+	{
+	  context->insert_type (
+	    stmt_pattern.get_pattern_mappings (),
+	    new TyTy::InferType (
+	      stmt_pattern.get_pattern_mappings ().get_hirid (),
+	      TyTy::InferType::InferTypeKind::GENERAL, stmt.get_locus ()));
+	}
+    }
+}
+
+void
+TypeCheckStmt::visit (HIR::TupleStruct &struct_decl)
+{
+  std::vector<TyTy::SubstitutionParamMapping> substitutions;
+  if (struct_decl.has_generics ())
+    {
+      for (auto &generic_param : struct_decl.get_generic_params ())
+	{
+	  switch (generic_param.get ()->get_kind ())
+	    {
+	    case HIR::GenericParam::GenericKind::LIFETIME:
+	    case HIR::GenericParam::GenericKind::CONST:
+	      // FIXME: Skipping Lifetime and Const completely until better
+	      // handling.
+	      break;
+
+	      case HIR::GenericParam::GenericKind::TYPE: {
+		auto param_type
+		  = TypeResolveGenericParam::Resolve (generic_param.get ());
+		context->insert_type (generic_param->get_mappings (),
+				      param_type);
+
+		substitutions.push_back (TyTy::SubstitutionParamMapping (
+		  static_cast<HIR::TypeParam &> (*generic_param), param_type));
+	      }
+	      break;
+	    }
+	}
+    }
+
+  std::vector<TyTy::StructFieldType *> fields;
+  size_t idx = 0;
+  for (auto &field : struct_decl.get_fields ())
+    {
+      TyTy::BaseType *field_type
+	= TypeCheckType::Resolve (field.get_field_type ().get ());
+      TyTy::StructFieldType *ty_field
+	= new TyTy::StructFieldType (field.get_mappings ().get_hirid (),
+				     std::to_string (idx), field_type);
+      fields.push_back (ty_field);
+      context->insert_type (field.get_mappings (), ty_field->get_field_type ());
+      idx++;
+    }
+
+  // get the path
+  const CanonicalPath *canonical_path = nullptr;
+  bool ok = mappings->lookup_canonical_path (
+    struct_decl.get_mappings ().get_nodeid (), &canonical_path);
+  rust_assert (ok);
+  RustIdent ident{*canonical_path, struct_decl.get_locus ()};
+
+  // there is only a single variant
+  std::vector<TyTy::VariantDef *> variants;
+  variants.push_back (new TyTy::VariantDef (
+    struct_decl.get_mappings ().get_hirid (), struct_decl.get_identifier (),
+    ident, TyTy::VariantDef::VariantType::TUPLE, nullptr, std::move (fields)));
+
+  // Process #[repr(...)] attribute, if any
+  const AST::AttrVec &attrs = struct_decl.get_outer_attrs ();
+  TyTy::ADTType::ReprOptions repr
+    = parse_repr_options (attrs, struct_decl.get_locus ());
+
+  TyTy::BaseType *type
+    = new TyTy::ADTType (struct_decl.get_mappings ().get_hirid (),
+			 mappings->get_next_hir_id (),
+			 struct_decl.get_identifier (), ident,
+			 TyTy::ADTType::ADTKind::TUPLE_STRUCT,
+			 std::move (variants), std::move (substitutions), repr);
+
+  context->insert_type (struct_decl.get_mappings (), type);
+  infered = type;
+}
+
+void
+TypeCheckStmt::visit (HIR::Enum &enum_decl)
+{
+  std::vector<TyTy::SubstitutionParamMapping> substitutions;
+  if (enum_decl.has_generics ())
+    {
+      for (auto &generic_param : enum_decl.get_generic_params ())
+	{
+	  switch (generic_param.get ()->get_kind ())
+	    {
+	    case HIR::GenericParam::GenericKind::LIFETIME:
+	    case HIR::GenericParam::GenericKind::CONST:
+	      // FIXME: Skipping Lifetime and Const completely until better
+	      // handling.
+	      break;
+
+	      case HIR::GenericParam::GenericKind::TYPE: {
+		auto param_type
+		  = TypeResolveGenericParam::Resolve (generic_param.get ());
+		context->insert_type (generic_param->get_mappings (),
+				      param_type);
+
+		substitutions.push_back (TyTy::SubstitutionParamMapping (
+		  static_cast<HIR::TypeParam &> (*generic_param), param_type));
+	      }
+	      break;
+	    }
+	}
+    }
+
+  std::vector<TyTy::VariantDef *> variants;
+  int64_t discriminant_value = 0;
+  for (auto &variant : enum_decl.get_variants ())
+    {
+      TyTy::VariantDef *field_type
+	= TypeCheckEnumItem::Resolve (variant.get (), discriminant_value);
+
+      discriminant_value++;
+      variants.push_back (field_type);
+    }
+
+  // get the path
+  const CanonicalPath *canonical_path = nullptr;
+  bool ok
+    = mappings->lookup_canonical_path (enum_decl.get_mappings ().get_nodeid (),
+				       &canonical_path);
+  rust_assert (ok);
+  RustIdent ident{*canonical_path, enum_decl.get_locus ()};
+
+  TyTy::BaseType *type
+    = new TyTy::ADTType (enum_decl.get_mappings ().get_hirid (),
+			 mappings->get_next_hir_id (),
+			 enum_decl.get_identifier (), ident,
+			 TyTy::ADTType::ADTKind::ENUM, std::move (variants),
+			 std::move (substitutions));
+
+  context->insert_type (enum_decl.get_mappings (), type);
+  infered = type;
+}
+
+void
+TypeCheckStmt::visit (HIR::StructStruct &struct_decl)
+{
+  std::vector<TyTy::SubstitutionParamMapping> substitutions;
+  if (struct_decl.has_generics ())
+    {
+      for (auto &generic_param : struct_decl.get_generic_params ())
+	{
+	  switch (generic_param.get ()->get_kind ())
+	    {
+	    case HIR::GenericParam::GenericKind::LIFETIME:
+	    case HIR::GenericParam::GenericKind::CONST:
+	      // FIXME: Skipping Lifetime and Const completely until better
+	      // handling.
+	      break;
+
+	      case HIR::GenericParam::GenericKind::TYPE: {
+		auto param_type
+		  = TypeResolveGenericParam::Resolve (generic_param.get ());
+		context->insert_type (generic_param->get_mappings (),
+				      param_type);
+
+		substitutions.push_back (TyTy::SubstitutionParamMapping (
+		  static_cast<HIR::TypeParam &> (*generic_param), param_type));
+	      }
+	      break;
+	    }
+	}
+    }
+
+  std::vector<TyTy::StructFieldType *> fields;
+  for (auto &field : struct_decl.get_fields ())
+    {
+      TyTy::BaseType *field_type
+	= TypeCheckType::Resolve (field.get_field_type ().get ());
+      TyTy::StructFieldType *ty_field
+	= new TyTy::StructFieldType (field.get_mappings ().get_hirid (),
+				     field.get_field_name (), field_type);
+      fields.push_back (ty_field);
+      context->insert_type (field.get_mappings (), ty_field->get_field_type ());
+    }
+
+  // get the path
+  const CanonicalPath *canonical_path = nullptr;
+  bool ok = mappings->lookup_canonical_path (
+    struct_decl.get_mappings ().get_nodeid (), &canonical_path);
+  rust_assert (ok);
+  RustIdent ident{*canonical_path, struct_decl.get_locus ()};
+
+  // there is only a single variant
+  std::vector<TyTy::VariantDef *> variants;
+  variants.push_back (new TyTy::VariantDef (
+    struct_decl.get_mappings ().get_hirid (), struct_decl.get_identifier (),
+    ident, TyTy::VariantDef::VariantType::STRUCT, nullptr, std::move (fields)));
+
+  // Process #[repr(...)] attribute, if any
+  const AST::AttrVec &attrs = struct_decl.get_outer_attrs ();
+  TyTy::ADTType::ReprOptions repr
+    = parse_repr_options (attrs, struct_decl.get_locus ());
+
+  TyTy::BaseType *type
+    = new TyTy::ADTType (struct_decl.get_mappings ().get_hirid (),
+			 mappings->get_next_hir_id (),
+			 struct_decl.get_identifier (), ident,
+			 TyTy::ADTType::ADTKind::STRUCT_STRUCT,
+			 std::move (variants), std::move (substitutions), repr);
+
+  context->insert_type (struct_decl.get_mappings (), type);
+  infered = type;
+}
+
+void
+TypeCheckStmt::visit (HIR::Union &union_decl)
+{
+  std::vector<TyTy::SubstitutionParamMapping> substitutions;
+  if (union_decl.has_generics ())
+    {
+      for (auto &generic_param : union_decl.get_generic_params ())
+	{
+	  switch (generic_param.get ()->get_kind ())
+	    {
+	    case HIR::GenericParam::GenericKind::LIFETIME:
+	    case HIR::GenericParam::GenericKind::CONST:
+	      // FIXME: Skipping Lifetime and Const completely until better
+	      // handling.
+	      break;
+
+	      case HIR::GenericParam::GenericKind::TYPE: {
+		auto param_type
+		  = TypeResolveGenericParam::Resolve (generic_param.get ());
+		context->insert_type (generic_param->get_mappings (),
+				      param_type);
+
+		substitutions.push_back (TyTy::SubstitutionParamMapping (
+		  static_cast<HIR::TypeParam &> (*generic_param), param_type));
+	      }
+	      break;
+	    }
+	}
+    }
+
+  std::vector<TyTy::StructFieldType *> fields;
+  for (auto &variant : union_decl.get_variants ())
+    {
+      TyTy::BaseType *variant_type
+	= TypeCheckType::Resolve (variant.get_field_type ().get ());
+      TyTy::StructFieldType *ty_variant
+	= new TyTy::StructFieldType (variant.get_mappings ().get_hirid (),
+				     variant.get_field_name (), variant_type);
+      fields.push_back (ty_variant);
+      context->insert_type (variant.get_mappings (),
+			    ty_variant->get_field_type ());
+    }
+
+  // get the path
+  const CanonicalPath *canonical_path = nullptr;
+  bool ok
+    = mappings->lookup_canonical_path (union_decl.get_mappings ().get_nodeid (),
+				       &canonical_path);
+  rust_assert (ok);
+  RustIdent ident{*canonical_path, union_decl.get_locus ()};
+
+  // there is only a single variant
+  std::vector<TyTy::VariantDef *> variants;
+  variants.push_back (new TyTy::VariantDef (
+    union_decl.get_mappings ().get_hirid (), union_decl.get_identifier (),
+    ident, TyTy::VariantDef::VariantType::STRUCT, nullptr, std::move (fields)));
+
+  TyTy::BaseType *type
+    = new TyTy::ADTType (union_decl.get_mappings ().get_hirid (),
+			 mappings->get_next_hir_id (),
+			 union_decl.get_identifier (), ident,
+			 TyTy::ADTType::ADTKind::UNION, std::move (variants),
+			 std::move (substitutions));
+
+  context->insert_type (union_decl.get_mappings (), type);
+  infered = type;
+}
+
+void
+TypeCheckStmt::visit (HIR::Function &function)
+{
+  std::vector<TyTy::SubstitutionParamMapping> substitutions;
+  if (function.has_generics ())
+    {
+      for (auto &generic_param : function.get_generic_params ())
+	{
+	  switch (generic_param.get ()->get_kind ())
+	    {
+	    case HIR::GenericParam::GenericKind::LIFETIME:
+	    case HIR::GenericParam::GenericKind::CONST:
+	      // FIXME: Skipping Lifetime and Const completely until better
+	      // handling.
+	      break;
+
+	      case HIR::GenericParam::GenericKind::TYPE: {
+		auto param_type
+		  = TypeResolveGenericParam::Resolve (generic_param.get ());
+		context->insert_type (generic_param->get_mappings (),
+				      param_type);
+
+		substitutions.push_back (TyTy::SubstitutionParamMapping (
+		  static_cast<HIR::TypeParam &> (*generic_param), param_type));
+	      }
+	      break;
+	    }
+	}
+    }
+
+  TyTy::BaseType *ret_type = nullptr;
+  if (!function.has_function_return_type ())
+    ret_type
+      = TyTy::TupleType::get_unit_type (function.get_mappings ().get_hirid ());
+  else
+    {
+      auto resolved
+	= TypeCheckType::Resolve (function.get_return_type ().get ());
+      if (resolved == nullptr)
+	{
+	  rust_error_at (function.get_locus (),
+			 "failed to resolve return type");
+	  return;
+	}
+
+      ret_type = resolved->clone ();
+      ret_type->set_ref (
+	function.get_return_type ()->get_mappings ().get_hirid ());
+    }
+
+  std::vector<std::pair<HIR::Pattern *, TyTy::BaseType *> > params;
+  for (auto &param : function.get_function_params ())
+    {
+      // get the name as well required for later on
+      auto param_tyty = TypeCheckType::Resolve (param.get_type ());
+      params.push_back (
+	std::pair<HIR::Pattern *, TyTy::BaseType *> (param.get_param_name (),
+						     param_tyty));
+
+      context->insert_type (param.get_mappings (), param_tyty);
+      TypeCheckPattern::Resolve (param.get_param_name (), param_tyty);
+    }
+
+  // get the path
+  const CanonicalPath *canonical_path = nullptr;
+  bool ok
+    = mappings->lookup_canonical_path (function.get_mappings ().get_nodeid (),
+				       &canonical_path);
+  rust_assert (ok);
+
+  RustIdent ident{*canonical_path, function.get_locus ()};
+  auto fnType = new TyTy::FnType (function.get_mappings ().get_hirid (),
+				  function.get_mappings ().get_defid (),
+				  function.get_function_name (), ident,
+				  TyTy::FnType::FNTYPE_DEFAULT_FLAGS, ABI::RUST,
+				  std::move (params), ret_type,
+				  std::move (substitutions));
+  context->insert_type (function.get_mappings (), fnType);
+
+  TyTy::FnType *resolved_fn_type = fnType;
+  auto expected_ret_tyty = resolved_fn_type->get_return_type ();
+  context->push_return_type (TypeCheckContextItem (&function),
+			     expected_ret_tyty);
+
+  auto block_expr_ty
+    = TypeCheckExpr::Resolve (function.get_definition ().get ());
+
+  context->pop_return_type ();
+
+  if (block_expr_ty->get_kind () != TyTy::NEVER)
+    expected_ret_tyty->unify (block_expr_ty);
+
+  infered = fnType;
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-hir-type-check-stmt.h b/gcc/rust/typecheck/rust-hir-type-check-stmt.h
new file mode 100644
index 00000000000..a79f17a59ce
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-stmt.h
@@ -0,0 +1,96 @@
+// 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_HIR_TYPE_CHECK_STMT
+#define RUST_HIR_TYPE_CHECK_STMT
+
+#include "rust-hir-type-check-base.h"
+
+namespace Rust {
+namespace Resolver {
+
+class TypeCheckStmt : private TypeCheckBase, private HIR::HIRStmtVisitor
+{
+public:
+  static TyTy::BaseType *Resolve (HIR::Stmt *stmt);
+
+  void visit (HIR::ExprStmtWithBlock &stmt) override;
+  void visit (HIR::ExprStmtWithoutBlock &stmt) override;
+  void visit (HIR::EmptyStmt &stmt) override;
+  void visit (HIR::ExternBlock &extern_block) override;
+  void visit (HIR::ConstantItem &constant) override;
+  void visit (HIR::LetStmt &stmt) override;
+  void visit (HIR::TupleStruct &struct_decl) override;
+  void visit (HIR::Enum &enum_decl) override;
+  void visit (HIR::StructStruct &struct_decl) override;
+  void visit (HIR::Union &union_decl) override;
+  void visit (HIR::Function &function) override;
+
+  void visit (HIR::EnumItemTuple &) override
+  { /* TODO? */
+  }
+  void visit (HIR::EnumItemStruct &) override
+  { /* TODO? */
+  }
+  void visit (HIR::EnumItem &item) override
+  { /* TODO? */
+  }
+  void visit (HIR::EnumItemDiscriminant &) override
+  { /* TODO? */
+  }
+  void visit (HIR::TypePathSegmentFunction &segment) override
+  { /* TODO? */
+  }
+  void visit (HIR::TypePath &path) override
+  { /* TODO? */
+  }
+  void visit (HIR::QualifiedPathInType &path) override
+  { /* TODO? */
+  }
+  void visit (HIR::Module &module) override
+  { /* TODO? */
+  }
+  void visit (HIR::ExternCrate &crate) override
+  { /* TODO? */
+  }
+  void visit (HIR::UseDeclaration &use_decl) override
+  { /* TODO? */
+  }
+  void visit (HIR::TypeAlias &type_alias) override
+  { /* TODO? */
+  }
+  void visit (HIR::StaticItem &static_item) override
+  { /* TODO? */
+  }
+  void visit (HIR::Trait &trait) override
+  { /* TODO? */
+  }
+  void visit (HIR::ImplBlock &impl) override
+  { /* TODO? */
+  }
+
+private:
+  TypeCheckStmt () : TypeCheckBase (), infered (nullptr) {}
+
+  TyTy::BaseType *infered;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_HIR_TYPE_CHECK_STMT
diff --git a/gcc/rust/typecheck/rust-hir-type-check-struct-field.h b/gcc/rust/typecheck/rust-hir-type-check-struct-field.h
new file mode 100644
index 00000000000..22af1aad4c3
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-struct-field.h
@@ -0,0 +1,59 @@
+// 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_HIR_TYPE_CHECK_STRUCT_FIELD
+#define RUST_HIR_TYPE_CHECK_STRUCT_FIELD
+
+#include "rust-hir-type-check-base.h"
+#include "rust-hir-full.h"
+#include "rust-hir-type-check-type.h"
+#include "rust-tyty.h"
+
+namespace Rust {
+namespace Resolver {
+
+class TypeCheckStructExpr : public TypeCheckBase
+{
+public:
+  static TyTy::BaseType *Resolve (HIR::StructExprStructFields *expr);
+
+protected:
+  void resolve (HIR::StructExprStructFields &struct_expr);
+
+  void visit (HIR::StructExprFieldIdentifierValue &field);
+  void visit (HIR::StructExprFieldIndexValue &field);
+  void visit (HIR::StructExprFieldIdentifier &field);
+
+private:
+  TypeCheckStructExpr (HIR::Expr *e);
+
+  // result
+  TyTy::BaseType *resolved;
+
+  // internal state:
+  TyTy::ADTType *struct_path_resolved;
+  TyTy::VariantDef *variant;
+  TyTy::BaseType *resolved_field_value_expr;
+  std::set<std::string> fields_assigned;
+  std::map<size_t, HIR::StructExprField *> adtFieldIndexToField;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_HIR_TYPE_CHECK_STRUCT_FIELD
diff --git a/gcc/rust/typecheck/rust-hir-type-check-struct.cc b/gcc/rust/typecheck/rust-hir-type-check-struct.cc
new file mode 100644
index 00000000000..b2261e8cdb3
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-struct.cc
@@ -0,0 +1,340 @@
+// 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/>.
+
+#include "rust-hir-type-check.h"
+#include "rust-hir-full.h"
+#include "rust-hir-type-check-expr.h"
+#include "rust-hir-type-check-struct-field.h"
+
+namespace Rust {
+namespace Resolver {
+
+TypeCheckStructExpr::TypeCheckStructExpr (HIR::Expr *e)
+  : TypeCheckBase (),
+    resolved (new TyTy::ErrorType (e->get_mappings ().get_hirid ())),
+    struct_path_resolved (nullptr),
+    variant (&TyTy::VariantDef::get_error_node ())
+{}
+
+TyTy::BaseType *
+TypeCheckStructExpr::Resolve (HIR::StructExprStructFields *expr)
+{
+  TypeCheckStructExpr resolver (expr);
+  resolver.resolve (*expr);
+  return resolver.resolved;
+}
+
+void
+TypeCheckStructExpr::resolve (HIR::StructExprStructFields &struct_expr)
+{
+  TyTy::BaseType *struct_path_ty
+    = TypeCheckExpr::Resolve (&struct_expr.get_struct_name ());
+  if (struct_path_ty->get_kind () != TyTy::TypeKind::ADT)
+    {
+      rust_error_at (struct_expr.get_struct_name ().get_locus (),
+		     "expected an ADT type for constructor");
+      return;
+    }
+
+  struct_path_resolved = static_cast<TyTy::ADTType *> (struct_path_ty);
+  TyTy::ADTType *struct_def = struct_path_resolved;
+  if (struct_expr.has_struct_base ())
+    {
+      TyTy::BaseType *base_resolved
+	= TypeCheckExpr::Resolve (struct_expr.struct_base->base_struct.get ());
+      struct_def = static_cast<TyTy::ADTType *> (
+	struct_path_resolved->unify (base_resolved));
+      if (struct_def == nullptr)
+	{
+	  rust_fatal_error (struct_expr.struct_base->base_struct->get_locus (),
+			    "incompatible types for base struct reference");
+	  return;
+	}
+    }
+
+  // figure out the variant
+  if (struct_path_resolved->is_enum ())
+    {
+      // lookup variant id
+      HirId variant_id;
+      bool ok = context->lookup_variant_definition (
+	struct_expr.get_struct_name ().get_mappings ().get_hirid (),
+	&variant_id);
+      rust_assert (ok);
+
+      ok = struct_path_resolved->lookup_variant_by_id (variant_id, &variant);
+      rust_assert (ok);
+    }
+  else
+    {
+      rust_assert (struct_path_resolved->number_of_variants () == 1);
+      variant = struct_path_resolved->get_variants ().at (0);
+    }
+
+  std::vector<TyTy::StructFieldType *> infered_fields;
+  bool ok = true;
+
+  for (auto &field : struct_expr.get_fields ())
+    {
+      resolved_field_value_expr = nullptr;
+
+      switch (field->get_kind ())
+	{
+	case HIR::StructExprField::StructExprFieldKind::IDENTIFIER:
+	  visit (static_cast<HIR::StructExprFieldIdentifier &> (*field.get ()));
+	  break;
+
+	case HIR::StructExprField::StructExprFieldKind::IDENTIFIER_VALUE:
+	  visit (
+	    static_cast<HIR::StructExprFieldIdentifierValue &> (*field.get ()));
+	  break;
+
+	case HIR::StructExprField::StructExprFieldKind::INDEX_VALUE:
+	  visit (static_cast<HIR::StructExprFieldIndexValue &> (*field.get ()));
+	  break;
+	}
+
+      if (resolved_field_value_expr == nullptr)
+	{
+	  rust_fatal_error (field->get_locus (),
+			    "failed to resolve type for field");
+	  ok = false;
+	  break;
+	}
+
+      context->insert_type (field->get_mappings (), resolved_field_value_expr);
+    }
+
+  // something failed setting up the fields
+  if (!ok)
+    {
+      rust_error_at (struct_expr.get_locus (),
+		     "constructor type resolution failure");
+      return;
+    }
+
+  // check the arguments are all assigned and fix up the ordering
+  if (fields_assigned.size () != variant->num_fields ())
+    {
+      if (struct_def->is_union ())
+	{
+	  if (fields_assigned.size () != 1 || struct_expr.has_struct_base ())
+	    {
+	      rust_error_at (
+		struct_expr.get_locus (),
+		"union must have exactly one field variant assigned");
+	      return;
+	    }
+	}
+      else if (!struct_expr.has_struct_base ())
+	{
+	  rust_error_at (struct_expr.get_locus (),
+			 "constructor is missing fields");
+	  return;
+	}
+      else
+	{
+	  // we have a struct base to assign the missing fields from.
+	  // the missing fields can be implicit FieldAccessExprs for the value
+	  std::set<std::string> missing_fields;
+	  for (auto &field : variant->get_fields ())
+	    {
+	      auto it = fields_assigned.find (field->get_name ());
+	      if (it == fields_assigned.end ())
+		missing_fields.insert (field->get_name ());
+	    }
+
+	  // we can generate FieldAccessExpr or TupleAccessExpr for the
+	  // values of the missing fields.
+	  for (auto &missing : missing_fields)
+	    {
+	      HIR::Expr *receiver
+		= struct_expr.struct_base->base_struct->clone_expr_impl ();
+
+	      HIR::StructExprField *implicit_field = nullptr;
+
+	      AST::AttrVec outer_attribs;
+	      auto crate_num = mappings->get_current_crate ();
+	      Analysis::NodeMapping mapping (
+		crate_num,
+		struct_expr.struct_base->base_struct->get_mappings ()
+		  .get_nodeid (),
+		mappings->get_next_hir_id (crate_num), UNKNOWN_LOCAL_DEFID);
+
+	      HIR::Expr *field_value = new HIR::FieldAccessExpr (
+		mapping, std::unique_ptr<HIR::Expr> (receiver), missing,
+		std::move (outer_attribs),
+		struct_expr.struct_base->base_struct->get_locus ());
+
+	      implicit_field = new HIR::StructExprFieldIdentifierValue (
+		mapping, missing, std::unique_ptr<HIR::Expr> (field_value),
+		struct_expr.struct_base->base_struct->get_locus ());
+
+	      size_t field_index;
+	      bool ok = variant->lookup_field (missing, nullptr, &field_index);
+	      rust_assert (ok);
+
+	      adtFieldIndexToField[field_index] = implicit_field;
+	      struct_expr.get_fields ().push_back (
+		std::unique_ptr<HIR::StructExprField> (implicit_field));
+	    }
+	}
+    }
+
+  if (struct_def->is_union ())
+    {
+      // There is exactly one field in this constructor, we need to
+      // figure out the field index to make sure we initialize the
+      // right union field.
+      for (size_t i = 0; i < adtFieldIndexToField.size (); i++)
+	{
+	  if (adtFieldIndexToField[i])
+	    {
+	      struct_expr.union_index = i;
+	      break;
+	    }
+	}
+      rust_assert (struct_expr.union_index != -1);
+    }
+  else
+    {
+      // everything is ok, now we need to ensure all field values are ordered
+      // correctly. The GIMPLE backend uses a simple algorithm that assumes each
+      // assigned field in the constructor is in the same order as the field in
+      // the type
+      for (auto &field : struct_expr.get_fields ())
+	field.release ();
+
+      std::vector<std::unique_ptr<HIR::StructExprField> > ordered_fields;
+      for (size_t i = 0; i < adtFieldIndexToField.size (); i++)
+	{
+	  ordered_fields.push_back (
+	    std::unique_ptr<HIR::StructExprField> (adtFieldIndexToField[i]));
+	}
+      struct_expr.set_fields_as_owner (std::move (ordered_fields));
+    }
+
+  resolved = struct_def;
+}
+
+void
+TypeCheckStructExpr::visit (HIR::StructExprFieldIdentifierValue &field)
+{
+  auto it = fields_assigned.find (field.field_name);
+  if (it != fields_assigned.end ())
+    {
+      rust_fatal_error (field.get_locus (), "used more than once");
+      return;
+    }
+
+  size_t field_index;
+  TyTy::StructFieldType *field_type;
+  bool ok = variant->lookup_field (field.field_name, &field_type, &field_index);
+  if (!ok)
+    {
+      rust_error_at (field.get_locus (), "unknown field");
+      return;
+    }
+
+  TyTy::BaseType *value = TypeCheckExpr::Resolve (field.get_value ());
+  resolved_field_value_expr
+    = coercion_site (field.get_mappings ().get_hirid (),
+		     field_type->get_field_type (), value, field.get_locus ());
+  if (resolved_field_value_expr != nullptr)
+    {
+      fields_assigned.insert (field.field_name);
+      adtFieldIndexToField[field_index] = &field;
+    }
+}
+
+void
+TypeCheckStructExpr::visit (HIR::StructExprFieldIndexValue &field)
+{
+  std::string field_name (std::to_string (field.get_tuple_index ()));
+  auto it = fields_assigned.find (field_name);
+  if (it != fields_assigned.end ())
+    {
+      rust_fatal_error (field.get_locus (), "used more than once");
+      return;
+    }
+
+  size_t field_index;
+  TyTy::StructFieldType *field_type;
+  bool ok = variant->lookup_field (field_name, &field_type, &field_index);
+  if (!ok)
+    {
+      rust_error_at (field.get_locus (), "unknown field");
+      return;
+    }
+
+  TyTy::BaseType *value = TypeCheckExpr::Resolve (field.get_value ());
+  resolved_field_value_expr
+    = coercion_site (field.get_mappings ().get_hirid (),
+		     field_type->get_field_type (), value, field.get_locus ());
+  if (resolved_field_value_expr != nullptr)
+    {
+      fields_assigned.insert (field_name);
+      adtFieldIndexToField[field_index] = &field;
+    }
+}
+
+void
+TypeCheckStructExpr::visit (HIR::StructExprFieldIdentifier &field)
+{
+  auto it = fields_assigned.find (field.get_field_name ());
+  if (it != fields_assigned.end ())
+    {
+      rust_fatal_error (field.get_locus (), "used more than once");
+      return;
+    }
+
+  size_t field_index;
+  TyTy::StructFieldType *field_type;
+  bool ok = variant->lookup_field (field.get_field_name (), &field_type,
+				   &field_index);
+  if (!ok)
+    {
+      rust_error_at (field.get_locus (), "unknown field");
+      return;
+    }
+
+  // we can make the field look like a path expr to take advantage of existing
+  // code
+  Analysis::NodeMapping mappings_copy1 = field.get_mappings ();
+  Analysis::NodeMapping mappings_copy2 = field.get_mappings ();
+
+  HIR::PathIdentSegment ident_seg (field.get_field_name ());
+  HIR::PathExprSegment seg (mappings_copy1, ident_seg, field.get_locus (),
+			    HIR::GenericArgs::create_empty ());
+  HIR::PathInExpression expr (mappings_copy2, {seg}, field.get_locus (), false,
+			      {});
+  TyTy::BaseType *value = TypeCheckExpr::Resolve (&expr);
+
+  resolved_field_value_expr
+    = coercion_site (field.get_mappings ().get_hirid (),
+		     field_type->get_field_type (), value, field.get_locus ());
+  if (resolved_field_value_expr != nullptr)
+
+    {
+      fields_assigned.insert (field.get_field_name ());
+      adtFieldIndexToField[field_index] = &field;
+    }
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-hir-type-check-toplevel.cc b/gcc/rust/typecheck/rust-hir-type-check-toplevel.cc
new file mode 100644
index 00000000000..27f36b642fc
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-toplevel.cc
@@ -0,0 +1,364 @@
+// 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/>.
+
+#include "rust-hir-type-check-toplevel.h"
+#include "rust-hir-type-check-enumitem.h"
+#include "rust-hir-type-check-type.h"
+#include "rust-hir-type-check-expr.h"
+#include "rust-hir-type-check-pattern.h"
+#include "rust-hir-type-check-implitem.h"
+
+namespace Rust {
+namespace Resolver {
+
+TypeCheckTopLevel::TypeCheckTopLevel () : TypeCheckBase () {}
+
+void
+TypeCheckTopLevel::Resolve (HIR::Item &item)
+{
+  rust_assert (item.get_hir_kind () == HIR::Node::BaseKind::VIS_ITEM);
+  HIR::VisItem &vis_item = static_cast<HIR::VisItem &> (item);
+
+  TypeCheckTopLevel resolver;
+  vis_item.accept_vis (resolver);
+}
+
+void
+TypeCheckTopLevel::visit (HIR::TypeAlias &alias)
+{
+  TyTy::BaseType *actual_type
+    = TypeCheckType::Resolve (alias.get_type_aliased ().get ());
+
+  context->insert_type (alias.get_mappings (), actual_type);
+
+  for (auto &where_clause_item : alias.get_where_clause ().get_items ())
+    {
+      ResolveWhereClauseItem::Resolve (*where_clause_item.get ());
+    }
+}
+
+void
+TypeCheckTopLevel::visit (HIR::TupleStruct &struct_decl)
+{
+  std::vector<TyTy::SubstitutionParamMapping> substitutions;
+  if (struct_decl.has_generics ())
+    resolve_generic_params (struct_decl.get_generic_params (), substitutions);
+
+  for (auto &where_clause_item : struct_decl.get_where_clause ().get_items ())
+    {
+      ResolveWhereClauseItem::Resolve (*where_clause_item.get ());
+    }
+
+  std::vector<TyTy::StructFieldType *> fields;
+  size_t idx = 0;
+  for (auto &field : struct_decl.get_fields ())
+    {
+      TyTy::BaseType *field_type
+	= TypeCheckType::Resolve (field.get_field_type ().get ());
+      TyTy::StructFieldType *ty_field
+	= new TyTy::StructFieldType (field.get_mappings ().get_hirid (),
+				     std::to_string (idx), field_type);
+      fields.push_back (ty_field);
+      context->insert_type (field.get_mappings (), ty_field->get_field_type ());
+      idx++;
+    }
+
+  // get the path
+  const CanonicalPath *canonical_path = nullptr;
+  bool ok = mappings->lookup_canonical_path (
+    struct_decl.get_mappings ().get_nodeid (), &canonical_path);
+  rust_assert (ok);
+  RustIdent ident{*canonical_path, struct_decl.get_locus ()};
+
+  // its a single variant ADT
+  std::vector<TyTy::VariantDef *> variants;
+  variants.push_back (new TyTy::VariantDef (
+    struct_decl.get_mappings ().get_hirid (), struct_decl.get_identifier (),
+    ident, TyTy::VariantDef::VariantType::TUPLE, nullptr, std::move (fields)));
+
+  // Process #[repr(X)] attribute, if any
+  const AST::AttrVec &attrs = struct_decl.get_outer_attrs ();
+  TyTy::ADTType::ReprOptions repr
+    = parse_repr_options (attrs, struct_decl.get_locus ());
+
+  TyTy::BaseType *type
+    = new TyTy::ADTType (struct_decl.get_mappings ().get_hirid (),
+			 mappings->get_next_hir_id (),
+			 struct_decl.get_identifier (), ident,
+			 TyTy::ADTType::ADTKind::TUPLE_STRUCT,
+			 std::move (variants), std::move (substitutions), repr);
+
+  context->insert_type (struct_decl.get_mappings (), type);
+}
+
+void
+TypeCheckTopLevel::visit (HIR::Module &module)
+{
+  for (auto &item : module.get_items ())
+    TypeCheckTopLevel::Resolve (*item.get ());
+}
+
+void
+TypeCheckTopLevel::visit (HIR::StructStruct &struct_decl)
+{
+  std::vector<TyTy::SubstitutionParamMapping> substitutions;
+  if (struct_decl.has_generics ())
+    resolve_generic_params (struct_decl.get_generic_params (), substitutions);
+
+  for (auto &where_clause_item : struct_decl.get_where_clause ().get_items ())
+    {
+      ResolveWhereClauseItem::Resolve (*where_clause_item.get ());
+    }
+
+  std::vector<TyTy::StructFieldType *> fields;
+  for (auto &field : struct_decl.get_fields ())
+    {
+      TyTy::BaseType *field_type
+	= TypeCheckType::Resolve (field.get_field_type ().get ());
+      TyTy::StructFieldType *ty_field
+	= new TyTy::StructFieldType (field.get_mappings ().get_hirid (),
+				     field.get_field_name (), field_type);
+      fields.push_back (ty_field);
+      context->insert_type (field.get_mappings (), ty_field->get_field_type ());
+    }
+
+  // get the path
+  const CanonicalPath *canonical_path = nullptr;
+  bool ok = mappings->lookup_canonical_path (
+    struct_decl.get_mappings ().get_nodeid (), &canonical_path);
+  rust_assert (ok);
+  RustIdent ident{*canonical_path, struct_decl.get_locus ()};
+
+  // its a single variant ADT
+  std::vector<TyTy::VariantDef *> variants;
+  variants.push_back (new TyTy::VariantDef (
+    struct_decl.get_mappings ().get_hirid (), struct_decl.get_identifier (),
+    ident, TyTy::VariantDef::VariantType::STRUCT, nullptr, std::move (fields)));
+
+  // Process #[repr(X)] attribute, if any
+  const AST::AttrVec &attrs = struct_decl.get_outer_attrs ();
+  TyTy::ADTType::ReprOptions repr
+    = parse_repr_options (attrs, struct_decl.get_locus ());
+
+  TyTy::BaseType *type
+    = new TyTy::ADTType (struct_decl.get_mappings ().get_hirid (),
+			 mappings->get_next_hir_id (),
+			 struct_decl.get_identifier (), ident,
+			 TyTy::ADTType::ADTKind::STRUCT_STRUCT,
+			 std::move (variants), std::move (substitutions), repr);
+
+  context->insert_type (struct_decl.get_mappings (), type);
+}
+
+void
+TypeCheckTopLevel::visit (HIR::Enum &enum_decl)
+{
+  std::vector<TyTy::SubstitutionParamMapping> substitutions;
+  if (enum_decl.has_generics ())
+    resolve_generic_params (enum_decl.get_generic_params (), substitutions);
+
+  std::vector<TyTy::VariantDef *> variants;
+  int64_t discriminant_value = 0;
+  for (auto &variant : enum_decl.get_variants ())
+    {
+      TyTy::VariantDef *field_type
+	= TypeCheckEnumItem::Resolve (variant.get (), discriminant_value);
+
+      discriminant_value++;
+      variants.push_back (field_type);
+    }
+
+  // get the path
+  const CanonicalPath *canonical_path = nullptr;
+  bool ok
+    = mappings->lookup_canonical_path (enum_decl.get_mappings ().get_nodeid (),
+				       &canonical_path);
+  rust_assert (ok);
+  RustIdent ident{*canonical_path, enum_decl.get_locus ()};
+
+  // multi variant ADT
+  TyTy::BaseType *type
+    = new TyTy::ADTType (enum_decl.get_mappings ().get_hirid (),
+			 mappings->get_next_hir_id (),
+			 enum_decl.get_identifier (), ident,
+			 TyTy::ADTType::ADTKind::ENUM, std::move (variants),
+			 std::move (substitutions));
+
+  context->insert_type (enum_decl.get_mappings (), type);
+}
+
+void
+TypeCheckTopLevel::visit (HIR::Union &union_decl)
+{
+  std::vector<TyTy::SubstitutionParamMapping> substitutions;
+  if (union_decl.has_generics ())
+    resolve_generic_params (union_decl.get_generic_params (), substitutions);
+
+  for (auto &where_clause_item : union_decl.get_where_clause ().get_items ())
+    {
+      ResolveWhereClauseItem::Resolve (*where_clause_item.get ());
+    }
+
+  std::vector<TyTy::StructFieldType *> fields;
+  for (auto &variant : union_decl.get_variants ())
+    {
+      TyTy::BaseType *variant_type
+	= TypeCheckType::Resolve (variant.get_field_type ().get ());
+      TyTy::StructFieldType *ty_variant
+	= new TyTy::StructFieldType (variant.get_mappings ().get_hirid (),
+				     variant.get_field_name (), variant_type);
+      fields.push_back (ty_variant);
+      context->insert_type (variant.get_mappings (),
+			    ty_variant->get_field_type ());
+    }
+
+  // get the path
+  const CanonicalPath *canonical_path = nullptr;
+  bool ok
+    = mappings->lookup_canonical_path (union_decl.get_mappings ().get_nodeid (),
+				       &canonical_path);
+  rust_assert (ok);
+  RustIdent ident{*canonical_path, union_decl.get_locus ()};
+
+  // there is only a single variant
+  std::vector<TyTy::VariantDef *> variants;
+  variants.push_back (new TyTy::VariantDef (
+    union_decl.get_mappings ().get_hirid (), union_decl.get_identifier (),
+    ident, TyTy::VariantDef::VariantType::STRUCT, nullptr, std::move (fields)));
+
+  TyTy::BaseType *type
+    = new TyTy::ADTType (union_decl.get_mappings ().get_hirid (),
+			 mappings->get_next_hir_id (),
+			 union_decl.get_identifier (), ident,
+			 TyTy::ADTType::ADTKind::UNION, std::move (variants),
+			 std::move (substitutions));
+
+  context->insert_type (union_decl.get_mappings (), type);
+}
+
+void
+TypeCheckTopLevel::visit (HIR::StaticItem &var)
+{
+  TyTy::BaseType *type = TypeCheckType::Resolve (var.get_type ());
+  TyTy::BaseType *expr_type = TypeCheckExpr::Resolve (var.get_expr ());
+
+  context->insert_type (var.get_mappings (), type->unify (expr_type));
+}
+
+void
+TypeCheckTopLevel::visit (HIR::ConstantItem &constant)
+{
+  TyTy::BaseType *type = TypeCheckType::Resolve (constant.get_type ());
+  TyTy::BaseType *expr_type = TypeCheckExpr::Resolve (constant.get_expr ());
+
+  context->insert_type (constant.get_mappings (), type->unify (expr_type));
+}
+
+void
+TypeCheckTopLevel::visit (HIR::Function &function)
+{
+  std::vector<TyTy::SubstitutionParamMapping> substitutions;
+  if (function.has_generics ())
+    resolve_generic_params (function.get_generic_params (), substitutions);
+
+  for (auto &where_clause_item : function.get_where_clause ().get_items ())
+    {
+      ResolveWhereClauseItem::Resolve (*where_clause_item.get ());
+    }
+
+  TyTy::BaseType *ret_type = nullptr;
+  if (!function.has_function_return_type ())
+    ret_type
+      = TyTy::TupleType::get_unit_type (function.get_mappings ().get_hirid ());
+  else
+    {
+      auto resolved
+	= TypeCheckType::Resolve (function.get_return_type ().get ());
+      if (resolved->get_kind () == TyTy::TypeKind::ERROR)
+	{
+	  rust_error_at (function.get_locus (),
+			 "failed to resolve return type");
+	  return;
+	}
+
+      ret_type = resolved->clone ();
+      ret_type->set_ref (
+	function.get_return_type ()->get_mappings ().get_hirid ());
+    }
+
+  std::vector<std::pair<HIR::Pattern *, TyTy::BaseType *>> params;
+  for (auto &param : function.get_function_params ())
+    {
+      // get the name as well required for later on
+      auto param_tyty = TypeCheckType::Resolve (param.get_type ());
+      params.push_back (
+	std::pair<HIR::Pattern *, TyTy::BaseType *> (param.get_param_name (),
+						     param_tyty));
+
+      context->insert_type (param.get_mappings (), param_tyty);
+      TypeCheckPattern::Resolve (param.get_param_name (), param_tyty);
+    }
+
+  const CanonicalPath *canonical_path = nullptr;
+  bool ok
+    = mappings->lookup_canonical_path (function.get_mappings ().get_nodeid (),
+				       &canonical_path);
+  rust_assert (ok);
+
+  RustIdent ident{*canonical_path, function.get_locus ()};
+  auto fnType = new TyTy::FnType (function.get_mappings ().get_hirid (),
+				  function.get_mappings ().get_defid (),
+				  function.get_function_name (), ident,
+				  TyTy::FnType::FNTYPE_DEFAULT_FLAGS, ABI::RUST,
+				  std::move (params), ret_type,
+				  std::move (substitutions));
+
+  context->insert_type (function.get_mappings (), fnType);
+}
+
+void
+TypeCheckTopLevel::visit (HIR::ImplBlock &impl_block)
+{
+  std::vector<TyTy::SubstitutionParamMapping> substitutions;
+  if (impl_block.has_generics ())
+    resolve_generic_params (impl_block.get_generic_params (), substitutions);
+
+  for (auto &where_clause_item : impl_block.get_where_clause ().get_items ())
+    {
+      ResolveWhereClauseItem::Resolve (*where_clause_item.get ());
+    }
+
+  auto self = TypeCheckType::Resolve (impl_block.get_type ().get ());
+  if (self->get_kind () == TyTy::TypeKind::ERROR)
+    return;
+
+  for (auto &impl_item : impl_block.get_impl_items ())
+    TypeCheckTopLevelImplItem::Resolve (impl_item.get (), self, substitutions);
+}
+
+void
+TypeCheckTopLevel::visit (HIR::ExternBlock &extern_block)
+{
+  for (auto &item : extern_block.get_extern_items ())
+    {
+      TypeCheckTopLevelExternItem::Resolve (item.get (), extern_block);
+    }
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-hir-type-check-toplevel.h b/gcc/rust/typecheck/rust-hir-type-check-toplevel.h
new file mode 100644
index 00000000000..d0db07d7281
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-toplevel.h
@@ -0,0 +1,56 @@
+// 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_HIR_TYPE_CHECK_TOPLEVEL
+#define RUST_HIR_TYPE_CHECK_TOPLEVEL
+
+#include "rust-hir-type-check-base.h"
+
+namespace Rust {
+namespace Resolver {
+
+class TypeCheckTopLevel : private TypeCheckBase, public HIR::HIRVisItemVisitor
+{
+public:
+  static void Resolve (HIR::Item &item);
+
+  void visit (HIR::Module &module) override;
+  void visit (HIR::Function &function) override;
+  void visit (HIR::TypeAlias &alias) override;
+  void visit (HIR::TupleStruct &struct_decl) override;
+  void visit (HIR::StructStruct &struct_decl) override;
+  void visit (HIR::Enum &enum_decl) override;
+  void visit (HIR::Union &union_decl) override;
+  void visit (HIR::StaticItem &var) override;
+  void visit (HIR::ConstantItem &constant) override;
+  void visit (HIR::ImplBlock &impl_block) override;
+  void visit (HIR::ExternBlock &extern_block) override;
+
+  // nothing to do
+  void visit (HIR::Trait &trait_block) override {}
+  void visit (HIR::ExternCrate &crate) override {}
+  void visit (HIR::UseDeclaration &use_decl) override {}
+
+private:
+  TypeCheckTopLevel ();
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_HIR_TYPE_CHECK_TOPLEVEL
diff --git a/gcc/rust/typecheck/rust-hir-type-check-type.cc b/gcc/rust/typecheck/rust-hir-type-check-type.cc
new file mode 100644
index 00000000000..3538d77b220
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-type.cc
@@ -0,0 +1,838 @@
+// 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/>.
+
+#include "rust-hir-type-check-type.h"
+#include "rust-hir-trait-resolve.h"
+#include "rust-hir-type-check-expr.h"
+
+namespace Rust {
+namespace Resolver {
+
+HIR::GenericArgs
+TypeCheckResolveGenericArguments::resolve (HIR::TypePathSegment *segment)
+{
+  TypeCheckResolveGenericArguments resolver (segment->get_locus ());
+  switch (segment->get_type ())
+    {
+    case HIR::TypePathSegment::SegmentType::GENERIC:
+      resolver.visit (static_cast<HIR::TypePathSegmentGeneric &> (*segment));
+      break;
+
+    default:
+      break;
+    }
+  return resolver.args;
+}
+
+void
+TypeCheckResolveGenericArguments::visit (HIR::TypePathSegmentGeneric &generic)
+{
+  args = generic.get_generic_args ();
+}
+
+TyTy::BaseType *
+TypeCheckType::Resolve (HIR::Type *type)
+{
+  TypeCheckType resolver (type->get_mappings ().get_hirid ());
+  type->accept_vis (resolver);
+  rust_assert (resolver.translated != nullptr);
+  resolver.context->insert_type (type->get_mappings (), resolver.translated);
+  return resolver.translated;
+}
+
+void
+TypeCheckType::visit (HIR::BareFunctionType &fntype)
+{
+  TyTy::BaseType *return_type
+    = fntype.has_return_type ()
+	? TypeCheckType::Resolve (fntype.get_return_type ().get ())
+	: TyTy::TupleType::get_unit_type (fntype.get_mappings ().get_hirid ());
+
+  std::vector<TyTy::TyVar> params;
+  for (auto &param : fntype.get_function_params ())
+    {
+      TyTy::BaseType *ptype = TypeCheckType::Resolve (param.get_type ().get ());
+      params.push_back (TyTy::TyVar (ptype->get_ref ()));
+    }
+
+  translated = new TyTy::FnPtr (fntype.get_mappings ().get_hirid (),
+				fntype.get_locus (), std::move (params),
+				TyTy::TyVar (return_type->get_ref ()));
+}
+
+void
+TypeCheckType::visit (HIR::TupleType &tuple)
+{
+  if (tuple.is_unit_type ())
+    {
+      auto unit_node_id = resolver->get_unit_type_node_id ();
+      if (!context->lookup_builtin (unit_node_id, &translated))
+	{
+	  rust_error_at (tuple.get_locus (),
+			 "failed to lookup builtin unit type");
+	}
+      return;
+    }
+
+  std::vector<TyTy::TyVar> fields;
+  for (auto &elem : tuple.get_elems ())
+    {
+      auto field_ty = TypeCheckType::Resolve (elem.get ());
+      fields.push_back (TyTy::TyVar (field_ty->get_ref ()));
+    }
+
+  translated = new TyTy::TupleType (tuple.get_mappings ().get_hirid (),
+				    tuple.get_locus (), fields);
+}
+
+void
+TypeCheckType::visit (HIR::TypePath &path)
+{
+  // lookup the Node this resolves to
+  NodeId ref;
+  auto nid = path.get_mappings ().get_nodeid ();
+  bool is_fully_resolved = resolver->lookup_resolved_type (nid, &ref);
+
+  TyTy::BaseType *lookup = nullptr;
+  if (!is_fully_resolved)
+    {
+      // this can happen so we need to look up the root then resolve the
+      // remaining segments if possible
+      size_t offset = 0;
+      NodeId resolved_node_id = UNKNOWN_NODEID;
+      TyTy::BaseType *root
+	= resolve_root_path (path, &offset, &resolved_node_id);
+
+      rust_assert (root != nullptr);
+      if (root->get_kind () == TyTy::TypeKind::ERROR)
+	return;
+
+      translated
+	= resolve_segments (resolved_node_id, path.get_mappings ().get_hirid (),
+			    path.get_segments (), offset, root,
+			    path.get_mappings (), path.get_locus ());
+      return;
+    }
+
+  HirId hir_lookup;
+  if (!context->lookup_type_by_node_id (ref, &hir_lookup))
+    {
+      rust_error_at (path.get_locus (), "failed to lookup HIR %d for node '%s'",
+		     ref, path.as_string ().c_str ());
+      return;
+    }
+
+  if (!context->lookup_type (hir_lookup, &lookup))
+    {
+      rust_error_at (path.get_locus (), "failed to lookup HIR TyTy");
+      return;
+    }
+
+  TyTy::BaseType *path_type = lookup->clone ();
+  path_type->set_ref (path.get_mappings ().get_hirid ());
+
+  HIR::TypePathSegment *final_seg = path.get_final_segment ().get ();
+  HIR::GenericArgs args = TypeCheckResolveGenericArguments::resolve (final_seg);
+
+  bool is_big_self = final_seg->is_ident_only ()
+		     && (final_seg->as_string ().compare ("Self") == 0);
+
+  if (path_type->needs_generic_substitutions ())
+    {
+      if (is_big_self)
+	{
+	  translated = path_type;
+	  return;
+	}
+
+      translated = SubstMapper::Resolve (path_type, path.get_locus (), &args);
+    }
+  else if (!args.is_empty ())
+    {
+      rust_error_at (path.get_locus (),
+		     "TypePath %s declares generic arguments but "
+		     "the type %s does not have any",
+		     path.as_string ().c_str (),
+		     path_type->as_string ().c_str ());
+    }
+  else
+    {
+      translated = path_type;
+    }
+}
+
+void
+TypeCheckType::visit (HIR::QualifiedPathInType &path)
+{
+  HIR::QualifiedPathType qual_path_type = path.get_path_type ();
+  TyTy::BaseType *root
+    = TypeCheckType::Resolve (qual_path_type.get_type ().get ());
+  if (root->get_kind () == TyTy::TypeKind::ERROR)
+    {
+      rust_debug_loc (path.get_locus (), "failed to resolve the root");
+      return;
+    }
+
+  if (!qual_path_type.has_as_clause ())
+    {
+      // then this is just a normal path-in-expression
+      NodeId root_resolved_node_id = UNKNOWN_NODEID;
+      bool ok = resolver->lookup_resolved_type (
+	qual_path_type.get_type ()->get_mappings ().get_nodeid (),
+	&root_resolved_node_id);
+      rust_assert (ok);
+
+      translated = resolve_segments (root_resolved_node_id,
+				     path.get_mappings ().get_hirid (),
+				     path.get_segments (), 0, translated,
+				     path.get_mappings (), path.get_locus ());
+
+      return;
+    }
+
+  // Resolve the trait now
+  TraitReference *trait_ref
+    = TraitResolver::Resolve (*qual_path_type.get_trait ().get ());
+  if (trait_ref->is_error ())
+    return;
+
+  // does this type actually implement this type-bound?
+  if (!TypeBoundsProbe::is_bound_satisfied_for_type (root, trait_ref))
+    {
+      rust_error_at (qual_path_type.get_locus (),
+		     "root does not satisfy specified trait-bound");
+      return;
+    }
+
+  // get the predicate for the bound
+  auto specified_bound
+    = get_predicate_from_bound (*qual_path_type.get_trait ().get ());
+  if (specified_bound.is_error ())
+    return;
+
+  // inherit the bound
+  root->inherit_bounds ({specified_bound});
+
+  // setup the associated types
+  const TraitReference *specified_bound_ref = specified_bound.get ();
+  auto candidates = TypeBoundsProbe::Probe (root);
+  AssociatedImplTrait *associated_impl_trait = nullptr;
+  for (auto &probed_bound : candidates)
+    {
+      const TraitReference *bound_trait_ref = probed_bound.first;
+      const HIR::ImplBlock *associated_impl = probed_bound.second;
+
+      HirId impl_block_id = associated_impl->get_mappings ().get_hirid ();
+      AssociatedImplTrait *associated = nullptr;
+      bool found_impl_trait
+	= context->lookup_associated_trait_impl (impl_block_id, &associated);
+      if (found_impl_trait)
+	{
+	  bool found_trait = specified_bound_ref->is_equal (*bound_trait_ref);
+	  bool found_self = associated->get_self ()->can_eq (root, false);
+	  if (found_trait && found_self)
+	    {
+	      associated_impl_trait = associated;
+	      break;
+	    }
+	}
+    }
+
+  if (associated_impl_trait != nullptr)
+    {
+      associated_impl_trait->setup_associated_types (root, specified_bound);
+    }
+
+  // lookup the associated item from the specified bound
+  std::unique_ptr<HIR::TypePathSegment> &item_seg
+    = path.get_associated_segment ();
+  HIR::PathIdentSegment item_seg_identifier = item_seg->get_ident_segment ();
+  TyTy::TypeBoundPredicateItem item
+    = specified_bound.lookup_associated_item (item_seg_identifier.as_string ());
+  if (item.is_error ())
+    {
+      rust_error_at (item_seg->get_locus (), "unknown associated item");
+      return;
+    }
+
+  // infer the root type
+  translated = item.get_tyty_for_receiver (root);
+
+  // turbo-fish segment path::<ty>
+  if (item_seg->get_type () == HIR::TypePathSegment::SegmentType::GENERIC)
+    {
+      HIR::TypePathSegmentGeneric &generic_seg
+	= static_cast<HIR::TypePathSegmentGeneric &> (*item_seg.get ());
+
+      // turbo-fish segment path::<ty>
+      if (generic_seg.has_generic_args ())
+	{
+	  if (!translated->can_substitute ())
+	    {
+	      rust_error_at (item_seg->get_locus (),
+			     "substitutions not supported for %s",
+			     translated->as_string ().c_str ());
+	      translated
+		= new TyTy::ErrorType (path.get_mappings ().get_hirid ());
+	      return;
+	    }
+	  translated = SubstMapper::Resolve (translated, path.get_locus (),
+					     &generic_seg.get_generic_args ());
+	}
+    }
+
+  // continue on as a path-in-expression
+  const TraitItemReference *trait_item_ref = item.get_raw_item ();
+  NodeId root_resolved_node_id = trait_item_ref->get_mappings ().get_nodeid ();
+  bool fully_resolved = path.get_segments ().empty ();
+  if (fully_resolved)
+    {
+      resolver->insert_resolved_type (path.get_mappings ().get_nodeid (),
+				      root_resolved_node_id);
+      context->insert_receiver (path.get_mappings ().get_hirid (), root);
+      return;
+    }
+
+  translated
+    = resolve_segments (root_resolved_node_id,
+			path.get_mappings ().get_hirid (), path.get_segments (),
+			0, translated, path.get_mappings (), path.get_locus ());
+}
+
+TyTy::BaseType *
+TypeCheckType::resolve_root_path (HIR::TypePath &path, size_t *offset,
+				  NodeId *root_resolved_node_id)
+{
+  TyTy::BaseType *root_tyty = nullptr;
+  *offset = 0;
+  for (size_t i = 0; i < path.get_num_segments (); i++)
+    {
+      std::unique_ptr<HIR::TypePathSegment> &seg = path.get_segments ().at (i);
+
+      bool have_more_segments = (path.get_num_segments () - 1 != i);
+      bool is_root = *offset == 0;
+      NodeId ast_node_id = seg->get_mappings ().get_nodeid ();
+
+      // then lookup the reference_node_id
+      NodeId ref_node_id = UNKNOWN_NODEID;
+      if (!resolver->lookup_resolved_name (ast_node_id, &ref_node_id))
+	{
+	  resolver->lookup_resolved_type (ast_node_id, &ref_node_id);
+	}
+
+      // ref_node_id is the NodeId that the segments refers to.
+      if (ref_node_id == UNKNOWN_NODEID)
+	{
+	  if (is_root)
+	    {
+	      rust_error_at (seg->get_locus (),
+			     "unknown reference for resolved name: %<%s%>",
+			     seg->get_ident_segment ().as_string ().c_str ());
+	      return new TyTy::ErrorType (path.get_mappings ().get_hirid ());
+	    }
+	  return root_tyty;
+	}
+
+      // node back to HIR
+      HirId ref = UNKNOWN_HIRID;
+      if (!mappings->lookup_node_to_hir (ref_node_id, &ref))
+	{
+	  if (is_root)
+	    {
+	      rust_error_at (seg->get_locus (), "789 reverse lookup failure");
+	      rust_debug_loc (
+		seg->get_locus (),
+		"failure with [%s] mappings [%s] ref_node_id [%u]",
+		seg->as_string ().c_str (),
+		seg->get_mappings ().as_string ().c_str (), ref_node_id);
+
+	      return new TyTy::ErrorType (path.get_mappings ().get_hirid ());
+	    }
+
+	  return root_tyty;
+	}
+
+      auto seg_is_module = (nullptr != mappings->lookup_module (ref));
+      auto seg_is_crate = mappings->is_local_hirid_crate (ref);
+      if (seg_is_module || seg_is_crate)
+	{
+	  // A::B::C::this_is_a_module::D::E::F
+	  //          ^^^^^^^^^^^^^^^^
+	  //          Currently handling this.
+	  if (have_more_segments)
+	    {
+	      (*offset)++;
+	      continue;
+	    }
+
+	  // In the case of :
+	  // A::B::C::this_is_a_module
+	  //          ^^^^^^^^^^^^^^^^
+	  // This is an error, we are not expecting a module.
+	  rust_error_at (seg->get_locus (), "expected value");
+	  return new TyTy::ErrorType (path.get_mappings ().get_hirid ());
+	}
+
+      TyTy::BaseType *lookup = nullptr;
+      if (!context->lookup_type (ref, &lookup))
+	{
+	  if (is_root)
+	    {
+	      rust_error_at (seg->get_locus (),
+			     "failed to resolve root segment");
+	      return new TyTy::ErrorType (path.get_mappings ().get_hirid ());
+	    }
+	  return root_tyty;
+	}
+
+      // if we have a previous segment type
+      if (root_tyty != nullptr)
+	{
+	  // if this next segment needs substitution we must apply the
+	  // previous type arguments
+	  //
+	  // such as: GenericStruct::<_>::new(123, 456)
+	  if (lookup->needs_generic_substitutions ())
+	    {
+	      if (!root_tyty->needs_generic_substitutions ())
+		{
+		  auto used_args_in_prev_segment
+		    = GetUsedSubstArgs::From (root_tyty);
+		  lookup
+		    = SubstMapperInternal::Resolve (lookup,
+						    used_args_in_prev_segment);
+		}
+	    }
+	}
+
+      // turbo-fish segment path::<ty>
+      if (seg->is_generic_segment ())
+	{
+	  HIR::TypePathSegmentGeneric *generic_segment
+	    = static_cast<HIR::TypePathSegmentGeneric *> (seg.get ());
+
+	  if (!lookup->can_substitute ())
+	    {
+	      rust_error_at (seg->get_locus (),
+			     "substitutions not supported for %s",
+			     lookup->as_string ().c_str ());
+	      return new TyTy::ErrorType (lookup->get_ref ());
+	    }
+	  lookup = SubstMapper::Resolve (lookup, path.get_locus (),
+					 &generic_segment->get_generic_args ());
+	}
+
+      *root_resolved_node_id = ref_node_id;
+      *offset = *offset + 1;
+      root_tyty = lookup;
+    }
+
+  return root_tyty;
+}
+
+TyTy::BaseType *
+TypeCheckType::resolve_segments (
+  NodeId root_resolved_node_id, HirId expr_id,
+  std::vector<std::unique_ptr<HIR::TypePathSegment>> &segments, size_t offset,
+  TyTy::BaseType *tyseg, const Analysis::NodeMapping &expr_mappings,
+  Location expr_locus)
+{
+  NodeId resolved_node_id = root_resolved_node_id;
+  TyTy::BaseType *prev_segment = tyseg;
+  for (size_t i = offset; i < segments.size (); i++)
+    {
+      std::unique_ptr<HIR::TypePathSegment> &seg = segments.at (i);
+
+      bool reciever_is_generic
+	= prev_segment->get_kind () == TyTy::TypeKind::PARAM;
+      bool probe_bounds = true;
+      bool probe_impls = !reciever_is_generic;
+      bool ignore_mandatory_trait_items = !reciever_is_generic;
+
+      // probe the path is done in two parts one where we search impls if no
+      // candidate is found then we search extensions from traits
+      auto candidates
+	= PathProbeType::Probe (prev_segment, seg->get_ident_segment (),
+				probe_impls, false,
+				ignore_mandatory_trait_items);
+      if (candidates.size () == 0)
+	{
+	  candidates
+	    = PathProbeType::Probe (prev_segment, seg->get_ident_segment (),
+				    false, probe_bounds,
+				    ignore_mandatory_trait_items);
+
+	  if (candidates.size () == 0)
+	    {
+	      rust_error_at (
+		seg->get_locus (),
+		"failed to resolve path segment using an impl Probe");
+	      return new TyTy::ErrorType (expr_id);
+	    }
+	}
+
+      if (candidates.size () > 1)
+	{
+	  ReportMultipleCandidateError::Report (candidates,
+						seg->get_ident_segment (),
+						seg->get_locus ());
+	  return new TyTy::ErrorType (expr_id);
+	}
+
+      auto &candidate = candidates.at (0);
+      prev_segment = tyseg;
+      tyseg = candidate.ty;
+
+      if (candidate.is_impl_candidate ())
+	{
+	  resolved_node_id
+	    = candidate.item.impl.impl_item->get_impl_mappings ().get_nodeid ();
+	}
+      else
+	{
+	  resolved_node_id
+	    = candidate.item.trait.item_ref->get_mappings ().get_nodeid ();
+	}
+
+      if (seg->is_generic_segment ())
+	{
+	  HIR::TypePathSegmentGeneric *generic_segment
+	    = static_cast<HIR::TypePathSegmentGeneric *> (seg.get ());
+
+	  if (!tyseg->can_substitute ())
+	    {
+	      rust_error_at (expr_locus, "substitutions not supported for %s",
+			     tyseg->as_string ().c_str ());
+	      return new TyTy::ErrorType (expr_id);
+	    }
+
+	  tyseg = SubstMapper::Resolve (tyseg, expr_locus,
+					&generic_segment->get_generic_args ());
+	  if (tyseg->get_kind () == TyTy::TypeKind::ERROR)
+	    return new TyTy::ErrorType (expr_id);
+	}
+    }
+
+  context->insert_receiver (expr_mappings.get_hirid (), prev_segment);
+  if (tyseg->needs_generic_substitutions ())
+    {
+      Location locus = segments.back ()->get_locus ();
+      if (!prev_segment->needs_generic_substitutions ())
+	{
+	  auto used_args_in_prev_segment
+	    = GetUsedSubstArgs::From (prev_segment);
+	  if (!used_args_in_prev_segment.is_error ())
+	    tyseg
+	      = SubstMapperInternal::Resolve (tyseg, used_args_in_prev_segment);
+	}
+      else
+	{
+	  tyseg = SubstMapper::InferSubst (tyseg, locus);
+	}
+
+      if (tyseg->get_kind () == TyTy::TypeKind::ERROR)
+	return new TyTy::ErrorType (expr_id);
+    }
+
+  rust_assert (resolved_node_id != UNKNOWN_NODEID);
+
+  // lookup if the name resolver was able to canonically resolve this or not
+  NodeId path_resolved_id = UNKNOWN_NODEID;
+  if (resolver->lookup_resolved_name (expr_mappings.get_nodeid (),
+				      &path_resolved_id))
+    {
+      rust_assert (path_resolved_id == resolved_node_id);
+    }
+  // check the type scope
+  else if (resolver->lookup_resolved_type (expr_mappings.get_nodeid (),
+					   &path_resolved_id))
+    {
+      rust_assert (path_resolved_id == resolved_node_id);
+    }
+  else
+    {
+      // name scope first
+      if (resolver->get_name_scope ().decl_was_declared_here (resolved_node_id))
+	{
+	  resolver->insert_resolved_name (expr_mappings.get_nodeid (),
+					  resolved_node_id);
+	}
+      // check the type scope
+      else if (resolver->get_type_scope ().decl_was_declared_here (
+		 resolved_node_id))
+	{
+	  resolver->insert_resolved_type (expr_mappings.get_nodeid (),
+					  resolved_node_id);
+	}
+    }
+
+  return tyseg;
+}
+
+void
+TypeCheckType::visit (HIR::TraitObjectType &type)
+{
+  std::vector<TyTy::TypeBoundPredicate> specified_bounds;
+  for (auto &bound : type.get_type_param_bounds ())
+    {
+      if (bound->get_bound_type ()
+	  != HIR::TypeParamBound::BoundType::TRAITBOUND)
+	continue;
+
+      HIR::TypeParamBound &b = *bound.get ();
+      HIR::TraitBound &trait_bound = static_cast<HIR::TraitBound &> (b);
+
+      TyTy::TypeBoundPredicate predicate
+	= get_predicate_from_bound (trait_bound.get_path ());
+
+      if (!predicate.is_error ()
+	  && predicate.is_object_safe (true, type.get_locus ()))
+	specified_bounds.push_back (std::move (predicate));
+    }
+
+  RustIdent ident{CanonicalPath::create_empty (), type.get_locus ()};
+  translated
+    = new TyTy::DynamicObjectType (type.get_mappings ().get_hirid (), ident,
+				   std::move (specified_bounds));
+}
+
+void
+TypeCheckType::visit (HIR::ArrayType &type)
+{
+  auto capacity_type = TypeCheckExpr::Resolve (type.get_size_expr ());
+  if (capacity_type->get_kind () == TyTy::TypeKind::ERROR)
+    return;
+
+  TyTy::BaseType *expected_ty = nullptr;
+  bool ok = context->lookup_builtin ("usize", &expected_ty);
+  rust_assert (ok);
+  context->insert_type (type.get_size_expr ()->get_mappings (), expected_ty);
+
+  auto unified = expected_ty->unify (capacity_type);
+  if (unified->get_kind () == TyTy::TypeKind::ERROR)
+    return;
+
+  TyTy::BaseType *base = TypeCheckType::Resolve (type.get_element_type ());
+  translated = new TyTy::ArrayType (type.get_mappings ().get_hirid (),
+				    type.get_locus (), *type.get_size_expr (),
+				    TyTy::TyVar (base->get_ref ()));
+}
+
+void
+TypeCheckType::visit (HIR::SliceType &type)
+{
+  TyTy::BaseType *base
+    = TypeCheckType::Resolve (type.get_element_type ().get ());
+  translated
+    = new TyTy::SliceType (type.get_mappings ().get_hirid (), type.get_locus (),
+			   TyTy::TyVar (base->get_ref ()));
+}
+void
+TypeCheckType::visit (HIR::ReferenceType &type)
+{
+  TyTy::BaseType *base = TypeCheckType::Resolve (type.get_base_type ().get ());
+  translated
+    = new TyTy::ReferenceType (type.get_mappings ().get_hirid (),
+			       TyTy::TyVar (base->get_ref ()), type.get_mut ());
+}
+
+void
+TypeCheckType::visit (HIR::RawPointerType &type)
+{
+  TyTy::BaseType *base = TypeCheckType::Resolve (type.get_base_type ().get ());
+  translated
+    = new TyTy::PointerType (type.get_mappings ().get_hirid (),
+			     TyTy::TyVar (base->get_ref ()), type.get_mut ());
+}
+
+void
+TypeCheckType::visit (HIR::InferredType &type)
+{
+  translated = new TyTy::InferType (type.get_mappings ().get_hirid (),
+				    TyTy::InferType::InferTypeKind::GENERAL,
+				    type.get_locus ());
+}
+
+void
+TypeCheckType::visit (HIR::NeverType &type)
+{
+  TyTy::BaseType *lookup = nullptr;
+  bool ok = context->lookup_builtin ("!", &lookup);
+  rust_assert (ok);
+
+  translated = lookup->clone ();
+}
+
+TyTy::ParamType *
+TypeResolveGenericParam::Resolve (HIR::GenericParam *param)
+{
+  TypeResolveGenericParam resolver;
+  switch (param->get_kind ())
+    {
+    case HIR::GenericParam::GenericKind::TYPE:
+      resolver.visit (static_cast<HIR::TypeParam &> (*param));
+      break;
+
+    case HIR::GenericParam::GenericKind::CONST:
+      resolver.visit (static_cast<HIR::ConstGenericParam &> (*param));
+      break;
+
+    case HIR::GenericParam::GenericKind::LIFETIME:
+      resolver.visit (static_cast<HIR::LifetimeParam &> (*param));
+      break;
+    }
+  return resolver.resolved;
+}
+
+void
+TypeResolveGenericParam::visit (HIR::LifetimeParam &param)
+{
+  // nothing to do
+}
+
+void
+TypeResolveGenericParam::visit (HIR::ConstGenericParam &param)
+{
+  // TODO
+}
+
+void
+TypeResolveGenericParam::visit (HIR::TypeParam &param)
+{
+  if (param.has_type ())
+    TypeCheckType::Resolve (param.get_type ().get ());
+
+  std::vector<TyTy::TypeBoundPredicate> specified_bounds;
+  if (param.has_type_param_bounds ())
+    {
+      for (auto &bound : param.get_type_param_bounds ())
+	{
+	  switch (bound->get_bound_type ())
+	    {
+	      case HIR::TypeParamBound::BoundType::TRAITBOUND: {
+		HIR::TraitBound *b
+		  = static_cast<HIR::TraitBound *> (bound.get ());
+
+		TyTy::TypeBoundPredicate predicate
+		  = get_predicate_from_bound (b->get_path ());
+		if (!predicate.is_error ())
+		  specified_bounds.push_back (std::move (predicate));
+	      }
+	      break;
+
+	    default:
+	      break;
+	    }
+	}
+    }
+
+  resolved
+    = new TyTy::ParamType (param.get_type_representation (), param.get_locus (),
+			   param.get_mappings ().get_hirid (), param,
+			   specified_bounds);
+}
+
+void
+ResolveWhereClauseItem::Resolve (HIR::WhereClauseItem &item)
+{
+  ResolveWhereClauseItem resolver;
+  switch (item.get_item_type ())
+    {
+    case HIR::WhereClauseItem::LIFETIME:
+      resolver.visit (static_cast<HIR::LifetimeWhereClauseItem &> (item));
+      break;
+
+    case HIR::WhereClauseItem::TYPE_BOUND:
+      resolver.visit (static_cast<HIR::TypeBoundWhereClauseItem &> (item));
+      break;
+    }
+}
+
+void
+ResolveWhereClauseItem::visit (HIR::LifetimeWhereClauseItem &item)
+{}
+
+void
+ResolveWhereClauseItem::visit (HIR::TypeBoundWhereClauseItem &item)
+{
+  auto &binding_type_path = item.get_bound_type ();
+  TyTy::BaseType *binding = TypeCheckType::Resolve (binding_type_path.get ());
+
+  std::vector<TyTy::TypeBoundPredicate> specified_bounds;
+  for (auto &bound : item.get_type_param_bounds ())
+    {
+      switch (bound->get_bound_type ())
+	{
+	  case HIR::TypeParamBound::BoundType::TRAITBOUND: {
+	    HIR::TraitBound *b = static_cast<HIR::TraitBound *> (bound.get ());
+
+	    TyTy::TypeBoundPredicate predicate
+	      = get_predicate_from_bound (b->get_path ());
+	    if (!predicate.is_error ())
+	      specified_bounds.push_back (std::move (predicate));
+	  }
+	  break;
+
+	default:
+	  break;
+	}
+    }
+  binding->inherit_bounds (specified_bounds);
+
+  // When we apply these bounds we must lookup which type this binding
+  // resolves to, as this is the type which will be used during resolution
+  // of the block.
+  NodeId ast_node_id = binding_type_path->get_mappings ().get_nodeid ();
+
+  // then lookup the reference_node_id
+  NodeId ref_node_id = UNKNOWN_NODEID;
+  if (!resolver->lookup_resolved_type (ast_node_id, &ref_node_id))
+    {
+      // FIXME
+      rust_error_at (Location (),
+		     "Failed to lookup type reference for node: %s",
+		     binding_type_path->as_string ().c_str ());
+      return;
+    }
+
+  // node back to HIR
+  HirId ref;
+  if (!mappings->lookup_node_to_hir (ref_node_id, &ref))
+    {
+      // FIXME
+      rust_error_at (Location (), "where-clause reverse lookup failure");
+      return;
+    }
+
+  // the base reference for this name _must_ have a type set
+  TyTy::BaseType *lookup;
+  if (!context->lookup_type (ref, &lookup))
+    {
+      rust_error_at (mappings->lookup_location (ref),
+		     "Failed to resolve where-clause binding type: %s",
+		     binding_type_path->as_string ().c_str ());
+      return;
+    }
+
+  // FIXME
+  // rust_assert (binding->is_equal (*lookup));
+  lookup->inherit_bounds (specified_bounds);
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-hir-type-check-type.h b/gcc/rust/typecheck/rust-hir-type-check-type.h
new file mode 100644
index 00000000000..90d5ddbb411
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-type.h
@@ -0,0 +1,130 @@
+// 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_HIR_TYPE_CHECK_TYPE
+#define RUST_HIR_TYPE_CHECK_TYPE
+
+#include "rust-hir-type-check-base.h"
+#include "rust-hir-full.h"
+#include "rust-substitution-mapper.h"
+#include "rust-hir-path-probe.h"
+
+namespace Rust {
+namespace Resolver {
+
+// FIXME
+// This simply fetches the HIR:::GenericArgs from the base class. Check to see
+// if we can get rid of this class
+class TypeCheckResolveGenericArguments : public TypeCheckBase
+{
+public:
+  static HIR::GenericArgs resolve (HIR::TypePathSegment *segment);
+
+  void visit (HIR::TypePathSegmentGeneric &generic);
+
+private:
+  TypeCheckResolveGenericArguments (Location locus)
+    : TypeCheckBase (), args (HIR::GenericArgs::create_empty (locus))
+  {}
+
+  HIR::GenericArgs args;
+};
+
+class TypeCheckType : public TypeCheckBase, public HIR::HIRTypeVisitor
+{
+public:
+  static TyTy::BaseType *Resolve (HIR::Type *type);
+
+  void visit (HIR::BareFunctionType &fntype) override;
+  void visit (HIR::TupleType &tuple) override;
+  void visit (HIR::TypePath &path) override;
+  void visit (HIR::QualifiedPathInType &path) override;
+  void visit (HIR::ArrayType &type) override;
+  void visit (HIR::SliceType &type) override;
+  void visit (HIR::ReferenceType &type) override;
+  void visit (HIR::RawPointerType &type) override;
+  void visit (HIR::InferredType &type) override;
+  void visit (HIR::NeverType &type) override;
+  void visit (HIR::TraitObjectType &type) override;
+
+  void visit (HIR::TypePathSegmentFunction &segment) override
+  { /* TODO */
+  }
+  void visit (HIR::TraitBound &bound) override
+  { /* TODO */
+  }
+  void visit (HIR::ImplTraitType &type) override
+  { /* TODO */
+  }
+  void visit (HIR::ParenthesisedType &type) override
+  { /* TODO */
+  }
+  void visit (HIR::ImplTraitTypeOneBound &type) override
+  { /* TODO */
+  }
+
+private:
+  TypeCheckType (HirId id)
+    : TypeCheckBase (), translated (new TyTy::ErrorType (id))
+  {}
+
+  TyTy::BaseType *resolve_root_path (HIR::TypePath &path, size_t *offset,
+				     NodeId *root_resolved_node_id);
+
+  TyTy::BaseType *resolve_segments (
+    NodeId root_resolved_node_id, HirId expr_id,
+    std::vector<std::unique_ptr<HIR::TypePathSegment>> &segments, size_t offset,
+    TyTy::BaseType *tyseg, const Analysis::NodeMapping &expr_mappings,
+    Location expr_locus);
+
+  TyTy::BaseType *translated;
+};
+
+class TypeResolveGenericParam : public TypeCheckBase
+{
+public:
+  static TyTy::ParamType *Resolve (HIR::GenericParam *param);
+
+protected:
+  void visit (HIR::TypeParam &param);
+  void visit (HIR::LifetimeParam &param);
+  void visit (HIR::ConstGenericParam &param);
+
+private:
+  TypeResolveGenericParam () : TypeCheckBase (), resolved (nullptr) {}
+
+  TyTy::ParamType *resolved;
+};
+
+class ResolveWhereClauseItem : public TypeCheckBase
+{
+public:
+  static void Resolve (HIR::WhereClauseItem &item);
+
+protected:
+  void visit (HIR::LifetimeWhereClauseItem &item);
+  void visit (HIR::TypeBoundWhereClauseItem &item);
+
+private:
+  ResolveWhereClauseItem () : TypeCheckBase () {}
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_HIR_TYPE_CHECK_TYPE
diff --git a/gcc/rust/typecheck/rust-hir-type-check-util.cc b/gcc/rust/typecheck/rust-hir-type-check-util.cc
new file mode 100644
index 00000000000..e25f431a507
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-util.cc
@@ -0,0 +1,41 @@
+// Copyright (C) 2021-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/>.
+
+#include "rust-hir-type-check-util.h"
+#include "rust-hir-full.h"
+
+namespace Rust {
+namespace Resolver {
+
+void
+ImplTypeIterator::go ()
+{
+  for (auto &item : impl.get_impl_items ())
+    {
+      item->accept_vis (*this);
+    }
+}
+
+void
+ImplTypeIterator::visit (HIR::TypeAlias &alias)
+{
+  cb (alias);
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-hir-type-check-util.h b/gcc/rust/typecheck/rust-hir-type-check-util.h
new file mode 100644
index 00000000000..1a4b17a3303
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check-util.h
@@ -0,0 +1,50 @@
+// Copyright (C) 2021-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_HIR_TYPE_CHECK_UTIL_H
+#define RUST_HIR_TYPE_CHECK_UTIL_H
+
+#include "rust-system.h"
+#include "rust-hir-visitor.h"
+
+namespace Rust {
+namespace Resolver {
+
+class ImplTypeIterator : public HIR::HIRFullVisitorBase
+{
+  using HIR::HIRFullVisitorBase::visit;
+
+public:
+  ImplTypeIterator (HIR::ImplBlock &impl,
+		    std::function<void (HIR::TypeAlias &alias)> cb)
+    : impl (impl), cb (cb)
+  {}
+
+  void go ();
+
+  void visit (HIR::TypeAlias &alias) override;
+
+private:
+  HIR::ImplBlock &impl;
+  std::function<void (HIR::TypeAlias &alias)> cb;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_HIR_TYPE_CHECK_UTIL_H
diff --git a/gcc/rust/typecheck/rust-hir-type-check.cc b/gcc/rust/typecheck/rust-hir-type-check.cc
new file mode 100644
index 00000000000..c314585cd3d
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check.cc
@@ -0,0 +1,295 @@
+// 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/>.
+
+#include "rust-hir-type-check.h"
+#include "rust-hir-full.h"
+#include "rust-hir-type-check-toplevel.h"
+#include "rust-hir-type-check-item.h"
+#include "rust-hir-type-check-expr.h"
+#include "rust-hir-type-check-pattern.h"
+#include "rust-hir-type-check-struct-field.h"
+#include "rust-hir-inherent-impl-overlap.h"
+
+extern bool
+saw_errors (void);
+
+namespace Rust {
+namespace Resolver {
+
+void
+TypeResolution::Resolve (HIR::Crate &crate)
+{
+  for (auto it = crate.items.begin (); it != crate.items.end (); it++)
+    TypeCheckTopLevel::Resolve (*it->get ());
+
+  if (saw_errors ())
+    return;
+
+  OverlappingImplItemPass::go ();
+  if (saw_errors ())
+    return;
+
+  for (auto it = crate.items.begin (); it != crate.items.end (); it++)
+    TypeCheckItem::Resolve (*it->get ());
+
+  if (saw_errors ())
+    return;
+
+  auto mappings = Analysis::Mappings::get ();
+  auto context = TypeCheckContext::get ();
+
+  // default inference variables if possible
+  context->iterate ([&] (HirId id, TyTy::BaseType *ty) mutable -> bool {
+    // nothing to do
+    if (ty->get_kind () != TyTy::TypeKind::INFER)
+      return true;
+
+    TyTy::InferType *infer_var = static_cast<TyTy::InferType *> (ty);
+    TyTy::BaseType *default_type;
+    bool ok = infer_var->default_type (&default_type);
+    if (!ok)
+      {
+	rust_error_at (mappings->lookup_location (id),
+		       "type annotations needed");
+	return true;
+      }
+    else
+      {
+	auto result = ty->unify (default_type);
+	result->set_ref (id);
+	context->insert_type (
+	  Analysis::NodeMapping (mappings->get_current_crate (), 0, id,
+				 UNKNOWN_LOCAL_DEFID),
+	  result);
+      }
+
+    return true;
+  });
+}
+
+// rust-hir-trait-ref.h
+
+TraitItemReference::TraitItemReference (
+  std::string identifier, bool optional, TraitItemType type,
+  HIR::TraitItem *hir_trait_item, TyTy::BaseType *self,
+  std::vector<TyTy::SubstitutionParamMapping> substitutions, Location locus)
+  : identifier (identifier), optional_flag (optional), type (type),
+    hir_trait_item (hir_trait_item),
+    inherited_substitutions (std::move (substitutions)), locus (locus),
+    self (self), context (TypeCheckContext::get ())
+{}
+
+TraitItemReference::TraitItemReference (TraitItemReference const &other)
+  : identifier (other.identifier), optional_flag (other.optional_flag),
+    type (other.type), hir_trait_item (other.hir_trait_item),
+    locus (other.locus), self (other.self), context (TypeCheckContext::get ())
+{
+  inherited_substitutions.clear ();
+  inherited_substitutions.reserve (other.inherited_substitutions.size ());
+  for (size_t i = 0; i < other.inherited_substitutions.size (); i++)
+    inherited_substitutions.push_back (
+      other.inherited_substitutions.at (i).clone ());
+}
+
+TraitItemReference &
+TraitItemReference::operator= (TraitItemReference const &other)
+{
+  identifier = other.identifier;
+  optional_flag = other.optional_flag;
+  type = other.type;
+  hir_trait_item = other.hir_trait_item;
+  self = other.self;
+  locus = other.locus;
+  context = other.context;
+
+  inherited_substitutions.clear ();
+  inherited_substitutions.reserve (other.inherited_substitutions.size ());
+  for (size_t i = 0; i < other.inherited_substitutions.size (); i++)
+    inherited_substitutions.push_back (
+      other.inherited_substitutions.at (i).clone ());
+
+  return *this;
+}
+
+TyTy::BaseType *
+TraitItemReference::get_type_from_typealias (/*const*/
+					     HIR::TraitItemType &type) const
+{
+  TyTy::TyVar var (get_mappings ().get_hirid ());
+  return var.get_tyty ();
+}
+
+TyTy::BaseType *
+TraitItemReference::get_type_from_constant (
+  /*const*/ HIR::TraitItemConst &constant) const
+{
+  TyTy::BaseType *type = TypeCheckType::Resolve (constant.get_type ().get ());
+  if (constant.has_expr ())
+    {
+      TyTy::BaseType *expr
+	= TypeCheckExpr::Resolve (constant.get_expr ().get ());
+
+      return type->unify (expr);
+    }
+  return type;
+}
+
+TyTy::BaseType *
+TraitItemReference::get_type_from_fn (/*const*/ HIR::TraitItemFunc &fn) const
+{
+  std::vector<TyTy::SubstitutionParamMapping> substitutions
+    = inherited_substitutions;
+
+  HIR::TraitFunctionDecl &function = fn.get_decl ();
+  if (function.has_generics ())
+    {
+      for (auto &generic_param : function.get_generic_params ())
+	{
+	  switch (generic_param.get ()->get_kind ())
+	    {
+	    case HIR::GenericParam::GenericKind::LIFETIME:
+	    case HIR::GenericParam::GenericKind::CONST:
+	      // FIXME: Skipping Lifetime and Const completely until better
+	      // handling.
+	      break;
+
+	      case HIR::GenericParam::GenericKind::TYPE: {
+		auto param_type
+		  = TypeResolveGenericParam::Resolve (generic_param.get ());
+		context->insert_type (generic_param->get_mappings (),
+				      param_type);
+
+		substitutions.push_back (TyTy::SubstitutionParamMapping (
+		  static_cast<HIR::TypeParam &> (*generic_param), param_type));
+	      }
+	      break;
+	    }
+	}
+    }
+
+  TyTy::BaseType *ret_type = nullptr;
+  if (!function.has_return_type ())
+    ret_type = TyTy::TupleType::get_unit_type (fn.get_mappings ().get_hirid ());
+  else
+    {
+      auto resolved
+	= TypeCheckType::Resolve (function.get_return_type ().get ());
+      if (resolved->get_kind () == TyTy::TypeKind::ERROR)
+	{
+	  rust_error_at (fn.get_locus (), "failed to resolve return type");
+	  return get_error ();
+	}
+
+      ret_type = resolved->clone ();
+      ret_type->set_ref (
+	function.get_return_type ()->get_mappings ().get_hirid ());
+    }
+
+  std::vector<std::pair<HIR::Pattern *, TyTy::BaseType *> > params;
+  if (function.is_method ())
+    {
+      // these are implicit mappings and not used
+      auto mappings = Analysis::Mappings::get ();
+      auto crate_num = mappings->get_current_crate ();
+      Analysis::NodeMapping mapping (crate_num, mappings->get_next_node_id (),
+				     mappings->get_next_hir_id (crate_num),
+				     UNKNOWN_LOCAL_DEFID);
+
+      // add the synthetic self param at the front, this is a placeholder
+      // for compilation to know parameter names. The types are ignored
+      // but we reuse the HIR identifier pattern which requires it
+      HIR::SelfParam &self_param = function.get_self ();
+      HIR::IdentifierPattern *self_pattern
+	= new HIR::IdentifierPattern (mapping, "self", self_param.get_locus (),
+				      self_param.is_ref (),
+				      self_param.is_mut () ? Mutability::Mut
+							   : Mutability::Imm,
+				      std::unique_ptr<HIR::Pattern> (nullptr));
+      // might have a specified type
+      TyTy::BaseType *self_type = nullptr;
+      if (self_param.has_type ())
+	{
+	  std::unique_ptr<HIR::Type> &specified_type = self_param.get_type ();
+	  self_type = TypeCheckType::Resolve (specified_type.get ());
+	}
+      else
+	{
+	  switch (self_param.get_self_kind ())
+	    {
+	    case HIR::SelfParam::IMM:
+	    case HIR::SelfParam::MUT:
+	      self_type = self->clone ();
+	      break;
+
+	    case HIR::SelfParam::IMM_REF:
+	      self_type = new TyTy::ReferenceType (
+		self_param.get_mappings ().get_hirid (),
+		TyTy::TyVar (self->get_ref ()), Mutability::Imm);
+	      break;
+
+	    case HIR::SelfParam::MUT_REF:
+	      self_type = new TyTy::ReferenceType (
+		self_param.get_mappings ().get_hirid (),
+		TyTy::TyVar (self->get_ref ()), Mutability::Mut);
+	      break;
+
+	    default:
+	      gcc_unreachable ();
+	      return nullptr;
+	    }
+	}
+
+      context->insert_type (self_param.get_mappings (), self_type);
+      params.push_back (
+	std::pair<HIR::Pattern *, TyTy::BaseType *> (self_pattern, self_type));
+    }
+
+  for (auto &param : function.get_function_params ())
+    {
+      // get the name as well required for later on
+      auto param_tyty = TypeCheckType::Resolve (param.get_type ());
+      params.push_back (
+	std::pair<HIR::Pattern *, TyTy::BaseType *> (param.get_param_name (),
+						     param_tyty));
+
+      context->insert_type (param.get_mappings (), param_tyty);
+      TypeCheckPattern::Resolve (param.get_param_name (), param_tyty);
+    }
+
+  auto mappings = Analysis::Mappings::get ();
+  const CanonicalPath *canonical_path = nullptr;
+  bool ok = mappings->lookup_canonical_path (fn.get_mappings ().get_nodeid (),
+					     &canonical_path);
+  rust_assert (ok);
+
+  RustIdent ident{*canonical_path, fn.get_locus ()};
+  auto resolved
+    = new TyTy::FnType (fn.get_mappings ().get_hirid (),
+			fn.get_mappings ().get_defid (),
+			function.get_function_name (), ident,
+			function.is_method ()
+			  ? TyTy::FnType::FNTYPE_IS_METHOD_FLAG
+			  : TyTy::FnType::FNTYPE_DEFAULT_FLAGS,
+			ABI::RUST, std::move (params), ret_type, substitutions);
+
+  context->insert_type (fn.get_mappings (), resolved);
+  return resolved;
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-hir-type-check.h b/gcc/rust/typecheck/rust-hir-type-check.h
new file mode 100644
index 00000000000..21694dd302b
--- /dev/null
+++ b/gcc/rust/typecheck/rust-hir-type-check.h
@@ -0,0 +1,379 @@
+// 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_HIR_TYPE_CHECK
+#define RUST_HIR_TYPE_CHECK
+
+#include "rust-hir-full-decls.h"
+#include "rust-hir-map.h"
+#include "rust-tyty.h"
+#include "rust-hir-trait-ref.h"
+#include "rust-autoderef.h"
+
+namespace Rust {
+namespace Resolver {
+
+class TypeCheckContextItem
+{
+public:
+  enum ItemType
+  {
+    ITEM,
+    IMPL_ITEM,
+    TRAIT_ITEM,
+  };
+
+  TypeCheckContextItem (HIR::Function *item)
+    : type (ItemType::ITEM), item (item)
+  {}
+
+  TypeCheckContextItem (HIR::ImplBlock *impl_block, HIR::Function *item)
+    : type (ItemType::IMPL_ITEM), item (impl_block, item)
+  {}
+
+  TypeCheckContextItem (HIR::TraitItemFunc *trait_item)
+    : type (ItemType::TRAIT_ITEM), item (trait_item)
+  {}
+
+  ItemType get_type () const { return type; }
+
+  HIR::Function *get_item ()
+  {
+    rust_assert (get_type () == ItemType::ITEM);
+    return item.item;
+  }
+
+  std::pair<HIR::ImplBlock *, HIR::Function *> &get_impl_item ()
+  {
+    rust_assert (get_type () == ItemType::IMPL_ITEM);
+    return item.impl_item;
+  };
+
+  HIR::TraitItemFunc *get_trait_item ()
+  {
+    rust_assert (get_type () == ItemType::TRAIT_ITEM);
+    return item.trait_item;
+  }
+
+private:
+  union Item
+  {
+    HIR::Function *item;
+    std::pair<HIR::ImplBlock *, HIR::Function *> impl_item;
+    HIR::TraitItemFunc *trait_item;
+
+    Item (HIR::Function *item) : item (item) {}
+
+    Item (HIR::ImplBlock *impl_block, HIR::Function *item)
+      : impl_item ({impl_block, item})
+    {}
+
+    Item (HIR::TraitItemFunc *trait_item) : trait_item (trait_item) {}
+  };
+
+  ItemType type;
+  Item item;
+};
+
+class TypeCheckContext
+{
+public:
+  static TypeCheckContext *get ();
+
+  ~TypeCheckContext ();
+
+  bool lookup_builtin (NodeId id, TyTy::BaseType **type);
+  bool lookup_builtin (std::string name, TyTy::BaseType **type);
+  void insert_builtin (HirId id, NodeId ref, TyTy::BaseType *type);
+
+  void insert_type (const Analysis::NodeMapping &mappings,
+		    TyTy::BaseType *type);
+  void insert_implicit_type (TyTy::BaseType *type);
+  bool lookup_type (HirId id, TyTy::BaseType **type) const;
+
+  void insert_implicit_type (HirId id, TyTy::BaseType *type);
+
+  void insert_type_by_node_id (NodeId ref, HirId id);
+  bool lookup_type_by_node_id (NodeId ref, HirId *id);
+
+  TyTy::BaseType *peek_return_type ();
+  TypeCheckContextItem &peek_context ();
+  void push_return_type (TypeCheckContextItem item,
+			 TyTy::BaseType *return_type);
+  void pop_return_type ();
+
+  void iterate (std::function<bool (HirId, TyTy::BaseType *)> cb)
+  {
+    for (auto it = resolved.begin (); it != resolved.end (); it++)
+      {
+	if (!cb (it->first, it->second))
+	  return;
+      }
+  }
+
+  bool have_loop_context () const { return !loop_type_stack.empty (); }
+
+  void push_new_loop_context (HirId id, Location locus)
+  {
+    TyTy::BaseType *infer_var
+      = new TyTy::InferType (id, TyTy::InferType::InferTypeKind::GENERAL,
+			     locus);
+    loop_type_stack.push_back (infer_var);
+  }
+
+  void push_new_while_loop_context (HirId id)
+  {
+    TyTy::BaseType *infer_var = new TyTy::ErrorType (id);
+    loop_type_stack.push_back (infer_var);
+  }
+
+  TyTy::BaseType *peek_loop_context () { return loop_type_stack.back (); }
+
+  TyTy::BaseType *pop_loop_context ()
+  {
+    auto back = peek_loop_context ();
+    loop_type_stack.pop_back ();
+    return back;
+  }
+
+  void swap_head_loop_context (TyTy::BaseType *val)
+  {
+    loop_type_stack.pop_back ();
+    loop_type_stack.push_back (val);
+  }
+
+  void insert_trait_reference (DefId id, TraitReference &&ref)
+  {
+    rust_assert (trait_context.find (id) == trait_context.end ());
+    trait_context.emplace (id, std::move (ref));
+  }
+
+  bool lookup_trait_reference (DefId id, TraitReference **ref)
+  {
+    auto it = trait_context.find (id);
+    if (it == trait_context.end ())
+      return false;
+
+    *ref = &it->second;
+    return true;
+  }
+
+  void insert_receiver (HirId id, TyTy::BaseType *t)
+  {
+    receiver_context[id] = t;
+  }
+
+  bool lookup_receiver (HirId id, TyTy::BaseType **ref)
+  {
+    auto it = receiver_context.find (id);
+    if (it == receiver_context.end ())
+      return false;
+
+    *ref = it->second;
+    return true;
+  }
+
+  void insert_associated_trait_impl (HirId id, AssociatedImplTrait &&associated)
+  {
+    rust_assert (associated_impl_traits.find (id)
+		 == associated_impl_traits.end ());
+    associated_impl_traits.emplace (id, std::move (associated));
+  }
+
+  bool lookup_associated_trait_impl (HirId id, AssociatedImplTrait **associated)
+  {
+    auto it = associated_impl_traits.find (id);
+    if (it == associated_impl_traits.end ())
+      return false;
+
+    *associated = &it->second;
+    return true;
+  }
+
+  void insert_associated_type_mapping (HirId id, HirId mapping)
+  {
+    associated_type_mappings[id] = mapping;
+  }
+
+  void clear_associated_type_mapping (HirId id)
+  {
+    auto it = associated_type_mappings.find (id);
+    if (it != associated_type_mappings.end ())
+      associated_type_mappings.erase (it);
+  }
+
+  // lookup any associated type mappings, the out parameter of mapping is
+  // allowed to be nullptr which allows this interface to do a simple does exist
+  // check
+  bool lookup_associated_type_mapping (HirId id, HirId *mapping)
+  {
+    auto it = associated_type_mappings.find (id);
+    if (it == associated_type_mappings.end ())
+      return false;
+
+    if (mapping != nullptr)
+      *mapping = it->second;
+
+    return true;
+  }
+
+  void insert_associated_impl_mapping (HirId trait_id,
+				       const TyTy::BaseType *impl_type,
+				       HirId impl_id)
+  {
+    auto it = associated_traits_to_impls.find (trait_id);
+    if (it == associated_traits_to_impls.end ())
+      {
+	associated_traits_to_impls[trait_id] = {};
+      }
+
+    associated_traits_to_impls[trait_id].push_back ({impl_type, impl_id});
+  }
+
+  bool lookup_associated_impl_mapping_for_self (HirId trait_id,
+						const TyTy::BaseType *self,
+						HirId *mapping)
+  {
+    auto it = associated_traits_to_impls.find (trait_id);
+    if (it == associated_traits_to_impls.end ())
+      return false;
+
+    for (auto &item : it->second)
+      {
+	if (item.first->can_eq (self, false))
+	  {
+	    *mapping = item.second;
+	    return true;
+	  }
+      }
+    return false;
+  }
+
+  void insert_autoderef_mappings (HirId id,
+				  std::vector<Adjustment> &&adjustments)
+  {
+    rust_assert (autoderef_mappings.find (id) == autoderef_mappings.end ());
+    autoderef_mappings.emplace (id, std::move (adjustments));
+  }
+
+  bool lookup_autoderef_mappings (HirId id,
+				  std::vector<Adjustment> **adjustments)
+  {
+    auto it = autoderef_mappings.find (id);
+    if (it == autoderef_mappings.end ())
+      return false;
+
+    *adjustments = &it->second;
+    return true;
+  }
+
+  void insert_cast_autoderef_mappings (HirId id,
+				       std::vector<Adjustment> &&adjustments)
+  {
+    rust_assert (cast_autoderef_mappings.find (id)
+		 == cast_autoderef_mappings.end ());
+    cast_autoderef_mappings.emplace (id, std::move (adjustments));
+  }
+
+  bool lookup_cast_autoderef_mappings (HirId id,
+				       std::vector<Adjustment> **adjustments)
+  {
+    auto it = cast_autoderef_mappings.find (id);
+    if (it == cast_autoderef_mappings.end ())
+      return false;
+
+    *adjustments = &it->second;
+    return true;
+  }
+
+  void insert_variant_definition (HirId id, HirId variant)
+  {
+    auto it = variants.find (id);
+    rust_assert (it == variants.end ());
+
+    variants[id] = variant;
+  }
+
+  bool lookup_variant_definition (HirId id, HirId *variant)
+  {
+    auto it = variants.find (id);
+    if (it == variants.end ())
+      return false;
+
+    *variant = it->second;
+    return true;
+  }
+
+  void insert_operator_overload (HirId id, TyTy::FnType *call_site)
+  {
+    auto it = operator_overloads.find (id);
+    rust_assert (it == operator_overloads.end ());
+
+    operator_overloads[id] = call_site;
+  }
+
+  bool lookup_operator_overload (HirId id, TyTy::FnType **call)
+  {
+    auto it = operator_overloads.find (id);
+    if (it == operator_overloads.end ())
+      return false;
+
+    *call = it->second;
+    return true;
+  }
+
+private:
+  TypeCheckContext ();
+
+  std::map<NodeId, HirId> node_id_refs;
+  std::map<HirId, TyTy::BaseType *> resolved;
+  std::vector<std::unique_ptr<TyTy::BaseType>> builtins;
+  std::vector<std::pair<TypeCheckContextItem, TyTy::BaseType *>>
+    return_type_stack;
+  std::vector<TyTy::BaseType *> loop_type_stack;
+  std::map<DefId, TraitReference> trait_context;
+  std::map<HirId, TyTy::BaseType *> receiver_context;
+  std::map<HirId, AssociatedImplTrait> associated_impl_traits;
+
+  // trait-id -> list of < self-tyty:impl-id>
+  std::map<HirId, std::vector<std::pair<const TyTy::BaseType *, HirId>>>
+    associated_traits_to_impls;
+
+  std::map<HirId, HirId> associated_type_mappings;
+
+  // adjustment mappings
+  std::map<HirId, std::vector<Adjustment>> autoderef_mappings;
+  std::map<HirId, std::vector<Adjustment>> cast_autoderef_mappings;
+
+  // operator overloads
+  std::map<HirId, TyTy::FnType *> operator_overloads;
+
+  // variants
+  std::map<HirId, HirId> variants;
+};
+
+class TypeResolution
+{
+public:
+  static void Resolve (HIR::Crate &crate);
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_HIR_TYPE_CHECK
diff --git a/gcc/rust/typecheck/rust-substitution-mapper.cc b/gcc/rust/typecheck/rust-substitution-mapper.cc
new file mode 100644
index 00000000000..f80368a0339
--- /dev/null
+++ b/gcc/rust/typecheck/rust-substitution-mapper.cc
@@ -0,0 +1,77 @@
+// 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/>.
+
+#include "rust-substitution-mapper.h"
+#include "rust-hir-type-check.h"
+
+namespace Rust {
+namespace Resolver {
+
+TyTy::BaseType *
+SubstMapperInternal::Resolve (TyTy::BaseType *base,
+			      TyTy::SubstitutionArgumentMappings &mappings)
+{
+  auto context = TypeCheckContext::get ();
+
+  SubstMapperInternal mapper (base->get_ref (), mappings);
+  base->accept_vis (mapper);
+  rust_assert (mapper.resolved != nullptr);
+
+  // insert these new implict types into the context
+  TyTy::BaseType *unused = nullptr;
+  bool is_ty_available
+    = context->lookup_type (mapper.resolved->get_ty_ref (), &unused);
+  if (!is_ty_available)
+    {
+      context->insert_type (
+	Analysis::NodeMapping (0, 0, mapper.resolved->get_ty_ref (), 0),
+	mapper.resolved);
+    }
+  bool is_ref_available
+    = context->lookup_type (mapper.resolved->get_ref (), &unused);
+  if (!is_ref_available)
+    {
+      context->insert_type (Analysis::NodeMapping (0, 0,
+						   mapper.resolved->get_ref (),
+						   0),
+			    mapper.resolved);
+    }
+
+  return mapper.resolved;
+}
+
+bool
+SubstMapperInternal::mappings_are_bound (
+  TyTy::BaseType *tyseg, TyTy::SubstitutionArgumentMappings &mappings)
+{
+  if (tyseg->get_kind () == TyTy::TypeKind::ADT)
+    {
+      TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (tyseg);
+      return adt->are_mappings_bound (mappings);
+    }
+  else if (tyseg->get_kind () == TyTy::TypeKind::FNDEF)
+    {
+      TyTy::FnType *fn = static_cast<TyTy::FnType *> (tyseg);
+      return fn->are_mappings_bound (mappings);
+    }
+
+  return false;
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-substitution-mapper.h b/gcc/rust/typecheck/rust-substitution-mapper.h
new file mode 100644
index 00000000000..028e10c0efe
--- /dev/null
+++ b/gcc/rust/typecheck/rust-substitution-mapper.h
@@ -0,0 +1,394 @@
+// 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_SUBSTITUTION_MAPPER_H
+#define RUST_SUBSTITUTION_MAPPER_H
+
+#include "rust-tyty.h"
+#include "rust-tyty-visitor.h"
+
+namespace Rust {
+namespace Resolver {
+
+class SubstMapper : public TyTy::TyVisitor
+{
+public:
+  static TyTy::BaseType *Resolve (TyTy::BaseType *base, Location locus,
+				  HIR::GenericArgs *generics = nullptr)
+  {
+    SubstMapper mapper (base->get_ref (), generics, locus);
+    base->accept_vis (mapper);
+    rust_assert (mapper.resolved != nullptr);
+    return mapper.resolved;
+  }
+
+  static TyTy::BaseType *InferSubst (TyTy::BaseType *base, Location locus)
+  {
+    return SubstMapper::Resolve (base, locus, nullptr);
+  }
+
+  bool have_generic_args () const { return generics != nullptr; }
+
+  void visit (TyTy::FnType &type) override
+  {
+    TyTy::FnType *concrete = nullptr;
+    if (!have_generic_args ())
+      {
+	TyTy::BaseType *substs = type.infer_substitions (locus);
+	rust_assert (substs->get_kind () == TyTy::TypeKind::FNDEF);
+	concrete = static_cast<TyTy::FnType *> (substs);
+      }
+    else
+      {
+	TyTy::SubstitutionArgumentMappings mappings
+	  = type.get_mappings_from_generic_args (*generics);
+	if (mappings.is_error ())
+	  return;
+
+	concrete = type.handle_substitions (mappings);
+      }
+
+    if (concrete != nullptr)
+      resolved = concrete;
+  }
+
+  void visit (TyTy::ADTType &type) override
+  {
+    TyTy::ADTType *concrete = nullptr;
+    if (!have_generic_args ())
+      {
+	TyTy::BaseType *substs = type.infer_substitions (locus);
+	rust_assert (substs->get_kind () == TyTy::TypeKind::ADT);
+	concrete = static_cast<TyTy::ADTType *> (substs);
+      }
+    else
+      {
+	TyTy::SubstitutionArgumentMappings mappings
+	  = type.get_mappings_from_generic_args (*generics);
+	if (mappings.is_error ())
+	  return;
+
+	concrete = type.handle_substitions (mappings);
+      }
+
+    if (concrete != nullptr)
+      resolved = concrete;
+  }
+
+  void visit (TyTy::PlaceholderType &type) override
+  {
+    rust_assert (type.can_resolve ());
+    resolved = SubstMapper::Resolve (type.resolve (), locus, generics);
+  }
+
+  void visit (TyTy::ProjectionType &type) override
+  {
+    TyTy::ProjectionType *concrete = nullptr;
+    if (!have_generic_args ())
+      {
+	TyTy::BaseType *substs = type.infer_substitions (locus);
+	rust_assert (substs->get_kind () == TyTy::TypeKind::ADT);
+	concrete = static_cast<TyTy::ProjectionType *> (substs);
+      }
+    else
+      {
+	TyTy::SubstitutionArgumentMappings mappings
+	  = type.get_mappings_from_generic_args (*generics);
+	if (mappings.is_error ())
+	  return;
+
+	concrete = type.handle_substitions (mappings);
+      }
+
+    if (concrete != nullptr)
+      resolved = concrete;
+  }
+
+  // nothing to do for these
+  void visit (TyTy::InferType &) override { gcc_unreachable (); }
+  void visit (TyTy::TupleType &) override { gcc_unreachable (); }
+  void visit (TyTy::FnPtr &) override { gcc_unreachable (); }
+  void visit (TyTy::ArrayType &) override { gcc_unreachable (); }
+  void visit (TyTy::SliceType &) override { gcc_unreachable (); }
+  void visit (TyTy::BoolType &) override { gcc_unreachable (); }
+  void visit (TyTy::IntType &) override { gcc_unreachable (); }
+  void visit (TyTy::UintType &) override { gcc_unreachable (); }
+  void visit (TyTy::FloatType &) override { gcc_unreachable (); }
+  void visit (TyTy::USizeType &) override { gcc_unreachable (); }
+  void visit (TyTy::ISizeType &) override { gcc_unreachable (); }
+  void visit (TyTy::ErrorType &) override { gcc_unreachable (); }
+  void visit (TyTy::CharType &) override { gcc_unreachable (); }
+  void visit (TyTy::ReferenceType &) override { gcc_unreachable (); }
+  void visit (TyTy::PointerType &) override { gcc_unreachable (); }
+  void visit (TyTy::ParamType &) override { gcc_unreachable (); }
+  void visit (TyTy::StrType &) override { gcc_unreachable (); }
+  void visit (TyTy::NeverType &) override { gcc_unreachable (); }
+  void visit (TyTy::DynamicObjectType &) override { gcc_unreachable (); }
+  void visit (TyTy::ClosureType &) override { gcc_unreachable (); }
+
+private:
+  SubstMapper (HirId ref, HIR::GenericArgs *generics, Location locus)
+    : resolved (new TyTy::ErrorType (ref)), generics (generics), locus (locus)
+  {}
+
+  TyTy::BaseType *resolved;
+  HIR::GenericArgs *generics;
+  Location locus;
+};
+
+class SubstMapperInternal : public TyTy::TyVisitor
+{
+public:
+  static TyTy::BaseType *Resolve (TyTy::BaseType *base,
+				  TyTy::SubstitutionArgumentMappings &mappings);
+
+  static bool mappings_are_bound (TyTy::BaseType *ty,
+				  TyTy::SubstitutionArgumentMappings &mappings);
+
+  void visit (TyTy::FnType &type) override
+  {
+    TyTy::SubstitutionArgumentMappings adjusted
+      = type.adjust_mappings_for_this (mappings);
+    if (adjusted.is_error ())
+      return;
+
+    TyTy::BaseType *concrete = type.handle_substitions (adjusted);
+    if (concrete != nullptr)
+      resolved = concrete;
+  }
+
+  void visit (TyTy::ADTType &type) override
+  {
+    TyTy::SubstitutionArgumentMappings adjusted
+      = type.adjust_mappings_for_this (mappings);
+    if (adjusted.is_error ())
+      return;
+
+    TyTy::BaseType *concrete = type.handle_substitions (adjusted);
+    if (concrete != nullptr)
+      resolved = concrete;
+  }
+
+  // these don't support generic arguments but might contain a type param
+  void visit (TyTy::TupleType &type) override
+  {
+    resolved = type.handle_substitions (mappings);
+  }
+
+  void visit (TyTy::ReferenceType &type) override
+  {
+    resolved = type.handle_substitions (mappings);
+  }
+
+  void visit (TyTy::PointerType &type) override
+  {
+    resolved = type.handle_substitions (mappings);
+  }
+
+  void visit (TyTy::ParamType &type) override
+  {
+    resolved = type.handle_substitions (mappings);
+  }
+
+  void visit (TyTy::PlaceholderType &type) override
+  {
+    rust_assert (type.can_resolve ());
+    if (mappings.trait_item_mode ())
+      {
+	resolved = type.resolve ();
+      }
+    else
+      {
+	resolved = SubstMapperInternal::Resolve (type.resolve (), mappings);
+      }
+  }
+
+  void visit (TyTy::ProjectionType &type) override
+  {
+    resolved = type.handle_substitions (mappings);
+  }
+
+  void visit (TyTy::ClosureType &type) override
+  {
+    resolved = type.handle_substitions (mappings);
+  }
+
+  void visit (TyTy::ArrayType &type) override
+  {
+    resolved = type.handle_substitions (mappings);
+  }
+
+  void visit (TyTy::SliceType &type) override
+  {
+    resolved = type.handle_substitions (mappings);
+  }
+
+  // nothing to do for these
+  void visit (TyTy::InferType &type) override { resolved = type.clone (); }
+  void visit (TyTy::FnPtr &type) override { resolved = type.clone (); }
+  void visit (TyTy::BoolType &type) override { resolved = type.clone (); }
+  void visit (TyTy::IntType &type) override { resolved = type.clone (); }
+  void visit (TyTy::UintType &type) override { resolved = type.clone (); }
+  void visit (TyTy::FloatType &type) override { resolved = type.clone (); }
+  void visit (TyTy::USizeType &type) override { resolved = type.clone (); }
+  void visit (TyTy::ISizeType &type) override { resolved = type.clone (); }
+  void visit (TyTy::ErrorType &type) override { resolved = type.clone (); }
+  void visit (TyTy::CharType &type) override { resolved = type.clone (); }
+  void visit (TyTy::StrType &type) override { resolved = type.clone (); }
+  void visit (TyTy::NeverType &type) override { resolved = type.clone (); }
+  void visit (TyTy::DynamicObjectType &type) override
+  {
+    resolved = type.clone ();
+  }
+
+private:
+  SubstMapperInternal (HirId ref, TyTy::SubstitutionArgumentMappings &mappings)
+    : resolved (new TyTy::ErrorType (ref)), mappings (mappings)
+  {}
+
+  TyTy::BaseType *resolved;
+  TyTy::SubstitutionArgumentMappings &mappings;
+};
+
+class SubstMapperFromExisting : public TyTy::TyVisitor
+{
+public:
+  static TyTy::BaseType *Resolve (TyTy::BaseType *concrete,
+				  TyTy::BaseType *receiver)
+  {
+    rust_assert (concrete->get_kind () == receiver->get_kind ());
+
+    SubstMapperFromExisting mapper (concrete, receiver);
+    concrete->accept_vis (mapper);
+    return mapper.resolved;
+  }
+
+  void visit (TyTy::FnType &type) override
+  {
+    rust_assert (type.was_substituted ());
+
+    TyTy::FnType *to_sub = static_cast<TyTy::FnType *> (receiver);
+    resolved = to_sub->handle_substitions (type.get_substitution_arguments ());
+  }
+
+  void visit (TyTy::ADTType &type) override
+  {
+    rust_assert (type.was_substituted ());
+
+    TyTy::ADTType *to_sub = static_cast<TyTy::ADTType *> (receiver);
+    resolved = to_sub->handle_substitions (type.get_substitution_arguments ());
+  }
+
+  void visit (TyTy::ClosureType &type) override
+  {
+    rust_assert (type.was_substituted ());
+
+    TyTy::ClosureType *to_sub = static_cast<TyTy::ClosureType *> (receiver);
+    resolved = to_sub->handle_substitions (type.get_substitution_arguments ());
+  }
+
+  void visit (TyTy::InferType &) override { gcc_unreachable (); }
+  void visit (TyTy::TupleType &) override { gcc_unreachable (); }
+  void visit (TyTy::FnPtr &) override { gcc_unreachable (); }
+  void visit (TyTy::ArrayType &) override { gcc_unreachable (); }
+  void visit (TyTy::SliceType &) override { gcc_unreachable (); }
+  void visit (TyTy::BoolType &) override { gcc_unreachable (); }
+  void visit (TyTy::IntType &) override { gcc_unreachable (); }
+  void visit (TyTy::UintType &) override { gcc_unreachable (); }
+  void visit (TyTy::FloatType &) override { gcc_unreachable (); }
+  void visit (TyTy::USizeType &) override { gcc_unreachable (); }
+  void visit (TyTy::ISizeType &) override { gcc_unreachable (); }
+  void visit (TyTy::ErrorType &) override { gcc_unreachable (); }
+  void visit (TyTy::CharType &) override { gcc_unreachable (); }
+  void visit (TyTy::ReferenceType &) override { gcc_unreachable (); }
+  void visit (TyTy::PointerType &) override { gcc_unreachable (); }
+  void visit (TyTy::ParamType &) override { gcc_unreachable (); }
+  void visit (TyTy::StrType &) override { gcc_unreachable (); }
+  void visit (TyTy::NeverType &) override { gcc_unreachable (); }
+  void visit (TyTy::PlaceholderType &) override { gcc_unreachable (); }
+  void visit (TyTy::ProjectionType &) override { gcc_unreachable (); }
+  void visit (TyTy::DynamicObjectType &) override { gcc_unreachable (); }
+
+private:
+  SubstMapperFromExisting (TyTy::BaseType *concrete, TyTy::BaseType *receiver)
+    : concrete (concrete), receiver (receiver), resolved (nullptr)
+  {}
+
+  TyTy::BaseType *concrete;
+  TyTy::BaseType *receiver;
+
+  TyTy::BaseType *resolved;
+};
+
+class GetUsedSubstArgs : public TyTy::TyConstVisitor
+{
+public:
+  static TyTy::SubstitutionArgumentMappings From (const TyTy::BaseType *from)
+  {
+    GetUsedSubstArgs mapper;
+    from->accept_vis (mapper);
+    return mapper.args;
+  }
+
+  void visit (const TyTy::FnType &type) override
+  {
+    args = type.get_substitution_arguments ();
+  }
+
+  void visit (const TyTy::ADTType &type) override
+  {
+    args = type.get_substitution_arguments ();
+  }
+
+  void visit (const TyTy::ClosureType &type) override
+  {
+    args = type.get_substitution_arguments ();
+  }
+
+  void visit (const TyTy::InferType &) override {}
+  void visit (const TyTy::TupleType &) override {}
+  void visit (const TyTy::FnPtr &) override {}
+  void visit (const TyTy::ArrayType &) override {}
+  void visit (const TyTy::SliceType &) override {}
+  void visit (const TyTy::BoolType &) override {}
+  void visit (const TyTy::IntType &) override {}
+  void visit (const TyTy::UintType &) override {}
+  void visit (const TyTy::FloatType &) override {}
+  void visit (const TyTy::USizeType &) override {}
+  void visit (const TyTy::ISizeType &) override {}
+  void visit (const TyTy::ErrorType &) override {}
+  void visit (const TyTy::CharType &) override {}
+  void visit (const TyTy::ReferenceType &) override {}
+  void visit (const TyTy::PointerType &) override {}
+  void visit (const TyTy::ParamType &) override {}
+  void visit (const TyTy::StrType &) override {}
+  void visit (const TyTy::NeverType &) override {}
+  void visit (const TyTy::PlaceholderType &) override {}
+  void visit (const TyTy::ProjectionType &) override {}
+  void visit (const TyTy::DynamicObjectType &) override {}
+
+private:
+  GetUsedSubstArgs () : args (TyTy::SubstitutionArgumentMappings::error ()) {}
+
+  TyTy::SubstitutionArgumentMappings args;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_SUBSTITUTION_MAPPER_H
diff --git a/gcc/rust/typecheck/rust-tycheck-dump.h b/gcc/rust/typecheck/rust-tycheck-dump.h
new file mode 100644
index 00000000000..ccf0f625e4b
--- /dev/null
+++ b/gcc/rust/typecheck/rust-tycheck-dump.h
@@ -0,0 +1,239 @@
+// 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_TYCHECK_DUMP
+#define RUST_TYCHECK_DUMP
+
+#include "rust-hir-type-check-base.h"
+#include "rust-hir-full.h"
+
+namespace Rust {
+namespace Resolver {
+
+class TypeResolverDump : private TypeCheckBase, private HIR::HIRFullVisitorBase
+{
+  using HIR::HIRFullVisitorBase::visit;
+
+public:
+  static void go (HIR::Crate &crate, std::ofstream &out)
+  {
+    TypeResolverDump dumper;
+    for (auto &item : crate.items)
+      {
+	item->accept_vis (dumper);
+	dumper.dump += "\n";
+      }
+
+    out << dumper.dump;
+  }
+
+  void visit (HIR::StructStruct &struct_decl) override
+  {
+    dump += indent () + "struct " + type_string (struct_decl.get_mappings ())
+	    + "\n";
+  }
+
+  void visit (HIR::Union &union_decl) override
+  {
+    dump
+      += indent () + "union " + type_string (union_decl.get_mappings ()) + "\n";
+  }
+
+  void visit (HIR::TupleStruct &struct_decl) override
+  {
+    dump += indent () + "struct" + type_string (struct_decl.get_mappings ())
+	    + "\n";
+  }
+
+  void visit (HIR::ImplBlock &impl_block) override
+  {
+    dump += indent () + "impl "
+	    + type_string (impl_block.get_type ()->get_mappings ()) + " {\n";
+    indentation_level++;
+
+    for (auto &impl_item : impl_block.get_impl_items ())
+      {
+	impl_item->accept_vis (*this);
+	dump += "\n";
+      }
+
+    indentation_level--;
+    dump += indent () + "}\n";
+  }
+
+  void visit (HIR::ConstantItem &constant) override
+  {
+    dump += indent () + "constant " + constant.get_identifier () + ":"
+	    + type_string (constant.get_mappings ()) + " = ";
+    constant.get_expr ()->accept_vis (*this);
+    dump += ";\n";
+  }
+
+  void visit (HIR::Function &function) override
+  {
+    dump += indent () + "fn " + function.get_function_name () + " "
+	    + type_string (function.get_mappings ()) + "\n";
+    dump += indent () + "{\n";
+
+    HIR::BlockExpr *function_body = function.get_definition ().get ();
+    function_body->accept_vis (*this);
+
+    dump += indent () + "}\n";
+  }
+
+  void visit (HIR::BlockExpr &expr) override
+  {
+    dump += "{\n";
+    indentation_level++;
+
+    for (auto &s : expr.get_statements ())
+      {
+	dump += indent ();
+	s->accept_vis (*this);
+	dump += ";\n";
+      }
+
+    if (expr.has_expr ())
+      {
+	dump += indent ();
+	expr.expr->accept_vis (*this);
+	dump += ";\n";
+      }
+
+    indentation_level--;
+    dump += "}\n";
+  }
+
+  void visit (HIR::UnsafeBlockExpr &expr) override
+  {
+    dump += "unsafe ";
+    expr.get_block_expr ()->accept_vis (*this);
+  }
+
+  void visit (HIR::LetStmt &stmt) override
+  {
+    dump += "let " + stmt.get_pattern ()->as_string () + ":"
+	    + type_string (stmt.get_pattern ()->get_pattern_mappings ());
+    if (stmt.has_init_expr ())
+      {
+	dump += " = ";
+	stmt.get_init_expr ()->accept_vis (*this);
+      }
+  }
+
+  void visit (HIR::ExprStmtWithBlock &stmt) override
+  {
+    stmt.get_expr ()->accept_vis (*this);
+  }
+
+  void visit (HIR::ExprStmtWithoutBlock &stmt) override
+  {
+    stmt.get_expr ()->accept_vis (*this);
+  }
+
+  void visit (HIR::AssignmentExpr &expr) override
+  {
+    expr.get_lhs ()->accept_vis (*this);
+    dump += " = ";
+    expr.get_rhs ()->accept_vis (*this);
+  }
+
+  void visit (HIR::LiteralExpr &expr) override
+  {
+    dump += expr.get_literal ().as_string () + ":"
+	    + type_string (expr.get_mappings ());
+  }
+
+  void visit (HIR::ArrayExpr &expr) override
+  {
+    dump += type_string (expr.get_mappings ()) + ":[";
+
+    HIR::ArrayElems *elements = expr.get_internal_elements ();
+    elements->accept_vis (*this);
+
+    dump += "]";
+  }
+
+  void visit (HIR::ArrayElemsValues &elems) override
+  {
+    for (auto &elem : elems.get_values ())
+      {
+	elem->accept_vis (*this);
+	dump += ",";
+      }
+  }
+
+  void visit (HIR::GroupedExpr &expr) override
+  {
+    HIR::Expr *paren_expr = expr.get_expr_in_parens ().get ();
+    dump += "(";
+    paren_expr->accept_vis (*this);
+    dump += ")";
+  }
+
+  void visit (HIR::PathInExpression &expr) override
+  {
+    dump += type_string (expr.get_mappings ());
+  }
+
+  void visit (HIR::StructExprStructFields &expr) override
+  {
+    dump += "ctor: " + type_string (expr.get_mappings ());
+  }
+
+protected:
+  std::string type_string (const Analysis::NodeMapping &mappings)
+  {
+    TyTy::BaseType *lookup = nullptr;
+    if (!context->lookup_type (mappings.get_hirid (), &lookup))
+      return "<error>";
+
+    std::string buf = "[";
+    for (auto &ref : lookup->get_combined_refs ())
+      {
+	buf += std::to_string (ref);
+	buf += ", ";
+      }
+    buf += "]";
+
+    std::string repr = lookup->as_string ();
+    return "<" + repr + " HIRID: " + std::to_string (mappings.get_hirid ())
+	   + " RF:" + std::to_string (lookup->get_ref ()) + " TF:"
+	   + std::to_string (lookup->get_ty_ref ()) + +" - " + buf + ">";
+  }
+
+  std::string indent ()
+  {
+    std::string buf;
+    for (size_t i = 0; i < indentation_level; ++i)
+      buf += "    ";
+
+    return buf;
+  }
+
+private:
+  TypeResolverDump () : TypeCheckBase (), indentation_level (0) {}
+
+  std::string dump;
+  size_t indentation_level;
+};
+
+} // namespace Resolver
+} // namespace Rust
+
+#endif // RUST_TYCHECK_DUMP
diff --git a/gcc/rust/typecheck/rust-tyctx.cc b/gcc/rust/typecheck/rust-tyctx.cc
new file mode 100644
index 00000000000..d8a49e8b9ea
--- /dev/null
+++ b/gcc/rust/typecheck/rust-tyctx.cc
@@ -0,0 +1,155 @@
+// 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/>.
+
+#include "rust-hir-type-check.h"
+
+namespace Rust {
+namespace Resolver {
+
+TypeCheckContext *
+TypeCheckContext::get ()
+{
+  static TypeCheckContext *instance;
+  if (instance == nullptr)
+    instance = new TypeCheckContext ();
+
+  return instance;
+}
+
+TypeCheckContext::TypeCheckContext () {}
+
+TypeCheckContext::~TypeCheckContext () {}
+
+bool
+TypeCheckContext::lookup_builtin (NodeId id, TyTy::BaseType **type)
+{
+  auto ref_it = node_id_refs.find (id);
+  if (ref_it == node_id_refs.end ())
+    return false;
+
+  auto it = resolved.find (ref_it->second);
+  if (it == resolved.end ())
+    return false;
+
+  *type = it->second;
+  return true;
+}
+
+bool
+TypeCheckContext::lookup_builtin (std::string name, TyTy::BaseType **type)
+{
+  for (auto &builtin : builtins)
+    {
+      if (name.compare (builtin->as_string ()) == 0)
+	{
+	  *type = builtin.get ();
+	  return true;
+	}
+    }
+  return false;
+}
+
+void
+TypeCheckContext::insert_builtin (HirId id, NodeId ref, TyTy::BaseType *type)
+{
+  node_id_refs[ref] = id;
+  resolved[id] = type;
+  builtins.push_back (std::unique_ptr<TyTy::BaseType> (type));
+}
+
+void
+TypeCheckContext::insert_type (const Analysis::NodeMapping &mappings,
+			       TyTy::BaseType *type)
+{
+  rust_assert (type != nullptr);
+  NodeId ref = mappings.get_nodeid ();
+  HirId id = mappings.get_hirid ();
+  node_id_refs[ref] = id;
+  resolved[id] = type;
+}
+
+void
+TypeCheckContext::insert_implicit_type (TyTy::BaseType *type)
+{
+  rust_assert (type != nullptr);
+  resolved[type->get_ref ()] = type;
+}
+
+void
+TypeCheckContext::insert_implicit_type (HirId id, TyTy::BaseType *type)
+{
+  rust_assert (type != nullptr);
+  resolved[id] = type;
+}
+
+bool
+TypeCheckContext::lookup_type (HirId id, TyTy::BaseType **type) const
+{
+  auto it = resolved.find (id);
+  if (it == resolved.end ())
+    return false;
+
+  *type = it->second;
+  return true;
+}
+
+void
+TypeCheckContext::insert_type_by_node_id (NodeId ref, HirId id)
+{
+  rust_assert (node_id_refs.find (ref) == node_id_refs.end ());
+  node_id_refs[ref] = id;
+}
+
+bool
+TypeCheckContext::lookup_type_by_node_id (NodeId ref, HirId *id)
+{
+  auto it = node_id_refs.find (ref);
+  if (it == node_id_refs.end ())
+    return false;
+
+  *id = it->second;
+  return true;
+}
+
+TyTy::BaseType *
+TypeCheckContext::peek_return_type ()
+{
+  return return_type_stack.back ().second;
+}
+
+void
+TypeCheckContext::push_return_type (TypeCheckContextItem item,
+				    TyTy::BaseType *return_type)
+{
+  return_type_stack.push_back ({std::move (item), return_type});
+}
+
+void
+TypeCheckContext::pop_return_type ()
+{
+  return_type_stack.pop_back ();
+}
+
+TypeCheckContextItem &
+TypeCheckContext::peek_context ()
+{
+  return return_type_stack.back ().first;
+}
+
+} // namespace Resolver
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-tyty-bounds.cc b/gcc/rust/typecheck/rust-tyty-bounds.cc
new file mode 100644
index 00000000000..7a1562ab544
--- /dev/null
+++ b/gcc/rust/typecheck/rust-tyty-bounds.cc
@@ -0,0 +1,462 @@
+// Copyright (C) 2021-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/>.
+
+#include "rust-hir-type-bounds.h"
+#include "rust-hir-trait-resolve.h"
+
+namespace Rust {
+namespace Resolver {
+
+void
+TypeBoundsProbe::scan ()
+{
+  std::vector<std::pair<HIR::TypePath *, HIR::ImplBlock *>>
+    possible_trait_paths;
+  mappings->iterate_impl_blocks (
+    [&] (HirId id, HIR::ImplBlock *impl) mutable -> bool {
+      // we are filtering for trait-impl-blocks
+      if (!impl->has_trait_ref ())
+	return true;
+
+      TyTy::BaseType *impl_type = nullptr;
+      bool ok
+	= context->lookup_type (impl->get_type ()->get_mappings ().get_hirid (),
+				&impl_type);
+      if (!ok)
+	return true;
+
+      if (!receiver->can_eq (impl_type, false))
+	{
+	  if (!impl_type->can_eq (receiver, false))
+	    return true;
+	}
+
+      possible_trait_paths.push_back ({impl->get_trait_ref ().get (), impl});
+      return true;
+    });
+
+  for (auto &path : possible_trait_paths)
+    {
+      HIR::TypePath *trait_path = path.first;
+      TraitReference *trait_ref = TraitResolver::Resolve (*trait_path);
+
+      if (!trait_ref->is_error ())
+	trait_references.push_back ({trait_ref, path.second});
+    }
+}
+
+TraitReference *
+TypeCheckBase::resolve_trait_path (HIR::TypePath &path)
+{
+  return TraitResolver::Resolve (path);
+}
+
+TyTy::TypeBoundPredicate
+TypeCheckBase::get_predicate_from_bound (HIR::TypePath &type_path)
+{
+  TraitReference *trait = resolve_trait_path (type_path);
+  if (trait->is_error ())
+    return TyTy::TypeBoundPredicate::error ();
+
+  TyTy::TypeBoundPredicate predicate (*trait, type_path.get_locus ());
+  HIR::GenericArgs args
+    = HIR::GenericArgs::create_empty (type_path.get_locus ());
+
+  auto &final_seg = type_path.get_final_segment ();
+  if (final_seg->is_generic_segment ())
+    {
+      auto final_generic_seg
+	= static_cast<HIR::TypePathSegmentGeneric *> (final_seg.get ());
+      if (final_generic_seg->has_generic_args ())
+	{
+	  args = final_generic_seg->get_generic_args ();
+	}
+    }
+
+  if (predicate.requires_generic_args ())
+    {
+      // this is applying generic arguments to a trait reference
+      predicate.apply_generic_arguments (&args);
+    }
+
+  return predicate;
+}
+
+} // namespace Resolver
+
+namespace TyTy {
+
+TypeBoundPredicate::TypeBoundPredicate (
+  const Resolver::TraitReference &trait_reference, Location locus)
+  : SubstitutionRef ({}, SubstitutionArgumentMappings::error ()),
+    reference (trait_reference.get_mappings ().get_defid ()), locus (locus),
+    error_flag (false)
+{
+  substitutions.clear ();
+  for (const auto &p : trait_reference.get_trait_substs ())
+    substitutions.push_back (p.clone ());
+
+  // we setup a dummy implict self argument
+  SubstitutionArg placeholder_self (&get_substs ().front (), nullptr);
+  used_arguments.get_mappings ().push_back (placeholder_self);
+}
+
+TypeBoundPredicate::TypeBoundPredicate (
+  DefId reference, std::vector<SubstitutionParamMapping> subst, Location locus)
+  : SubstitutionRef ({}, SubstitutionArgumentMappings::error ()),
+    reference (reference), locus (locus), error_flag (false)
+{
+  substitutions.clear ();
+  for (const auto &p : subst)
+    substitutions.push_back (p.clone ());
+
+  // we setup a dummy implict self argument
+  SubstitutionArg placeholder_self (&get_substs ().front (), nullptr);
+  used_arguments.get_mappings ().push_back (placeholder_self);
+}
+
+TypeBoundPredicate::TypeBoundPredicate (const TypeBoundPredicate &other)
+  : SubstitutionRef ({}, SubstitutionArgumentMappings::error ()),
+    reference (other.reference), locus (other.locus),
+    error_flag (other.error_flag)
+{
+  substitutions.clear ();
+  for (const auto &p : other.get_substs ())
+    substitutions.push_back (p.clone ());
+
+  std::vector<SubstitutionArg> mappings;
+  for (size_t i = 0; i < other.used_arguments.get_mappings ().size (); i++)
+    {
+      const SubstitutionArg &oa = other.used_arguments.get_mappings ().at (i);
+      SubstitutionArg arg (oa);
+      mappings.push_back (std::move (arg));
+    }
+
+  // we need to remap the argument mappings based on this copied constructor
+  std::vector<SubstitutionArg> copied_arg_mappings;
+  size_t i = 0;
+  for (const auto &m : other.used_arguments.get_mappings ())
+    {
+      TyTy::BaseType *argument
+	= m.get_tyty () == nullptr ? nullptr : m.get_tyty ()->clone ();
+      SubstitutionArg c (&substitutions.at (i++), argument);
+      copied_arg_mappings.push_back (std::move (c));
+    }
+
+  used_arguments
+    = SubstitutionArgumentMappings (copied_arg_mappings,
+				    other.used_arguments.get_locus ());
+}
+
+TypeBoundPredicate &
+TypeBoundPredicate::operator= (const TypeBoundPredicate &other)
+{
+  reference = other.reference;
+  locus = other.locus;
+  error_flag = other.error_flag;
+  used_arguments = SubstitutionArgumentMappings::error ();
+
+  substitutions.clear ();
+  for (const auto &p : other.get_substs ())
+    substitutions.push_back (p.clone ());
+
+  std::vector<SubstitutionArg> mappings;
+  for (size_t i = 0; i < other.used_arguments.get_mappings ().size (); i++)
+    {
+      const SubstitutionArg &oa = other.used_arguments.get_mappings ().at (i);
+      SubstitutionArg arg (oa);
+      mappings.push_back (std::move (arg));
+    }
+
+  // we need to remap the argument mappings based on this copied constructor
+  std::vector<SubstitutionArg> copied_arg_mappings;
+  size_t i = 0;
+  for (const auto &m : other.used_arguments.get_mappings ())
+    {
+      TyTy::BaseType *argument
+	= m.get_tyty () == nullptr ? nullptr : m.get_tyty ()->clone ();
+      SubstitutionArg c (&substitutions.at (i++), argument);
+      copied_arg_mappings.push_back (std::move (c));
+    }
+
+  used_arguments
+    = SubstitutionArgumentMappings (copied_arg_mappings,
+				    other.used_arguments.get_locus ());
+
+  return *this;
+}
+
+TypeBoundPredicate
+TypeBoundPredicate::error ()
+{
+  auto p = TypeBoundPredicate (UNKNOWN_DEFID, {}, Location ());
+  p.error_flag = true;
+  return p;
+}
+
+std::string
+TypeBoundPredicate::as_string () const
+{
+  return get ()->as_string () + subst_as_string ();
+}
+
+std::string
+TypeBoundPredicate::as_name () const
+{
+  return get ()->get_name () + subst_as_string ();
+}
+
+const Resolver::TraitReference *
+TypeBoundPredicate::get () const
+{
+  auto context = Resolver::TypeCheckContext::get ();
+
+  Resolver::TraitReference *ref = nullptr;
+  bool ok = context->lookup_trait_reference (reference, &ref);
+  rust_assert (ok);
+
+  return ref;
+}
+
+std::string
+TypeBoundPredicate::get_name () const
+{
+  return get ()->get_name ();
+}
+
+bool
+TypeBoundPredicate::is_object_safe (bool emit_error, Location locus) const
+{
+  const Resolver::TraitReference *trait = get ();
+  rust_assert (trait != nullptr);
+  return trait->is_object_safe (emit_error, locus);
+}
+
+void
+TypeBoundPredicate::apply_generic_arguments (HIR::GenericArgs *generic_args)
+{
+  // we need to get the substitutions argument mappings but also remember that
+  // we have an implicit Self argument which we must be careful to respect
+  rust_assert (!used_arguments.is_empty ());
+  rust_assert (!substitutions.empty ());
+
+  // now actually perform a substitution
+  used_arguments = get_mappings_from_generic_args (*generic_args);
+
+  error_flag |= used_arguments.is_error ();
+  auto &subst_mappings = used_arguments;
+  for (auto &sub : get_substs ())
+    {
+      SubstitutionArg arg = SubstitutionArg::error ();
+      bool ok
+	= subst_mappings.get_argument_for_symbol (sub.get_param_ty (), &arg);
+      if (ok && arg.get_tyty () != nullptr)
+	sub.fill_param_ty (subst_mappings, subst_mappings.get_locus ());
+    }
+}
+
+bool
+TypeBoundPredicate::contains_item (const std::string &search) const
+{
+  auto trait_ref = get ();
+  const Resolver::TraitItemReference *trait_item_ref = nullptr;
+  return trait_ref->lookup_trait_item (search, &trait_item_ref);
+}
+
+TypeBoundPredicateItem
+TypeBoundPredicate::lookup_associated_item (const std::string &search) const
+{
+  auto trait_ref = get ();
+  const Resolver::TraitItemReference *trait_item_ref = nullptr;
+  if (!trait_ref->lookup_trait_item (search, &trait_item_ref))
+    return TypeBoundPredicateItem::error ();
+
+  return TypeBoundPredicateItem (this, trait_item_ref);
+}
+
+TypeBoundPredicateItem
+TypeBoundPredicate::lookup_associated_item (
+  const Resolver::TraitItemReference *ref) const
+{
+  return lookup_associated_item (ref->get_identifier ());
+}
+
+BaseType *
+TypeBoundPredicateItem::get_tyty_for_receiver (const TyTy::BaseType *receiver)
+{
+  TyTy::BaseType *trait_item_tyty = get_raw_item ()->get_tyty ();
+  if (parent->get_substitution_arguments ().is_empty ())
+    return trait_item_tyty;
+
+  const Resolver::TraitItemReference *tref = get_raw_item ();
+  bool is_associated_type = tref->get_trait_item_type ();
+  if (is_associated_type)
+    return trait_item_tyty;
+
+  // set up the self mapping
+  SubstitutionArgumentMappings gargs = parent->get_substitution_arguments ();
+  rust_assert (!gargs.is_empty ());
+
+  // setup the adjusted mappings
+  std::vector<SubstitutionArg> adjusted_mappings;
+  for (size_t i = 0; i < gargs.get_mappings ().size (); i++)
+    {
+      auto &mapping = gargs.get_mappings ().at (i);
+
+      bool is_implicit_self = i == 0;
+      TyTy::BaseType *argument
+	= is_implicit_self ? receiver->clone () : mapping.get_tyty ();
+
+      SubstitutionArg arg (mapping.get_param_mapping (), argument);
+      adjusted_mappings.push_back (std::move (arg));
+    }
+
+  SubstitutionArgumentMappings adjusted (adjusted_mappings, gargs.get_locus (),
+					 gargs.get_subst_cb (),
+					 true /* trait-mode-flag */);
+  return Resolver::SubstMapperInternal::Resolve (trait_item_tyty, adjusted);
+}
+bool
+TypeBoundPredicate::is_error () const
+{
+  auto context = Resolver::TypeCheckContext::get ();
+
+  Resolver::TraitReference *ref = nullptr;
+  bool ok = context->lookup_trait_reference (reference, &ref);
+
+  return !ok || error_flag;
+}
+
+BaseType *
+TypeBoundPredicate::handle_substitions (
+  SubstitutionArgumentMappings subst_mappings)
+{
+  for (auto &sub : get_substs ())
+    {
+      if (sub.get_param_ty () == nullptr)
+	continue;
+
+      ParamType *p = sub.get_param_ty ();
+      BaseType *r = p->resolve ();
+      BaseType *s = Resolver::SubstMapperInternal::Resolve (r, subst_mappings);
+
+      p->set_ty_ref (s->get_ty_ref ());
+    }
+
+  // FIXME more error handling at some point
+  // used_arguments = subst_mappings;
+  // error_flag |= used_arguments.is_error ();
+
+  return nullptr;
+}
+
+bool
+TypeBoundPredicate::requires_generic_args () const
+{
+  if (is_error ())
+    return false;
+
+  return substitutions.size () > 1;
+}
+
+// trait item reference
+
+const Resolver::TraitItemReference *
+TypeBoundPredicateItem::get_raw_item () const
+{
+  return trait_item_ref;
+}
+
+bool
+TypeBoundPredicateItem::needs_implementation () const
+{
+  return !get_raw_item ()->is_optional ();
+}
+
+Location
+TypeBoundPredicateItem::get_locus () const
+{
+  return get_raw_item ()->get_locus ();
+}
+
+// TypeBoundsMappings
+
+TypeBoundsMappings::TypeBoundsMappings (
+  std::vector<TypeBoundPredicate> specified_bounds)
+  : specified_bounds (specified_bounds)
+{}
+
+std::vector<TypeBoundPredicate> &
+TypeBoundsMappings::get_specified_bounds ()
+{
+  return specified_bounds;
+}
+
+const std::vector<TypeBoundPredicate> &
+TypeBoundsMappings::get_specified_bounds () const
+{
+  return specified_bounds;
+}
+
+size_t
+TypeBoundsMappings::num_specified_bounds () const
+{
+  return specified_bounds.size ();
+}
+
+std::string
+TypeBoundsMappings::raw_bounds_as_string () const
+{
+  std::string buf;
+  for (size_t i = 0; i < specified_bounds.size (); i++)
+    {
+      const TypeBoundPredicate &b = specified_bounds.at (i);
+      bool has_next = (i + 1) < specified_bounds.size ();
+      buf += b.as_string () + (has_next ? " + " : "");
+    }
+  return buf;
+}
+
+std::string
+TypeBoundsMappings::bounds_as_string () const
+{
+  return "bounds:[" + raw_bounds_as_string () + "]";
+}
+
+std::string
+TypeBoundsMappings::raw_bounds_as_name () const
+{
+  std::string buf;
+  for (size_t i = 0; i < specified_bounds.size (); i++)
+    {
+      const TypeBoundPredicate &b = specified_bounds.at (i);
+      bool has_next = (i + 1) < specified_bounds.size ();
+      buf += b.as_name () + (has_next ? " + " : "");
+    }
+
+  return buf;
+}
+
+void
+TypeBoundsMappings::add_bound (TypeBoundPredicate predicate)
+{
+  specified_bounds.push_back (predicate);
+}
+
+} // namespace TyTy
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-tyty-call.cc b/gcc/rust/typecheck/rust-tyty-call.cc
new file mode 100644
index 00000000000..1ce82c943f5
--- /dev/null
+++ b/gcc/rust/typecheck/rust-tyty-call.cc
@@ -0,0 +1,263 @@
+// 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/>.
+
+#include "rust-tyty-call.h"
+#include "rust-hir-type-check-expr.h"
+
+namespace Rust {
+namespace TyTy {
+
+void
+TypeCheckCallExpr::visit (ADTType &type)
+{
+  rust_assert (!variant.is_error ());
+  if (variant.get_variant_type () != TyTy::VariantDef::VariantType::TUPLE)
+    {
+      rust_error_at (
+	call.get_locus (),
+	"expected function, tuple struct or tuple variant, found struct %<%s%>",
+	type.get_name ().c_str ());
+      return;
+    }
+
+  if (call.num_params () != variant.num_fields ())
+    {
+      rust_error_at (call.get_locus (),
+		     "unexpected number of arguments %lu expected %lu",
+		     (unsigned long) call.num_params (),
+		     (unsigned long) variant.num_fields ());
+      return;
+    }
+
+  size_t i = 0;
+  for (auto &argument : call.get_arguments ())
+    {
+      StructFieldType *field = variant.get_field_at_index (i);
+      BaseType *field_tyty = field->get_field_type ();
+
+      BaseType *arg = Resolver::TypeCheckExpr::Resolve (argument.get ());
+      if (arg->get_kind () == TyTy::TypeKind::ERROR)
+	{
+	  rust_error_at (argument->get_locus (),
+			 "failed to resolve argument type");
+	  return;
+	}
+
+      auto res = Resolver::TypeCheckBase::coercion_site (
+	argument->get_mappings ().get_hirid (), field_tyty, arg,
+	argument->get_locus ());
+      if (res->get_kind () == TyTy::TypeKind::ERROR)
+	{
+	  return;
+	}
+
+      delete res;
+      i++;
+    }
+
+  if (i != call.num_params ())
+    {
+      rust_error_at (call.get_locus (),
+		     "unexpected number of arguments %lu expected %lu",
+		     (unsigned long) i, (unsigned long) call.num_params ());
+      return;
+    }
+
+  resolved = type.clone ();
+}
+
+void
+TypeCheckCallExpr::visit (FnType &type)
+{
+  type.monomorphize ();
+  if (call.num_params () != type.num_params ())
+    {
+      if (type.is_varadic ())
+	{
+	  if (call.num_params () < type.num_params ())
+	    {
+	      rust_error_at (call.get_locus (),
+			     "unexpected number of arguments %lu expected %lu",
+			     (unsigned long) call.num_params (),
+			     (unsigned long) type.num_params ());
+	      return;
+	    }
+	}
+      else
+	{
+	  rust_error_at (call.get_locus (),
+			 "unexpected number of arguments %lu expected %lu",
+			 (unsigned long) call.num_params (),
+			 (unsigned long) type.num_params ());
+	  return;
+	}
+    }
+
+  size_t i = 0;
+  for (auto &argument : call.get_arguments ())
+    {
+      auto argument_expr_tyty
+	= Resolver::TypeCheckExpr::Resolve (argument.get ());
+      if (argument_expr_tyty->get_kind () == TyTy::TypeKind::ERROR)
+	{
+	  rust_error_at (
+	    argument->get_locus (),
+	    "failed to resolve type for argument expr in CallExpr");
+	  return;
+	}
+
+      // it might be a varadic function
+      if (i < type.num_params ())
+	{
+	  auto fnparam = type.param_at (i);
+	  auto resolved_argument_type = Resolver::TypeCheckBase::coercion_site (
+	    argument->get_mappings ().get_hirid (), fnparam.second,
+	    argument_expr_tyty, argument->get_locus ());
+	  if (resolved_argument_type->get_kind () == TyTy::TypeKind::ERROR)
+	    {
+	      rust_error_at (argument->get_locus (),
+			     "Type Resolution failure on parameter");
+	      return;
+	    }
+	}
+
+      i++;
+    }
+
+  if (i < call.num_params ())
+    {
+      rust_error_at (call.get_locus (),
+		     "unexpected number of arguments %lu expected %lu",
+		     (unsigned long) i, (unsigned long) call.num_params ());
+      return;
+    }
+
+  type.monomorphize ();
+  resolved = type.get_return_type ()->clone ();
+}
+
+void
+TypeCheckCallExpr::visit (FnPtr &type)
+{
+  if (call.num_params () != type.num_params ())
+    {
+      rust_error_at (call.get_locus (),
+		     "unexpected number of arguments %lu expected %lu",
+		     (unsigned long) call.num_params (),
+		     (unsigned long) type.num_params ());
+      return;
+    }
+
+  size_t i = 0;
+  for (auto &argument : call.get_arguments ())
+    {
+      auto fnparam = type.param_at (i);
+      auto argument_expr_tyty
+	= Resolver::TypeCheckExpr::Resolve (argument.get ());
+      if (argument_expr_tyty->get_kind () == TyTy::TypeKind::ERROR)
+	{
+	  rust_error_at (
+	    argument->get_locus (),
+	    "failed to resolve type for argument expr in CallExpr");
+	  return;
+	}
+
+      auto resolved_argument_type = Resolver::TypeCheckBase::coercion_site (
+	argument->get_mappings ().get_hirid (), fnparam, argument_expr_tyty,
+	argument->get_locus ());
+      if (resolved_argument_type->get_kind () == TyTy::TypeKind::ERROR)
+	{
+	  rust_error_at (argument->get_locus (),
+			 "Type Resolution failure on parameter");
+	  return;
+	}
+
+      i++;
+    }
+
+  if (i != call.num_params ())
+    {
+      rust_error_at (call.get_locus (),
+		     "unexpected number of arguments %lu expected %lu",
+		     (unsigned long) i, (unsigned long) call.num_params ());
+      return;
+    }
+
+  resolved = type.get_return_type ()->monomorphized_clone ();
+}
+
+// method call checker
+
+void
+TypeCheckMethodCallExpr::visit (FnType &type)
+{
+  type.get_self_type ()->unify (adjusted_self);
+
+  // +1 for the receiver self
+  size_t num_args_to_call = call.num_params () + 1;
+  if (num_args_to_call != type.num_params ())
+    {
+      rust_error_at (call.get_locus (),
+		     "unexpected number of arguments %lu expected %lu",
+		     (unsigned long) call.num_params (),
+		     (unsigned long) type.num_params ());
+      return;
+    }
+
+  size_t i = 1;
+  for (auto &argument : call.get_arguments ())
+    {
+      auto fnparam = type.param_at (i);
+      auto argument_expr_tyty
+	= Resolver::TypeCheckExpr::Resolve (argument.get ());
+      if (argument_expr_tyty->get_kind () == TyTy::TypeKind::ERROR)
+	{
+	  rust_error_at (
+	    argument->get_locus (),
+	    "failed to resolve type for argument expr in CallExpr");
+	  return;
+	}
+
+      auto resolved_argument_type = Resolver::TypeCheckBase::coercion_site (
+	argument->get_mappings ().get_hirid (), fnparam.second,
+	argument_expr_tyty, argument->get_locus ());
+      if (resolved_argument_type->get_kind () == TyTy::TypeKind::ERROR)
+	{
+	  rust_error_at (argument->get_locus (),
+			 "Type Resolution failure on parameter");
+	  return;
+	}
+
+      i++;
+    }
+
+  if (i != num_args_to_call)
+    {
+      rust_error_at (call.get_locus (),
+		     "unexpected number of arguments %lu expected %lu",
+		     (unsigned long) i, (unsigned long) call.num_params ());
+      return;
+    }
+
+  type.monomorphize ();
+
+  resolved = type.get_return_type ()->monomorphized_clone ();
+}
+
+} // namespace TyTy
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-tyty-call.h b/gcc/rust/typecheck/rust-tyty-call.h
new file mode 100644
index 00000000000..51817e6446d
--- /dev/null
+++ b/gcc/rust/typecheck/rust-tyty-call.h
@@ -0,0 +1,147 @@
+// 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_TYTY_CALL
+#define RUST_TYTY_CALL
+
+#include "rust-diagnostics.h"
+#include "rust-hir-full.h"
+#include "rust-tyty-visitor.h"
+#include "rust-tyty.h"
+#include "rust-hir-type-check.h"
+
+namespace Rust {
+namespace TyTy {
+
+class TypeCheckCallExpr : private TyVisitor
+{
+public:
+  static BaseType *go (BaseType *ref, HIR::CallExpr &call,
+		       TyTy::VariantDef &variant,
+		       Resolver::TypeCheckContext *context)
+  {
+    TypeCheckCallExpr checker (call, variant, context);
+    ref->accept_vis (checker);
+    return checker.resolved;
+  }
+
+  void visit (InferType &) override { gcc_unreachable (); }
+  void visit (TupleType &) override { gcc_unreachable (); }
+  void visit (ArrayType &) override { gcc_unreachable (); }
+  void visit (SliceType &) override { gcc_unreachable (); }
+  void visit (BoolType &) override { gcc_unreachable (); }
+  void visit (IntType &) override { gcc_unreachable (); }
+  void visit (UintType &) override { gcc_unreachable (); }
+  void visit (FloatType &) override { gcc_unreachable (); }
+  void visit (USizeType &) override { gcc_unreachable (); }
+  void visit (ISizeType &) override { gcc_unreachable (); }
+  void visit (ErrorType &) override { gcc_unreachable (); }
+  void visit (CharType &) override { gcc_unreachable (); }
+  void visit (ReferenceType &) override { gcc_unreachable (); }
+  void visit (PointerType &) override { gcc_unreachable (); }
+  void visit (ParamType &) override { gcc_unreachable (); }
+  void visit (StrType &) override { gcc_unreachable (); }
+  void visit (NeverType &) override { gcc_unreachable (); }
+  void visit (PlaceholderType &) override { gcc_unreachable (); }
+  void visit (ProjectionType &) override { gcc_unreachable (); }
+  void visit (DynamicObjectType &) override { gcc_unreachable (); }
+  void visit (ClosureType &type) override { gcc_unreachable (); }
+
+  // tuple-structs
+  void visit (ADTType &type) override;
+
+  // call fns
+  void visit (FnType &type) override;
+  void visit (FnPtr &type) override;
+
+private:
+  TypeCheckCallExpr (HIR::CallExpr &c, TyTy::VariantDef &variant,
+		     Resolver::TypeCheckContext *context)
+    : resolved (new TyTy::ErrorType (c.get_mappings ().get_hirid ())), call (c),
+      variant (variant), context (context),
+      mappings (Analysis::Mappings::get ())
+  {}
+
+  BaseType *resolved;
+  HIR::CallExpr &call;
+  TyTy::VariantDef &variant;
+  Resolver::TypeCheckContext *context;
+  Analysis::Mappings *mappings;
+};
+
+class TypeCheckMethodCallExpr : private TyVisitor
+{
+public:
+  // Resolve the Method parameters and return back the return type
+  static BaseType *go (BaseType *ref, HIR::MethodCallExpr &call,
+		       TyTy::BaseType *adjusted_self,
+		       Resolver::TypeCheckContext *context)
+  {
+    TypeCheckMethodCallExpr checker (call, adjusted_self, context);
+    ref->accept_vis (checker);
+    return checker.resolved;
+  }
+
+  void visit (InferType &) override { gcc_unreachable (); }
+  void visit (TupleType &) override { gcc_unreachable (); }
+  void visit (ArrayType &) override { gcc_unreachable (); }
+  void visit (SliceType &) override { gcc_unreachable (); }
+  void visit (BoolType &) override { gcc_unreachable (); }
+  void visit (IntType &) override { gcc_unreachable (); }
+  void visit (UintType &) override { gcc_unreachable (); }
+  void visit (FloatType &) override { gcc_unreachable (); }
+  void visit (USizeType &) override { gcc_unreachable (); }
+  void visit (ISizeType &) override { gcc_unreachable (); }
+  void visit (ErrorType &) override { gcc_unreachable (); }
+  void visit (ADTType &) override { gcc_unreachable (); };
+  void visit (CharType &) override { gcc_unreachable (); }
+  void visit (ReferenceType &) override { gcc_unreachable (); }
+  void visit (PointerType &) override { gcc_unreachable (); }
+  void visit (ParamType &) override { gcc_unreachable (); }
+  void visit (StrType &) override { gcc_unreachable (); }
+  void visit (NeverType &) override { gcc_unreachable (); }
+  void visit (PlaceholderType &) override { gcc_unreachable (); }
+  void visit (ProjectionType &) override { gcc_unreachable (); }
+  void visit (DynamicObjectType &) override { gcc_unreachable (); }
+
+  // FIXME
+  void visit (FnPtr &type) override { gcc_unreachable (); }
+
+  // call fns
+  void visit (FnType &type) override;
+  void visit (ClosureType &type) override { gcc_unreachable (); }
+
+private:
+  TypeCheckMethodCallExpr (HIR::MethodCallExpr &c,
+			   TyTy::BaseType *adjusted_self,
+			   Resolver::TypeCheckContext *context)
+    : resolved (nullptr), call (c), adjusted_self (adjusted_self),
+      context (context), mappings (Analysis::Mappings::get ())
+  {}
+
+  BaseType *resolved;
+  HIR::MethodCallExpr &call;
+  TyTy::BaseType *adjusted_self;
+  Resolver::TypeCheckContext *context;
+  Analysis::Mappings *mappings;
+};
+
+} // namespace TyTy
+} // namespace Rust
+
+#endif // RUST_TYTY_CALL
diff --git a/gcc/rust/typecheck/rust-tyty-cmp.h b/gcc/rust/typecheck/rust-tyty-cmp.h
new file mode 100644
index 00000000000..07d1dea7464
--- /dev/null
+++ b/gcc/rust/typecheck/rust-tyty-cmp.h
@@ -0,0 +1,1554 @@
+// 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_TYTY_CMP_H
+#define RUST_TYTY_CMP_H
+
+#include "rust-diagnostics.h"
+#include "rust-tyty.h"
+#include "rust-tyty-visitor.h"
+#include "rust-hir-map.h"
+#include "rust-hir-type-check.h"
+
+namespace Rust {
+namespace TyTy {
+
+class BaseCmp : public TyConstVisitor
+{
+public:
+  virtual bool can_eq (const BaseType *other)
+  {
+    if (other->get_kind () == TypeKind::PARAM)
+      {
+	const ParamType *p = static_cast<const ParamType *> (other);
+	other = p->resolve ();
+      }
+    if (other->get_kind () == TypeKind::PLACEHOLDER)
+      {
+	const PlaceholderType *p = static_cast<const PlaceholderType *> (other);
+	if (p->can_resolve ())
+	  {
+	    other = p->resolve ();
+	  }
+      }
+    if (other->get_kind () == TypeKind::PROJECTION)
+      {
+	const ProjectionType *p = static_cast<const ProjectionType *> (other);
+	other = p->get ();
+      }
+
+    other->accept_vis (*this);
+    return ok;
+  }
+
+  virtual void visit (const TupleType &type) override
+  {
+    ok = false;
+
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const ADTType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const InferType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const FnType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const FnPtr &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const ArrayType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const SliceType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const BoolType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const IntType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const UintType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const USizeType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const ISizeType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const FloatType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const ErrorType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const CharType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const ReferenceType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const PointerType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const StrType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const NeverType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const ProjectionType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const PlaceholderType &type) override
+  {
+    // it is ok for types to can eq to a placeholder
+    ok = true;
+  }
+
+  virtual void visit (const ParamType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const DynamicObjectType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+  virtual void visit (const ClosureType &type) override
+  {
+    ok = false;
+    if (emit_error_flag)
+      {
+	Location ref_locus = mappings->lookup_location (type.get_ref ());
+	Location base_locus
+	  = mappings->lookup_location (get_base ()->get_ref ());
+	RichLocation r (ref_locus);
+	r.add_range (base_locus);
+	rust_error_at (r, "expected [%s] got [%s]",
+		       get_base ()->as_string ().c_str (),
+		       type.as_string ().c_str ());
+      }
+  }
+
+protected:
+  BaseCmp (const BaseType *base, bool emit_errors)
+    : mappings (Analysis::Mappings::get ()),
+      context (Resolver::TypeCheckContext::get ()), ok (false),
+      emit_error_flag (emit_errors)
+  {}
+
+  Analysis::Mappings *mappings;
+  Resolver::TypeCheckContext *context;
+
+  bool ok;
+  bool emit_error_flag;
+
+private:
+  /* Returns a pointer to the ty that created this rule. */
+  virtual const BaseType *get_base () const = 0;
+};
+
+class InferCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  InferCmp (const InferType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const BoolType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+    if (is_valid)
+      {
+	ok = true;
+	return;
+      }
+
+    BaseCmp::visit (type);
+  }
+
+  void visit (const IntType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL)
+	|| (base->get_infer_kind ()
+	    == TyTy::InferType::InferTypeKind::INTEGRAL);
+    if (is_valid)
+      {
+	ok = true;
+	return;
+      }
+
+    BaseCmp::visit (type);
+  }
+
+  void visit (const UintType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL)
+	|| (base->get_infer_kind ()
+	    == TyTy::InferType::InferTypeKind::INTEGRAL);
+    if (is_valid)
+      {
+	ok = true;
+	return;
+      }
+
+    BaseCmp::visit (type);
+  }
+
+  void visit (const USizeType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL)
+	|| (base->get_infer_kind ()
+	    == TyTy::InferType::InferTypeKind::INTEGRAL);
+    if (is_valid)
+      {
+	ok = true;
+	return;
+      }
+
+    BaseCmp::visit (type);
+  }
+
+  void visit (const ISizeType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL)
+	|| (base->get_infer_kind ()
+	    == TyTy::InferType::InferTypeKind::INTEGRAL);
+    if (is_valid)
+      {
+	ok = true;
+	return;
+      }
+
+    BaseCmp::visit (type);
+  }
+
+  void visit (const FloatType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL)
+	|| (base->get_infer_kind () == TyTy::InferType::InferTypeKind::FLOAT);
+    if (is_valid)
+      {
+	ok = true;
+	return;
+      }
+
+    BaseCmp::visit (type);
+  }
+
+  void visit (const ArrayType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+    if (is_valid)
+      {
+	ok = true;
+	return;
+      }
+
+    BaseCmp::visit (type);
+  }
+
+  void visit (const SliceType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+    if (is_valid)
+      {
+	ok = true;
+	return;
+      }
+
+    BaseCmp::visit (type);
+  }
+
+  void visit (const ADTType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+    if (is_valid)
+      {
+	ok = true;
+	return;
+      }
+
+    BaseCmp::visit (type);
+  }
+
+  void visit (const TupleType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+    if (is_valid)
+      {
+	ok = true;
+	return;
+      }
+
+    BaseCmp::visit (type);
+  }
+
+  void visit (const InferType &type) override
+  {
+    switch (base->get_infer_kind ())
+      {
+      case InferType::InferTypeKind::GENERAL:
+	ok = true;
+	return;
+
+	case InferType::InferTypeKind::INTEGRAL: {
+	  if (type.get_infer_kind () == InferType::InferTypeKind::INTEGRAL)
+	    {
+	      ok = true;
+	      return;
+	    }
+	  else if (type.get_infer_kind () == InferType::InferTypeKind::GENERAL)
+	    {
+	      ok = true;
+	      return;
+	    }
+	}
+	break;
+
+	case InferType::InferTypeKind::FLOAT: {
+	  if (type.get_infer_kind () == InferType::InferTypeKind::FLOAT)
+	    {
+	      ok = true;
+	      return;
+	    }
+	  else if (type.get_infer_kind () == InferType::InferTypeKind::GENERAL)
+	    {
+	      ok = true;
+	      return;
+	    }
+	}
+	break;
+      }
+
+    BaseCmp::visit (type);
+  }
+
+  void visit (const CharType &type) override
+  {
+    {
+      bool is_valid
+	= (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+      if (is_valid)
+	{
+	  ok = true;
+	  return;
+	}
+
+      BaseCmp::visit (type);
+    }
+  }
+
+  void visit (const ReferenceType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+    if (is_valid)
+      {
+	ok = true;
+	return;
+      }
+
+    BaseCmp::visit (type);
+  }
+
+  void visit (const PointerType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+    if (is_valid)
+      {
+	ok = true;
+	return;
+      }
+
+    BaseCmp::visit (type);
+  }
+
+  void visit (const ParamType &) override { ok = true; }
+
+  void visit (const DynamicObjectType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+    if (is_valid)
+      {
+	ok = true;
+	return;
+      }
+
+    BaseCmp::visit (type);
+  }
+
+  void visit (const ClosureType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+    if (is_valid)
+      {
+	ok = true;
+	return;
+      }
+
+    BaseCmp::visit (type);
+  }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const InferType *base;
+};
+
+class FnCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  FnCmp (const FnType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const InferType &type) override
+  {
+    ok = type.get_infer_kind () == InferType::InferTypeKind::GENERAL;
+  }
+
+  void visit (const FnType &type) override
+  {
+    if (base->num_params () != type.num_params ())
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    for (size_t i = 0; i < base->num_params (); i++)
+      {
+	auto a = base->param_at (i).second;
+	auto b = type.param_at (i).second;
+
+	if (!a->can_eq (b, emit_error_flag))
+	  {
+	    emit_error_flag = false;
+	    BaseCmp::visit (type);
+	    return;
+	  }
+      }
+
+    if (!base->get_return_type ()->can_eq (type.get_return_type (),
+					   emit_error_flag))
+      {
+	emit_error_flag = false;
+	BaseCmp::visit (type);
+	return;
+      }
+
+    ok = true;
+  }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const FnType *base;
+};
+
+class FnptrCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  FnptrCmp (const FnPtr *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    ok = true;
+  }
+
+  void visit (const FnPtr &type) override
+  {
+    if (base->num_params () != type.num_params ())
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    auto this_ret_type = base->get_return_type ();
+    auto other_ret_type = type.get_return_type ();
+    if (!this_ret_type->can_eq (other_ret_type, emit_error_flag))
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    for (size_t i = 0; i < base->num_params (); i++)
+      {
+	auto this_param = base->param_at (i);
+	auto other_param = type.param_at (i);
+	if (!this_param->can_eq (other_param, emit_error_flag))
+	  {
+	    BaseCmp::visit (type);
+	    return;
+	  }
+      }
+
+    ok = true;
+  }
+
+  void visit (const FnType &type) override
+  {
+    if (base->num_params () != type.num_params ())
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    auto this_ret_type = base->get_return_type ();
+    auto other_ret_type = type.get_return_type ();
+    if (!this_ret_type->can_eq (other_ret_type, emit_error_flag))
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    for (size_t i = 0; i < base->num_params (); i++)
+      {
+	auto this_param = base->param_at (i);
+	auto other_param = type.param_at (i).second;
+	if (!this_param->can_eq (other_param, emit_error_flag))
+	  {
+	    BaseCmp::visit (type);
+	    return;
+	  }
+      }
+
+    ok = true;
+  }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const FnPtr *base;
+};
+
+class ClosureCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  ClosureCmp (const ClosureType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    ok = true;
+  }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const ClosureType *base;
+};
+
+class ArrayCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  ArrayCmp (const ArrayType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const ArrayType &type) override
+  {
+    // check base type
+    const BaseType *base_element = base->get_element_type ();
+    const BaseType *other_element = type.get_element_type ();
+    if (!base_element->can_eq (other_element, emit_error_flag))
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    ok = true;
+  }
+
+  void visit (const InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    ok = true;
+  }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const ArrayType *base;
+};
+
+class SliceCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  SliceCmp (const SliceType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const SliceType &type) override
+  {
+    // check base type
+    const BaseType *base_element = base->get_element_type ();
+    const BaseType *other_element = type.get_element_type ();
+    if (!base_element->can_eq (other_element, emit_error_flag))
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    ok = true;
+  }
+
+  void visit (const InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    ok = true;
+  }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const SliceType *base;
+};
+
+class BoolCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  BoolCmp (const BoolType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const BoolType &type) override { ok = true; }
+
+  void visit (const InferType &type) override
+  {
+    ok = type.get_infer_kind () == InferType::InferTypeKind::GENERAL;
+  }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const BoolType *base;
+};
+
+class IntCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  IntCmp (const IntType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const InferType &type) override
+  {
+    ok = type.get_infer_kind () != InferType::InferTypeKind::FLOAT;
+  }
+
+  void visit (const IntType &type) override
+  {
+    ok = type.get_int_kind () == base->get_int_kind ();
+  }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const IntType *base;
+};
+
+class UintCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  UintCmp (const UintType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const InferType &type) override
+  {
+    ok = type.get_infer_kind () != InferType::InferTypeKind::FLOAT;
+  }
+
+  void visit (const UintType &type) override
+  {
+    ok = type.get_uint_kind () == base->get_uint_kind ();
+  }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const UintType *base;
+};
+
+class FloatCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  FloatCmp (const FloatType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const InferType &type) override
+  {
+    ok = type.get_infer_kind () != InferType::InferTypeKind::INTEGRAL;
+  }
+
+  void visit (const FloatType &type) override
+  {
+    ok = type.get_float_kind () == base->get_float_kind ();
+  }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const FloatType *base;
+};
+
+class ADTCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  ADTCmp (const ADTType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const ADTType &type) override
+  {
+    if (base->get_adt_kind () != type.get_adt_kind ())
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    if (base->get_identifier ().compare (type.get_identifier ()) != 0)
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    if (base->number_of_variants () != type.number_of_variants ())
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    for (size_t i = 0; i < type.number_of_variants (); ++i)
+      {
+	TyTy::VariantDef *a = base->get_variants ().at (i);
+	TyTy::VariantDef *b = type.get_variants ().at (i);
+
+	if (a->num_fields () != b->num_fields ())
+	  {
+	    BaseCmp::visit (type);
+	    return;
+	  }
+
+	for (size_t j = 0; j < a->num_fields (); j++)
+	  {
+	    TyTy::StructFieldType *base_field = a->get_field_at_index (j);
+	    TyTy::StructFieldType *other_field = b->get_field_at_index (j);
+
+	    TyTy::BaseType *this_field_ty = base_field->get_field_type ();
+	    TyTy::BaseType *other_field_ty = other_field->get_field_type ();
+
+	    if (!this_field_ty->can_eq (other_field_ty, emit_error_flag))
+	      {
+		BaseCmp::visit (type);
+		return;
+	      }
+	  }
+      }
+
+    ok = true;
+  }
+
+  void visit (const InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    ok = true;
+  }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const ADTType *base;
+};
+
+class TupleCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  TupleCmp (const TupleType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const TupleType &type) override
+  {
+    if (base->num_fields () != type.num_fields ())
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    for (size_t i = 0; i < base->num_fields (); i++)
+      {
+	BaseType *bo = base->get_field (i);
+	BaseType *fo = type.get_field (i);
+
+	if (!bo->can_eq (fo, emit_error_flag))
+	  {
+	    BaseCmp::visit (type);
+	    return;
+	  }
+      }
+
+    ok = true;
+  }
+
+  void visit (const InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    ok = true;
+  }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const TupleType *base;
+};
+
+class USizeCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  USizeCmp (const USizeType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const InferType &type) override
+  {
+    ok = type.get_infer_kind () != InferType::InferTypeKind::FLOAT;
+  }
+
+  void visit (const USizeType &type) override { ok = true; }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const USizeType *base;
+};
+
+class ISizeCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  ISizeCmp (const ISizeType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const InferType &type) override
+  {
+    ok = type.get_infer_kind () != InferType::InferTypeKind::FLOAT;
+  }
+
+  void visit (const ISizeType &type) override { ok = true; }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const ISizeType *base;
+};
+
+class CharCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  CharCmp (const CharType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const InferType &type) override
+  {
+    ok = type.get_infer_kind () == InferType::InferTypeKind::GENERAL;
+  }
+
+  void visit (const CharType &type) override { ok = true; }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const CharType *base;
+};
+
+class ReferenceCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  ReferenceCmp (const ReferenceType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const ReferenceType &type) override
+  {
+    auto base_type = base->get_base ();
+    auto other_base_type = type.get_base ();
+
+    bool mutability_match = base->is_mutable () == type.is_mutable ();
+    if (!mutability_match)
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    if (!base_type->can_eq (other_base_type, emit_error_flag))
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    ok = true;
+  }
+
+  void visit (const InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    ok = true;
+  }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const ReferenceType *base;
+};
+
+class PointerCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  PointerCmp (const PointerType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const PointerType &type) override
+  {
+    auto base_type = base->get_base ();
+    auto other_base_type = type.get_base ();
+
+    // rust is permissive about mutablity here you can always go from mutable to
+    // immutable but not the otherway round
+    bool mutability_ok = base->is_mutable () ? type.is_mutable () : true;
+    if (!mutability_ok)
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    if (!base_type->can_eq (other_base_type, emit_error_flag))
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    ok = true;
+  }
+
+  void visit (const InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    ok = true;
+  }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const PointerType *base;
+};
+
+class ParamCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  ParamCmp (const ParamType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  // param types are a placeholder we shouldn't have cases where we unify
+  // against it. eg: struct foo<T> { a: T }; When we invoke it we can do either:
+  //
+  // foo<i32>{ a: 123 }.
+  // Then this enforces the i32 type to be referenced on the
+  // field via an hirid.
+  //
+  // rust also allows for a = foo{a:123}; Where we can use an Inference Variable
+  // to handle the typing of the struct
+  bool can_eq (const BaseType *other) override
+  {
+    if (!base->can_resolve ())
+      return BaseCmp::can_eq (other);
+
+    auto lookup = base->resolve ();
+    return lookup->can_eq (other, emit_error_flag);
+  }
+
+  // imagine the case where we have:
+  // struct Foo<T>(T);
+  // Then we declare a generic impl block
+  // impl <X>Foo<X> { ... }
+  // both of these types are compatible so we mostly care about the number of
+  // generic arguments
+  void visit (const ParamType &) override { ok = true; }
+
+  void visit (const InferType &) override { ok = true; }
+
+  void visit (const FnType &) override { ok = true; }
+
+  void visit (const FnPtr &) override { ok = true; }
+
+  void visit (const ADTType &) override { ok = true; }
+
+  void visit (const ArrayType &) override { ok = true; }
+
+  void visit (const SliceType &) override { ok = true; }
+
+  void visit (const BoolType &) override { ok = true; }
+
+  void visit (const IntType &) override { ok = true; }
+
+  void visit (const UintType &) override { ok = true; }
+
+  void visit (const USizeType &) override { ok = true; }
+
+  void visit (const ISizeType &) override { ok = true; }
+
+  void visit (const FloatType &) override { ok = true; }
+
+  void visit (const CharType &) override { ok = true; }
+
+  void visit (const ReferenceType &) override { ok = true; }
+
+  void visit (const PointerType &) override { ok = true; }
+
+  void visit (const StrType &) override { ok = true; }
+
+  void visit (const NeverType &) override { ok = true; }
+
+  void visit (const DynamicObjectType &) override { ok = true; }
+
+  void visit (const PlaceholderType &type) override
+  {
+    ok = base->get_symbol ().compare (type.get_symbol ()) == 0;
+  }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const ParamType *base;
+};
+
+class StrCmp : public BaseCmp
+{
+  // FIXME we will need a enum for the StrType like ByteBuf etc..
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  StrCmp (const StrType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const StrType &type) override { ok = true; }
+
+  void visit (const InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    ok = true;
+  }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const StrType *base;
+};
+
+class NeverCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  NeverCmp (const NeverType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const NeverType &type) override { ok = true; }
+
+  void visit (const InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    ok = true;
+  }
+
+private:
+  const BaseType *get_base () const override { return base; }
+  const NeverType *base;
+};
+
+class PlaceholderCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  PlaceholderCmp (const PlaceholderType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  bool can_eq (const BaseType *other) override
+  {
+    if (!base->can_resolve ())
+      return BaseCmp::can_eq (other);
+
+    BaseType *lookup = base->resolve ();
+    return lookup->can_eq (other, emit_error_flag);
+  }
+
+  void visit (const TupleType &) override { ok = true; }
+
+  void visit (const ADTType &) override { ok = true; }
+
+  void visit (const InferType &) override { ok = true; }
+
+  void visit (const FnType &) override { ok = true; }
+
+  void visit (const FnPtr &) override { ok = true; }
+
+  void visit (const ArrayType &) override { ok = true; }
+
+  void visit (const BoolType &) override { ok = true; }
+
+  void visit (const IntType &) override { ok = true; }
+
+  void visit (const UintType &) override { ok = true; }
+
+  void visit (const USizeType &) override { ok = true; }
+
+  void visit (const ISizeType &) override { ok = true; }
+
+  void visit (const FloatType &) override { ok = true; }
+
+  void visit (const ErrorType &) override { ok = true; }
+
+  void visit (const CharType &) override { ok = true; }
+
+  void visit (const ReferenceType &) override { ok = true; }
+
+  void visit (const ParamType &) override { ok = true; }
+
+  void visit (const StrType &) override { ok = true; }
+
+  void visit (const NeverType &) override { ok = true; }
+
+  void visit (const SliceType &) override { ok = true; }
+
+private:
+  const BaseType *get_base () const override { return base; }
+
+  const PlaceholderType *base;
+};
+
+class DynamicCmp : public BaseCmp
+{
+  using Rust::TyTy::BaseCmp::visit;
+
+public:
+  DynamicCmp (const DynamicObjectType *base, bool emit_errors)
+    : BaseCmp (base, emit_errors), base (base)
+  {}
+
+  void visit (const DynamicObjectType &type) override
+  {
+    if (base->num_specified_bounds () != type.num_specified_bounds ())
+      {
+	BaseCmp::visit (type);
+	return;
+      }
+
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    ok = base->bounds_compatible (type, ref_locus, false);
+  }
+
+private:
+  const BaseType *get_base () const override { return base; }
+
+  const DynamicObjectType *base;
+};
+
+} // namespace TyTy
+} // namespace Rust
+
+#endif // RUST_TYTY_CMP_H
diff --git a/gcc/rust/typecheck/rust-tyty-rules.h b/gcc/rust/typecheck/rust-tyty-rules.h
new file mode 100644
index 00000000000..77d912a5921
--- /dev/null
+++ b/gcc/rust/typecheck/rust-tyty-rules.h
@@ -0,0 +1,1584 @@
+// 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_TYTY_RULES
+#define RUST_TYTY_RULES
+
+#include "rust-diagnostics.h"
+#include "rust-tyty.h"
+#include "rust-tyty-visitor.h"
+#include "rust-hir-map.h"
+#include "rust-hir-type-check.h"
+
+namespace Rust {
+namespace TyTy {
+
+/* Rules specify how to unify two Ty. For example, the result of unifying the
+   two tuples (u64, A) and (B, i64) would be (u64, i64).
+
+   Performing a unification requires a double dispatch. To illustrate, suppose
+   we want to unify `ty1` and `ty2`. Here's what it looks like:
+     1. The caller calls `ty1.unify(ty2)`. This is the first dispatch.
+     2. `ty1` creates a rule specific to its type(e.g. TupleRules).
+     3. The rule calls `ty2.accept_vis(rule)`. This is the second dispatch.
+     4. `ty2` calls `rule.visit(*this)`, which will method-overload to the
+	      correct implementation at compile time.
+
+   The nice thing about Rules is that they seperate unification logic from the
+   representation of Ty. To support unifying a new Ty, implement its
+   `accept_vis` and `unify` method to pass the unification request to Rules.
+   Then, create a new `XXXRules` class and implement one `visit` method for
+   every Ty it can unify with. */
+class BaseRules : public TyVisitor
+{
+public:
+  virtual ~BaseRules () {}
+
+  /* Unify two ty. Returns a pointer to the newly-created unified ty, or nullptr
+     if the two types cannot be unified. The caller is responsible for releasing
+     the memory of the returned ty.
+
+     This method is meant to be used internally by Ty. If you're trying to unify
+     two ty, you can simply call `unify` on ty themselves. */
+  virtual BaseType *unify (BaseType *other)
+  {
+    if (other->get_kind () == TypeKind::PARAM)
+      {
+	ParamType *p = static_cast<ParamType *> (other);
+	other = p->resolve ();
+      }
+    else if (other->get_kind () == TypeKind::PLACEHOLDER)
+      {
+	PlaceholderType *p = static_cast<PlaceholderType *> (other);
+	if (p->can_resolve ())
+	  {
+	    other = p->resolve ();
+	    return get_base ()->unify (other);
+	  }
+      }
+    else if (other->get_kind () == TypeKind::PROJECTION)
+      {
+	ProjectionType *p = static_cast<ProjectionType *> (other);
+	other = p->get ();
+	return get_base ()->unify (other);
+      }
+
+    other->accept_vis (*this);
+    if (resolved->get_kind () == TyTy::TypeKind::ERROR)
+      return resolved;
+
+    resolved->append_reference (get_base ()->get_ref ());
+    resolved->append_reference (other->get_ref ());
+    for (auto ref : get_base ()->get_combined_refs ())
+      resolved->append_reference (ref);
+    for (auto ref : other->get_combined_refs ())
+      resolved->append_reference (ref);
+
+    other->append_reference (resolved->get_ref ());
+    other->append_reference (get_base ()->get_ref ());
+    get_base ()->append_reference (resolved->get_ref ());
+    get_base ()->append_reference (other->get_ref ());
+
+    bool result_resolved = resolved->get_kind () != TyTy::TypeKind::INFER;
+    bool result_is_infer_var = resolved->get_kind () == TyTy::TypeKind::INFER;
+    bool results_is_non_general_infer_var
+      = (result_is_infer_var
+	 && (static_cast<InferType *> (resolved))->get_infer_kind ()
+	      != TyTy::InferType::GENERAL);
+    if (result_resolved || results_is_non_general_infer_var)
+      {
+	for (auto &ref : resolved->get_combined_refs ())
+	  {
+	    TyTy::BaseType *ref_tyty = nullptr;
+	    bool ok = context->lookup_type (ref, &ref_tyty);
+	    if (!ok)
+	      continue;
+
+	    // if any of the types are inference variables lets fix them
+	    if (ref_tyty->get_kind () == TyTy::TypeKind::INFER)
+	      {
+		context->insert_type (
+		  Analysis::NodeMapping (mappings->get_current_crate (),
+					 UNKNOWN_NODEID, ref,
+					 UNKNOWN_LOCAL_DEFID),
+		  resolved->clone ());
+	      }
+	  }
+      }
+    return resolved;
+  }
+
+  virtual void visit (TupleType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (ADTType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (InferType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (FnType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (FnPtr &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (ArrayType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (SliceType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (BoolType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (IntType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (UintType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (USizeType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (ISizeType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (FloatType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (ErrorType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (CharType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (ReferenceType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (PointerType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (ParamType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (StrType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (NeverType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (PlaceholderType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (ProjectionType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (DynamicObjectType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+  virtual void visit (ClosureType &type) override
+  {
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    Location base_locus = mappings->lookup_location (get_base ()->get_ref ());
+    RichLocation r (ref_locus);
+    r.add_range (base_locus);
+    rust_error_at (r, "expected [%s] got [%s]",
+		   get_base ()->as_string ().c_str (),
+		   type.as_string ().c_str ());
+  }
+
+protected:
+  BaseRules (BaseType *base)
+    : mappings (Analysis::Mappings::get ()),
+      context (Resolver::TypeCheckContext::get ()),
+      resolved (new ErrorType (base->get_ref (), base->get_ref ()))
+  {}
+
+  Analysis::Mappings *mappings;
+  Resolver::TypeCheckContext *context;
+
+  /* Temporary storage for the result of a unification.
+     We could return the result directly instead of storing it in the rule
+     object, but that involves modifying the visitor pattern to accommodate
+     the return value, which is too complex. */
+  BaseType *resolved;
+
+private:
+  /* Returns a pointer to the ty that created this rule. */
+  virtual BaseType *get_base () = 0;
+};
+
+class InferRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  InferRules (InferType *base) : BaseRules (base), base (base) {}
+
+  void visit (BoolType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+    if (is_valid)
+      {
+	resolved = type.clone ();
+	return;
+      }
+
+    BaseRules::visit (type);
+  }
+
+  void visit (IntType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL)
+	|| (base->get_infer_kind ()
+	    == TyTy::InferType::InferTypeKind::INTEGRAL);
+    if (is_valid)
+      {
+	resolved = type.clone ();
+	return;
+      }
+
+    BaseRules::visit (type);
+  }
+
+  void visit (UintType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL)
+	|| (base->get_infer_kind ()
+	    == TyTy::InferType::InferTypeKind::INTEGRAL);
+    if (is_valid)
+      {
+	resolved = type.clone ();
+	return;
+      }
+
+    BaseRules::visit (type);
+  }
+
+  void visit (USizeType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL)
+	|| (base->get_infer_kind ()
+	    == TyTy::InferType::InferTypeKind::INTEGRAL);
+    if (is_valid)
+      {
+	resolved = type.clone ();
+	return;
+      }
+
+    BaseRules::visit (type);
+  }
+
+  void visit (ISizeType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL)
+	|| (base->get_infer_kind ()
+	    == TyTy::InferType::InferTypeKind::INTEGRAL);
+    if (is_valid)
+      {
+	resolved = type.clone ();
+	return;
+      }
+
+    BaseRules::visit (type);
+  }
+
+  void visit (FloatType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL)
+	|| (base->get_infer_kind () == TyTy::InferType::InferTypeKind::FLOAT);
+    if (is_valid)
+      {
+	resolved = type.clone ();
+	return;
+      }
+
+    BaseRules::visit (type);
+  }
+
+  void visit (ArrayType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+    if (is_valid)
+      {
+	resolved = type.clone ();
+	return;
+      }
+
+    BaseRules::visit (type);
+  }
+
+  void visit (SliceType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+    if (is_valid)
+      {
+	resolved = type.clone ();
+	return;
+      }
+
+    BaseRules::visit (type);
+  }
+
+  void visit (ADTType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+    if (is_valid)
+      {
+	resolved = type.clone ();
+	return;
+      }
+
+    BaseRules::visit (type);
+  }
+
+  void visit (TupleType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+    if (is_valid)
+      {
+	resolved = type.clone ();
+	return;
+      }
+
+    BaseRules::visit (type);
+  }
+
+  void visit (InferType &type) override
+  {
+    switch (base->get_infer_kind ())
+      {
+      case InferType::InferTypeKind::GENERAL:
+	resolved = type.clone ();
+	return;
+
+	case InferType::InferTypeKind::INTEGRAL: {
+	  if (type.get_infer_kind () == InferType::InferTypeKind::INTEGRAL)
+	    {
+	      resolved = type.clone ();
+	      return;
+	    }
+	  else if (type.get_infer_kind () == InferType::InferTypeKind::GENERAL)
+	    {
+	      resolved = base->clone ();
+	      return;
+	    }
+	}
+	break;
+
+	case InferType::InferTypeKind::FLOAT: {
+	  if (type.get_infer_kind () == InferType::InferTypeKind::FLOAT)
+	    {
+	      resolved = type.clone ();
+	      return;
+	    }
+	  else if (type.get_infer_kind () == InferType::InferTypeKind::GENERAL)
+	    {
+	      resolved = base->clone ();
+	      return;
+	    }
+	}
+	break;
+      }
+
+    BaseRules::visit (type);
+  }
+
+  void visit (CharType &type) override
+  {
+    {
+      bool is_valid
+	= (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+      if (is_valid)
+	{
+	  resolved = type.clone ();
+	  return;
+	}
+
+      BaseRules::visit (type);
+    }
+  }
+
+  void visit (ReferenceType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+    if (is_valid)
+      {
+	resolved = type.clone ();
+	return;
+      }
+
+    BaseRules::visit (type);
+  }
+
+  void visit (PointerType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+    if (is_valid)
+      {
+	resolved = type.clone ();
+	return;
+      }
+
+    BaseRules::visit (type);
+  }
+
+  void visit (ParamType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+    if (is_valid)
+      {
+	resolved = type.clone ();
+	return;
+      }
+
+    BaseRules::visit (type);
+  }
+
+  void visit (DynamicObjectType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+    if (is_valid)
+      {
+	resolved = type.clone ();
+	return;
+      }
+
+    BaseRules::visit (type);
+  }
+
+  void visit (ClosureType &type) override
+  {
+    bool is_valid
+      = (base->get_infer_kind () == TyTy::InferType::InferTypeKind::GENERAL);
+    if (is_valid)
+      {
+	resolved = type.clone ();
+	return;
+      }
+
+    BaseRules::visit (type);
+  }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  InferType *base;
+};
+
+class FnRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  FnRules (FnType *base) : BaseRules (base), base (base) {}
+
+  void visit (InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = base->clone ();
+    resolved->set_ref (type.get_ref ());
+  }
+
+  void visit (FnType &type) override
+  {
+    if (base->num_params () != type.num_params ())
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    for (size_t i = 0; i < base->num_params (); i++)
+      {
+	auto a = base->param_at (i).second;
+	auto b = type.param_at (i).second;
+
+	auto unified_param = a->unify (b);
+	if (unified_param == nullptr)
+	  {
+	    BaseRules::visit (type);
+	    return;
+	  }
+      }
+
+    auto unified_return
+      = base->get_return_type ()->unify (type.get_return_type ());
+    if (unified_return == nullptr)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = base->clone ();
+    resolved->set_ref (type.get_ref ());
+  }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  FnType *base;
+};
+
+class FnptrRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  FnptrRules (FnPtr *base) : BaseRules (base), base (base) {}
+
+  void visit (InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = base->clone ();
+    resolved->set_ref (type.get_ref ());
+  }
+
+  void visit (FnPtr &type) override
+  {
+    auto this_ret_type = base->get_return_type ();
+    auto other_ret_type = type.get_return_type ();
+    auto unified_result = this_ret_type->unify (other_ret_type);
+    if (unified_result == nullptr
+	|| unified_result->get_kind () == TypeKind::ERROR)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    if (base->num_params () != type.num_params ())
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    for (size_t i = 0; i < base->num_params (); i++)
+      {
+	auto this_param = base->param_at (i);
+	auto other_param = type.param_at (i);
+	auto unified_param = this_param->unify (other_param);
+	if (unified_param == nullptr
+	    || unified_param->get_kind () == TypeKind::ERROR)
+	  {
+	    BaseRules::visit (type);
+	    return;
+	  }
+      }
+
+    resolved = base->clone ();
+    resolved->set_ref (type.get_ref ());
+  }
+
+  void visit (FnType &type) override
+  {
+    auto this_ret_type = base->get_return_type ();
+    auto other_ret_type = type.get_return_type ();
+    auto unified_result = this_ret_type->unify (other_ret_type);
+    if (unified_result == nullptr
+	|| unified_result->get_kind () == TypeKind::ERROR)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    if (base->num_params () != type.num_params ())
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    for (size_t i = 0; i < base->num_params (); i++)
+      {
+	auto this_param = base->param_at (i);
+	auto other_param = type.param_at (i).second;
+	auto unified_param = this_param->unify (other_param);
+	if (unified_param == nullptr
+	    || unified_param->get_kind () == TypeKind::ERROR)
+	  {
+	    BaseRules::visit (type);
+	    return;
+	  }
+      }
+
+    resolved = base->clone ();
+    resolved->set_ref (type.get_ref ());
+  }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  FnPtr *base;
+};
+
+class ClosureRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  ClosureRules (ClosureType *base) : BaseRules (base), base (base) {}
+
+  // TODO
+
+private:
+  BaseType *get_base () override { return base; }
+
+  ClosureType *base;
+};
+
+class ArrayRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  ArrayRules (ArrayType *base) : BaseRules (base), base (base) {}
+
+  void visit (ArrayType &type) override
+  {
+    // check base type
+    auto base_resolved
+      = base->get_element_type ()->unify (type.get_element_type ());
+    if (base_resolved == nullptr)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved
+      = new ArrayType (type.get_ref (), type.get_ty_ref (),
+		       type.get_ident ().locus, type.get_capacity_expr (),
+		       TyVar (base_resolved->get_ref ()));
+  }
+
+  void visit (InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = base->clone ();
+    resolved->set_ref (type.get_ref ());
+  }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  ArrayType *base;
+};
+
+class SliceRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  SliceRules (SliceType *base) : BaseRules (base), base (base) {}
+
+  void visit (SliceType &type) override
+  {
+    // check base type
+    auto base_resolved
+      = base->get_element_type ()->unify (type.get_element_type ());
+    if (base_resolved == nullptr)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = new SliceType (type.get_ref (), type.get_ty_ref (),
+			      type.get_ident ().locus,
+			      TyVar (base_resolved->get_ref ()));
+  }
+
+  void visit (InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = base->clone ();
+    resolved->set_ref (type.get_ref ());
+  }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  SliceType *base;
+};
+
+class BoolRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  BoolRules (BoolType *base) : BaseRules (base), base (base) {}
+
+  void visit (BoolType &type) override
+  {
+    resolved = new BoolType (type.get_ref (), type.get_ty_ref ());
+  }
+
+  void visit (InferType &type) override
+  {
+    switch (type.get_infer_kind ())
+      {
+      case InferType::InferTypeKind::GENERAL:
+	resolved = base->clone ();
+	break;
+
+      default:
+	BaseRules::visit (type);
+	break;
+      }
+  }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  BoolType *base;
+};
+
+class IntRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  IntRules (IntType *base) : BaseRules (base), base (base) {}
+
+  void visit (InferType &type) override
+  {
+    // cant assign a float inference variable
+    if (type.get_infer_kind () == InferType::InferTypeKind::FLOAT)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = base->clone ();
+    resolved->set_ref (type.get_ref ());
+  }
+
+  void visit (IntType &type) override
+  {
+    if (type.get_int_kind () != base->get_int_kind ())
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved
+      = new IntType (type.get_ref (), type.get_ty_ref (), type.get_int_kind ());
+  }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  IntType *base;
+};
+
+class UintRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  UintRules (UintType *base) : BaseRules (base), base (base) {}
+
+  void visit (InferType &type) override
+  {
+    // cant assign a float inference variable
+    if (type.get_infer_kind () == InferType::InferTypeKind::FLOAT)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = base->clone ();
+    resolved->set_ref (type.get_ref ());
+  }
+
+  void visit (UintType &type) override
+  {
+    if (type.get_uint_kind () != base->get_uint_kind ())
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = new UintType (type.get_ref (), type.get_ty_ref (),
+			     type.get_uint_kind ());
+  }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  UintType *base;
+};
+
+class FloatRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  FloatRules (FloatType *base) : BaseRules (base), base (base) {}
+
+  void visit (InferType &type) override
+  {
+    if (type.get_infer_kind () == InferType::InferTypeKind::INTEGRAL)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = base->clone ();
+    resolved->set_ref (type.get_ref ());
+  }
+
+  void visit (FloatType &type) override
+  {
+    if (type.get_float_kind () != base->get_float_kind ())
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = new FloatType (type.get_ref (), type.get_ty_ref (),
+			      type.get_float_kind ());
+  }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  FloatType *base;
+};
+
+class ADTRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  ADTRules (ADTType *base) : BaseRules (base), base (base) {}
+
+  void visit (ADTType &type) override
+  {
+    if (base->get_adt_kind () != type.get_adt_kind ())
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    if (base->get_identifier ().compare (type.get_identifier ()) != 0)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    if (base->number_of_variants () != type.number_of_variants ())
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    for (size_t i = 0; i < type.number_of_variants (); ++i)
+      {
+	TyTy::VariantDef *a = base->get_variants ().at (i);
+	TyTy::VariantDef *b = type.get_variants ().at (i);
+
+	if (a->num_fields () != b->num_fields ())
+	  {
+	    BaseRules::visit (type);
+	    return;
+	  }
+
+	for (size_t j = 0; j < a->num_fields (); j++)
+	  {
+	    TyTy::StructFieldType *base_field = a->get_field_at_index (j);
+	    TyTy::StructFieldType *other_field = b->get_field_at_index (j);
+
+	    TyTy::BaseType *this_field_ty = base_field->get_field_type ();
+	    TyTy::BaseType *other_field_ty = other_field->get_field_type ();
+
+	    BaseType *unified_ty = this_field_ty->unify (other_field_ty);
+	    if (unified_ty->get_kind () == TyTy::TypeKind::ERROR)
+	      return;
+	  }
+      }
+
+    // generic args for the unit-struct case
+    if (type.is_unit () && base->is_unit ())
+      {
+	rust_assert (type.get_num_substitutions ()
+		     == base->get_num_substitutions ());
+
+	for (size_t i = 0; i < type.get_num_substitutions (); i++)
+	  {
+	    auto &a = base->get_substs ().at (i);
+	    auto &b = type.get_substs ().at (i);
+
+	    auto pa = a.get_param_ty ();
+	    auto pb = b.get_param_ty ();
+
+	    auto res = pa->unify (pb);
+	    if (res->get_kind () == TyTy::TypeKind::ERROR)
+	      {
+		return;
+	      }
+	  }
+      }
+
+    resolved = type.clone ();
+  }
+
+  void visit (InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = base->clone ();
+    resolved->set_ref (type.get_ref ());
+  }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  ADTType *base;
+};
+
+class TupleRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  TupleRules (TupleType *base) : BaseRules (base), base (base) {}
+
+  void visit (TupleType &type) override
+  {
+    if (base->num_fields () != type.num_fields ())
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    std::vector<TyVar> fields;
+    for (size_t i = 0; i < base->num_fields (); i++)
+      {
+	BaseType *bo = base->get_field (i);
+	BaseType *fo = type.get_field (i);
+
+	BaseType *unified_ty = bo->unify (fo);
+	if (unified_ty->get_kind () == TyTy::TypeKind::ERROR)
+	  return;
+
+	fields.push_back (TyVar (unified_ty->get_ref ()));
+      }
+
+    resolved = new TyTy::TupleType (type.get_ref (), type.get_ty_ref (),
+				    type.get_ident ().locus, fields);
+  }
+
+  void visit (InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = base->clone ();
+    resolved->set_ref (type.get_ref ());
+  }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  TupleType *base;
+};
+
+class USizeRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  USizeRules (USizeType *base) : BaseRules (base), base (base) {}
+
+  void visit (InferType &type) override
+  {
+    // cant assign a float inference variable
+    if (type.get_infer_kind () == InferType::InferTypeKind::FLOAT)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = base->clone ();
+    resolved->set_ref (type.get_ref ());
+  }
+
+  void visit (USizeType &type) override { resolved = type.clone (); }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  USizeType *base;
+};
+
+class ISizeRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  ISizeRules (ISizeType *base) : BaseRules (base), base (base) {}
+
+  void visit (InferType &type) override
+  {
+    // cant assign a float inference variable
+    if (type.get_infer_kind () == InferType::InferTypeKind::FLOAT)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = base->clone ();
+    resolved->set_ref (type.get_ref ());
+  }
+
+  void visit (ISizeType &type) override { resolved = type.clone (); }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  ISizeType *base;
+};
+
+class CharRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  CharRules (CharType *base) : BaseRules (base), base (base) {}
+
+  void visit (InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = base->clone ();
+    resolved->set_ref (type.get_ref ());
+  }
+
+  void visit (CharType &type) override { resolved = type.clone (); }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  CharType *base;
+};
+
+class ReferenceRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  ReferenceRules (ReferenceType *base) : BaseRules (base), base (base) {}
+
+  void visit (ReferenceType &type) override
+  {
+    auto base_type = base->get_base ();
+    auto other_base_type = type.get_base ();
+
+    TyTy::BaseType *base_resolved = base_type->unify (other_base_type);
+    if (base_resolved == nullptr
+	|| base_resolved->get_kind () == TypeKind::ERROR)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    // rust is permissive about mutablity here you can always go from mutable to
+    // immutable but not the otherway round
+    bool mutability_ok = base->is_mutable () ? type.is_mutable () : true;
+    if (!mutability_ok)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = new ReferenceType (base->get_ref (), base->get_ty_ref (),
+				  TyVar (base_resolved->get_ref ()),
+				  base->mutability ());
+  }
+
+  void visit (InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = base->clone ();
+    resolved->set_ref (type.get_ref ());
+  }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  ReferenceType *base;
+};
+
+class PointerRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  PointerRules (PointerType *base) : BaseRules (base), base (base) {}
+
+  void visit (PointerType &type) override
+  {
+    auto base_type = base->get_base ();
+    auto other_base_type = type.get_base ();
+
+    TyTy::BaseType *base_resolved = base_type->unify (other_base_type);
+    if (base_resolved == nullptr
+	|| base_resolved->get_kind () == TypeKind::ERROR)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    // rust is permissive about mutablity here you can always go from mutable to
+    // immutable but not the otherway round
+    bool mutability_ok = base->is_mutable () ? type.is_mutable () : true;
+    if (!mutability_ok)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = new PointerType (base->get_ref (), base->get_ty_ref (),
+				TyVar (base_resolved->get_ref ()),
+				base->mutability ());
+  }
+
+  void visit (InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = base->clone ();
+    resolved->set_ref (type.get_ref ());
+  }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  PointerType *base;
+};
+
+class ParamRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  ParamRules (ParamType *base) : BaseRules (base), base (base) {}
+
+  // param types are a placeholder we shouldn't have cases where we unify
+  // against it. eg: struct foo<T> { a: T }; When we invoke it we can do either:
+  //
+  // foo<i32>{ a: 123 }.
+  // Then this enforces the i32 type to be referenced on the
+  // field via an hirid.
+  //
+  // rust also allows for a = foo{a:123}; Where we can use an Inference Variable
+  // to handle the typing of the struct
+  BaseType *unify (BaseType *other) override final
+  {
+    if (!base->can_resolve ())
+      return BaseRules::unify (other);
+
+    auto lookup = base->resolve ();
+    return lookup->unify (other);
+  }
+
+  void visit (ParamType &type) override
+  {
+    if (base->get_symbol ().compare (type.get_symbol ()) != 0)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = type.clone ();
+  }
+
+  void visit (InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = base->clone ();
+  }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  ParamType *base;
+};
+
+class StrRules : public BaseRules
+{
+  // FIXME we will need a enum for the StrType like ByteBuf etc..
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  StrRules (StrType *base) : BaseRules (base), base (base) {}
+
+  void visit (StrType &type) override { resolved = type.clone (); }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  StrType *base;
+};
+
+class NeverRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  NeverRules (NeverType *base) : BaseRules (base), base (base) {}
+
+  void visit (NeverType &type) override { resolved = type.clone (); }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  NeverType *base;
+};
+
+class PlaceholderRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  PlaceholderRules (PlaceholderType *base) : BaseRules (base), base (base) {}
+
+  BaseType *unify (BaseType *other) override final
+  {
+    if (!base->can_resolve ())
+      return BaseRules::unify (other);
+
+    BaseType *lookup = base->resolve ();
+    return lookup->unify (other);
+  }
+
+  void visit (PlaceholderType &type) override
+  {
+    if (base->get_symbol ().compare (type.get_symbol ()) != 0)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = type.clone ();
+  }
+
+  void visit (InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = base->clone ();
+  }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  PlaceholderType *base;
+};
+
+class DynamicRules : public BaseRules
+{
+  using Rust::TyTy::BaseRules::visit;
+
+public:
+  DynamicRules (DynamicObjectType *base) : BaseRules (base), base (base) {}
+
+  void visit (InferType &type) override
+  {
+    if (type.get_infer_kind () != InferType::InferTypeKind::GENERAL)
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = base->clone ();
+  }
+
+  void visit (DynamicObjectType &type) override
+  {
+    if (base->num_specified_bounds () != type.num_specified_bounds ())
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    Location ref_locus = mappings->lookup_location (type.get_ref ());
+    if (!base->bounds_compatible (type, ref_locus, true))
+      {
+	BaseRules::visit (type);
+	return;
+      }
+
+    resolved = base->clone ();
+  }
+
+private:
+  BaseType *get_base () override { return base; }
+
+  DynamicObjectType *base;
+};
+
+} // namespace TyTy
+} // namespace Rust
+
+#endif // RUST_TYTY_RULES
diff --git a/gcc/rust/typecheck/rust-tyty-visitor.h b/gcc/rust/typecheck/rust-tyty-visitor.h
new file mode 100644
index 00000000000..464e70d39d7
--- /dev/null
+++ b/gcc/rust/typecheck/rust-tyty-visitor.h
@@ -0,0 +1,88 @@
+// 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_TYTY_VISITOR
+#define RUST_TYTY_VISITOR
+
+#include "rust-tyty.h"
+
+namespace Rust {
+namespace TyTy {
+
+class TyVisitor
+{
+public:
+  virtual void visit (InferType &type) = 0;
+  virtual void visit (ADTType &type) = 0;
+  virtual void visit (TupleType &type) = 0;
+  virtual void visit (FnType &type) = 0;
+  virtual void visit (FnPtr &type) = 0;
+  virtual void visit (ArrayType &type) = 0;
+  virtual void visit (SliceType &type) = 0;
+  virtual void visit (BoolType &type) = 0;
+  virtual void visit (IntType &type) = 0;
+  virtual void visit (UintType &type) = 0;
+  virtual void visit (FloatType &type) = 0;
+  virtual void visit (USizeType &type) = 0;
+  virtual void visit (ISizeType &type) = 0;
+  virtual void visit (ErrorType &type) = 0;
+  virtual void visit (CharType &type) = 0;
+  virtual void visit (ReferenceType &type) = 0;
+  virtual void visit (PointerType &type) = 0;
+  virtual void visit (ParamType &type) = 0;
+  virtual void visit (StrType &type) = 0;
+  virtual void visit (NeverType &type) = 0;
+  virtual void visit (PlaceholderType &type) = 0;
+  virtual void visit (ProjectionType &type) = 0;
+  virtual void visit (DynamicObjectType &type) = 0;
+  virtual void visit (ClosureType &type) = 0;
+};
+
+class TyConstVisitor
+{
+public:
+  virtual void visit (const InferType &type) = 0;
+  virtual void visit (const ADTType &type) = 0;
+  virtual void visit (const TupleType &type) = 0;
+  virtual void visit (const FnType &type) = 0;
+  virtual void visit (const FnPtr &type) = 0;
+  virtual void visit (const ArrayType &type) = 0;
+  virtual void visit (const SliceType &type) = 0;
+  virtual void visit (const BoolType &type) = 0;
+  virtual void visit (const IntType &type) = 0;
+  virtual void visit (const UintType &type) = 0;
+  virtual void visit (const FloatType &type) = 0;
+  virtual void visit (const USizeType &type) = 0;
+  virtual void visit (const ISizeType &type) = 0;
+  virtual void visit (const ErrorType &type) = 0;
+  virtual void visit (const CharType &type) = 0;
+  virtual void visit (const ReferenceType &type) = 0;
+  virtual void visit (const PointerType &type) = 0;
+  virtual void visit (const ParamType &type) = 0;
+  virtual void visit (const StrType &type) = 0;
+  virtual void visit (const NeverType &type) = 0;
+  virtual void visit (const PlaceholderType &type) = 0;
+  virtual void visit (const ProjectionType &type) = 0;
+  virtual void visit (const DynamicObjectType &type) = 0;
+  virtual void visit (const ClosureType &type) = 0;
+};
+
+} // namespace TyTy
+} // namespace Rust
+
+#endif // RUST_TYTY_VISITOR
diff --git a/gcc/rust/typecheck/rust-tyty.cc b/gcc/rust/typecheck/rust-tyty.cc
new file mode 100644
index 00000000000..3c2c6786940
--- /dev/null
+++ b/gcc/rust/typecheck/rust-tyty.cc
@@ -0,0 +1,2885 @@
+// 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/>.
+
+#include "rust-tyty.h"
+#include "rust-tyty-visitor.h"
+#include "rust-tyty-call.h"
+#include "rust-hir-type-check-expr.h"
+#include "rust-hir-type-check-type.h"
+#include "rust-tyty-rules.h"
+#include "rust-tyty-cmp.h"
+#include "rust-hir-map.h"
+#include "rust-substitution-mapper.h"
+#include "rust-hir-trait-ref.h"
+#include "rust-hir-type-bounds.h"
+
+namespace Rust {
+namespace TyTy {
+
+std::string
+TypeKindFormat::to_string (TypeKind kind)
+{
+  switch (kind)
+    {
+    case TypeKind::INFER:
+      return "Infer";
+
+    case TypeKind::ADT:
+      return "ADT";
+
+    case TypeKind::STR:
+      return "STR";
+
+    case TypeKind::REF:
+      return "REF";
+
+    case TypeKind::POINTER:
+      return "POINTER";
+
+    case TypeKind::PARAM:
+      return "PARAM";
+
+    case TypeKind::ARRAY:
+      return "ARRAY";
+
+    case TypeKind::SLICE:
+      return "SLICE";
+
+    case TypeKind::FNDEF:
+      return "FnDef";
+
+    case TypeKind::FNPTR:
+      return "FnPtr";
+
+    case TypeKind::TUPLE:
+      return "Tuple";
+
+    case TypeKind::BOOL:
+      return "Bool";
+
+    case TypeKind::CHAR:
+      return "Char";
+
+    case TypeKind::INT:
+      return "Int";
+
+    case TypeKind::UINT:
+      return "Uint";
+
+    case TypeKind::FLOAT:
+      return "Float";
+
+    case TypeKind::USIZE:
+      return "Usize";
+
+    case TypeKind::ISIZE:
+      return "Isize";
+
+    case TypeKind::NEVER:
+      return "Never";
+
+    case TypeKind::PLACEHOLDER:
+      return "Placeholder";
+
+    case TypeKind::PROJECTION:
+      return "Projection";
+
+    case TypeKind::DYNAMIC:
+      return "Dynamic";
+
+    case TypeKind::CLOSURE:
+      return "Closure";
+
+    case TypeKind::ERROR:
+      return "ERROR";
+    }
+  gcc_unreachable ();
+}
+
+bool
+is_primitive_type_kind (TypeKind kind)
+{
+  switch (kind)
+    {
+    case TypeKind::BOOL:
+    case TypeKind::CHAR:
+    case TypeKind::INT:
+    case TypeKind::UINT:
+    case TypeKind::ISIZE:
+    case TypeKind::USIZE:
+    case TypeKind::FLOAT:
+    case TypeKind::NEVER:
+    case TypeKind::STR:
+      return true;
+    default:
+      return false;
+    }
+}
+
+bool
+BaseType::satisfies_bound (const TypeBoundPredicate &predicate) const
+{
+  const Resolver::TraitReference *query = predicate.get ();
+  for (auto &bound : specified_bounds)
+    {
+      const Resolver::TraitReference *item = bound.get ();
+      bool found = item->get_mappings ().get_defid ()
+		   == query->get_mappings ().get_defid ();
+      if (found)
+	return true;
+    }
+
+  auto probed = Resolver::TypeBoundsProbe::Probe (this);
+  for (auto &b : probed)
+    {
+      const Resolver::TraitReference *bound = b.first;
+      bool found = bound->get_mappings ().get_defid ()
+		   == query->get_mappings ().get_defid ();
+      if (found)
+	return true;
+    }
+
+  return false;
+}
+
+bool
+BaseType::bounds_compatible (const BaseType &other, Location locus,
+			     bool emit_error) const
+{
+  std::vector<std::reference_wrapper<const TypeBoundPredicate>>
+    unsatisfied_bounds;
+  for (auto &bound : get_specified_bounds ())
+    {
+      if (!other.satisfies_bound (bound))
+	unsatisfied_bounds.push_back (bound);
+    }
+
+  // lets emit a single error for this
+  if (unsatisfied_bounds.size () > 0)
+    {
+      RichLocation r (locus);
+      std::string missing_preds;
+      for (size_t i = 0; i < unsatisfied_bounds.size (); i++)
+	{
+	  const TypeBoundPredicate &pred = unsatisfied_bounds.at (i);
+	  r.add_range (pred.get_locus ());
+	  missing_preds += pred.get_name ();
+
+	  bool have_next = (i + 1) < unsatisfied_bounds.size ();
+	  if (have_next)
+	    missing_preds += ", ";
+	}
+
+      if (emit_error)
+	{
+	  rust_error_at (r,
+			 "bounds not satisfied for %s %<%s%> is not satisfied",
+			 other.get_name ().c_str (), missing_preds.c_str ());
+	  // rust_assert (!emit_error);
+	}
+    }
+
+  return unsatisfied_bounds.size () == 0;
+}
+
+void
+BaseType::inherit_bounds (const BaseType &other)
+{
+  inherit_bounds (other.get_specified_bounds ());
+}
+
+void
+BaseType::inherit_bounds (
+  const std::vector<TyTy::TypeBoundPredicate> &specified_bounds)
+{
+  // FIXME
+  // 1. This needs to union the bounds
+  // 2. Do some checking for trait polarity to ensure compatibility
+  for (auto &bound : specified_bounds)
+    {
+      add_bound (bound);
+    }
+}
+
+const BaseType *
+BaseType::get_root () const
+{
+  // FIXME this needs to be it its own visitor class with a vector adjustments
+  const TyTy::BaseType *root = this;
+  if (get_kind () == TyTy::REF)
+    {
+      const ReferenceType *r = static_cast<const ReferenceType *> (root);
+      root = r->get_base ()->get_root ();
+    }
+  else if (get_kind () == TyTy::POINTER)
+    {
+      const PointerType *r = static_cast<const PointerType *> (root);
+      root = r->get_base ()->get_root ();
+    }
+
+  // these are an unsize
+  else if (get_kind () == TyTy::SLICE)
+    {
+      const SliceType *r = static_cast<const SliceType *> (root);
+      root = r->get_element_type ()->get_root ();
+    }
+  // else if (get_kind () == TyTy::ARRAY)
+  //   {
+  //     const ArrayType *r = static_cast<const ArrayType *> (root);
+  //     root = r->get_element_type ()->get_root ();
+  //   }
+
+  return root;
+}
+
+const BaseType *
+BaseType::destructure () const
+{
+  int recurisve_ops = 0;
+  const BaseType *x = this;
+  while (true)
+    {
+      if (recurisve_ops++ >= rust_max_recursion_depth)
+	{
+	  rust_error_at (
+	    Location (),
+	    "%<recursion depth%> count exceeds limit of %i (use "
+	    "%<frust-max-recursion-depth=%> to increase the limit)",
+	    rust_max_recursion_depth);
+	  return new ErrorType (get_ref ());
+	}
+
+      switch (x->get_kind ())
+	{
+	  case TyTy::TypeKind::PARAM: {
+	    const TyTy::ParamType *p = static_cast<const TyTy::ParamType *> (x);
+	    x = p->resolve ();
+	  }
+	  break;
+
+	  case TyTy::TypeKind::PLACEHOLDER: {
+	    const TyTy::PlaceholderType *p
+	      = static_cast<const TyTy::PlaceholderType *> (x);
+	    rust_assert (p->can_resolve ());
+	    x = p->resolve ();
+	  }
+	  break;
+
+	  case TyTy::TypeKind::PROJECTION: {
+	    const TyTy::ProjectionType *p
+	      = static_cast<const TyTy::ProjectionType *> (x);
+	    x = p->get ();
+	  }
+	  break;
+
+	default:
+	  return x;
+	}
+    }
+
+  return x;
+}
+
+TyVar::TyVar (HirId ref) : ref (ref)
+{
+  // ensure this reference is defined within the context
+  auto context = Resolver::TypeCheckContext::get ();
+  BaseType *lookup = nullptr;
+  bool ok = context->lookup_type (ref, &lookup);
+  rust_assert (ok);
+}
+
+BaseType *
+TyVar::get_tyty () const
+{
+  auto context = Resolver::TypeCheckContext::get ();
+  BaseType *lookup = nullptr;
+  bool ok = context->lookup_type (ref, &lookup);
+  rust_assert (ok);
+  return lookup;
+}
+
+TyVar
+TyVar::get_implicit_infer_var (Location locus)
+{
+  auto mappings = Analysis::Mappings::get ();
+  auto context = Resolver::TypeCheckContext::get ();
+
+  InferType *infer = new InferType (mappings->get_next_hir_id (),
+				    InferType::InferTypeKind::GENERAL, locus);
+  context->insert_type (Analysis::NodeMapping (mappings->get_current_crate (),
+					       UNKNOWN_NODEID,
+					       infer->get_ref (),
+					       UNKNOWN_LOCAL_DEFID),
+			infer);
+  mappings->insert_location (infer->get_ref (), locus);
+
+  return TyVar (infer->get_ref ());
+}
+
+TyVar
+TyVar::subst_covariant_var (TyTy::BaseType *orig, TyTy::BaseType *subst)
+{
+  if (orig->get_kind () != TyTy::TypeKind::PARAM)
+    return TyVar (subst->get_ty_ref ());
+  else if (subst->get_kind () == TyTy::TypeKind::PARAM)
+    {
+      TyTy::ParamType *p = static_cast<TyTy::ParamType *> (subst);
+      if (p->resolve ()->get_kind () == TyTy::TypeKind::PARAM)
+	{
+	  return TyVar (subst->get_ty_ref ());
+	}
+    }
+
+  return TyVar (subst->get_ref ());
+}
+
+TyVar
+TyVar::clone () const
+{
+  TyTy::BaseType *c = get_tyty ()->clone ();
+  return TyVar (c->get_ref ());
+}
+
+TyVar
+TyVar::monomorphized_clone () const
+{
+  auto mappings = Analysis::Mappings::get ();
+  auto context = Resolver::TypeCheckContext::get ();
+
+  // this needs a new hirid
+  TyTy::BaseType *c = get_tyty ()->monomorphized_clone ();
+  c->set_ref (mappings->get_next_hir_id ());
+
+  // insert it
+  context->insert_type (Analysis::NodeMapping (mappings->get_current_crate (),
+					       UNKNOWN_NODEID, c->get_ref (),
+					       UNKNOWN_LOCAL_DEFID),
+			c);
+
+  return TyVar (c->get_ref ());
+}
+
+TyWithLocation::TyWithLocation (BaseType *ty, Location locus)
+  : ty (ty), locus (locus)
+{}
+
+TyWithLocation::TyWithLocation (BaseType *ty) : ty (ty)
+{
+  auto mappings = Analysis::Mappings::get ();
+  locus = mappings->lookup_location (ty->get_ref ());
+}
+
+void
+InferType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+InferType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+InferType::as_string () const
+{
+  switch (infer_kind)
+    {
+    case GENERAL:
+      return "T?";
+    case INTEGRAL:
+      return "<integer>";
+    case FLOAT:
+      return "<float>";
+    }
+  return "<infer::error>";
+}
+
+BaseType *
+InferType::unify (BaseType *other)
+{
+  InferRules r (this);
+  return r.unify (other);
+}
+
+bool
+InferType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  InferCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+BaseType *
+InferType::clone () const
+{
+  // clones for inference variables are special in that they _must_ exist within
+  // the type check context and we must ensure we don't loose the chain
+  // otherwise we will end up in the missing type annotations case
+  //
+  // This means we cannot simply take over the same reference we must generate a
+  // new ref just like the get_implicit_infer_var code then we can setup the
+  // chain of references accordingly to ensure we don't loose the ability to
+  // update the inference variables when we solve the type
+
+  auto mappings = Analysis::Mappings::get ();
+  auto context = Resolver::TypeCheckContext::get ();
+
+  InferType *clone
+    = new InferType (mappings->get_next_hir_id (), get_infer_kind (),
+		     get_ident ().locus, get_combined_refs ());
+
+  context->insert_type (Analysis::NodeMapping (mappings->get_current_crate (),
+					       UNKNOWN_NODEID,
+					       clone->get_ref (),
+					       UNKNOWN_LOCAL_DEFID),
+			clone);
+  mappings->insert_location (clone->get_ref (),
+			     mappings->lookup_location (get_ref ()));
+
+  // setup the chain to reference this
+  clone->append_reference (get_ref ());
+
+  return clone;
+}
+
+BaseType *
+InferType::monomorphized_clone () const
+{
+  return clone ();
+}
+
+bool
+InferType::default_type (BaseType **type) const
+{
+  auto context = Resolver::TypeCheckContext::get ();
+  bool ok = false;
+  switch (infer_kind)
+    {
+    case GENERAL:
+      return false;
+
+      case INTEGRAL: {
+	ok = context->lookup_builtin ("i32", type);
+	rust_assert (ok);
+	return ok;
+      }
+
+      case FLOAT: {
+	ok = context->lookup_builtin ("f64", type);
+	rust_assert (ok);
+	return ok;
+      }
+    }
+  return false;
+}
+
+void
+ErrorType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ErrorType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+ErrorType::as_string () const
+{
+  return "<tyty::error>";
+}
+
+BaseType *
+ErrorType::unify (BaseType *other)
+{
+  return this;
+}
+
+bool
+ErrorType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  return get_kind () == other->get_kind ();
+}
+
+BaseType *
+ErrorType::clone () const
+{
+  return new ErrorType (get_ref (), get_ty_ref (), get_combined_refs ());
+}
+
+BaseType *
+ErrorType::monomorphized_clone () const
+{
+  return clone ();
+}
+
+std::string
+StructFieldType::as_string () const
+{
+  return name + ":" + get_field_type ()->debug_str ();
+}
+
+bool
+StructFieldType::is_equal (const StructFieldType &other) const
+{
+  bool names_eq = get_name ().compare (other.get_name ()) == 0;
+
+  TyTy::BaseType *o = other.get_field_type ();
+  if (o->get_kind () == TypeKind::PARAM)
+    {
+      ParamType *op = static_cast<ParamType *> (o);
+      o = op->resolve ();
+    }
+
+  bool types_eq = get_field_type ()->is_equal (*o);
+
+  return names_eq && types_eq;
+}
+
+StructFieldType *
+StructFieldType::clone () const
+{
+  return new StructFieldType (get_ref (), get_name (),
+			      get_field_type ()->clone ());
+}
+
+StructFieldType *
+StructFieldType::monomorphized_clone () const
+{
+  return new StructFieldType (get_ref (), get_name (),
+			      get_field_type ()->monomorphized_clone ());
+}
+
+bool
+SubstitutionParamMapping::need_substitution () const
+{
+  if (!param->can_resolve ())
+    return true;
+
+  auto resolved = param->resolve ();
+  return !resolved->is_concrete ();
+}
+
+bool
+SubstitutionParamMapping::fill_param_ty (
+  SubstitutionArgumentMappings &subst_mappings, Location locus)
+{
+  SubstitutionArg arg = SubstitutionArg::error ();
+  bool ok = subst_mappings.get_argument_for_symbol (get_param_ty (), &arg);
+  if (!ok)
+    return true;
+
+  TyTy::BaseType &type = *arg.get_tyty ();
+  if (type.get_kind () == TyTy::TypeKind::INFER)
+    {
+      type.inherit_bounds (*param);
+    }
+  else
+    {
+      if (!param->bounds_compatible (type, locus, true))
+	return false;
+    }
+
+  if (type.get_kind () == TypeKind::PARAM)
+    {
+      // delete param;
+      param = static_cast<ParamType *> (type.clone ());
+    }
+  else
+    {
+      // check the substitution is compatible with bounds
+      if (!param->bounds_compatible (type, locus, true))
+	return false;
+
+      // recursively pass this down to all HRTB's
+      for (auto &bound : param->get_specified_bounds ())
+	bound.handle_substitions (subst_mappings);
+
+      param->set_ty_ref (type.get_ref ());
+    }
+
+  return true;
+}
+
+void
+SubstitutionParamMapping::override_context ()
+{
+  if (!param->can_resolve ())
+    return;
+
+  auto mappings = Analysis::Mappings::get ();
+  auto context = Resolver::TypeCheckContext::get ();
+
+  context->insert_type (Analysis::NodeMapping (mappings->get_current_crate (),
+					       UNKNOWN_NODEID,
+					       param->get_ref (),
+					       UNKNOWN_LOCAL_DEFID),
+			param->resolve ());
+}
+
+SubstitutionArgumentMappings
+SubstitutionRef::get_mappings_from_generic_args (HIR::GenericArgs &args)
+{
+  if (args.get_binding_args ().size () > 0)
+    {
+      RichLocation r (args.get_locus ());
+      for (auto &binding : args.get_binding_args ())
+	r.add_range (binding.get_locus ());
+
+      rust_error_at (r, "associated type bindings are not allowed here");
+      return SubstitutionArgumentMappings::error ();
+    }
+
+  // for inherited arguments
+  size_t offs = used_arguments.size ();
+  if (args.get_type_args ().size () + offs > substitutions.size ())
+    {
+      RichLocation r (args.get_locus ());
+      r.add_range (substitutions.front ().get_param_locus ());
+
+      rust_error_at (
+	r,
+	"generic item takes at most %lu type arguments but %lu were supplied",
+	(unsigned long) substitutions.size (),
+	(unsigned long) args.get_type_args ().size ());
+      return SubstitutionArgumentMappings::error ();
+    }
+
+  if (args.get_type_args ().size () + offs < min_required_substitutions ())
+    {
+      RichLocation r (args.get_locus ());
+      r.add_range (substitutions.front ().get_param_locus ());
+
+      rust_error_at (
+	r,
+	"generic item takes at least %lu type arguments but %lu were supplied",
+	(unsigned long) (min_required_substitutions () - offs),
+	(unsigned long) args.get_type_args ().size ());
+      return SubstitutionArgumentMappings::error ();
+    }
+
+  std::vector<SubstitutionArg> mappings = used_arguments.get_mappings ();
+  for (auto &arg : args.get_type_args ())
+    {
+      BaseType *resolved = Resolver::TypeCheckType::Resolve (arg.get ());
+      if (resolved == nullptr || resolved->get_kind () == TyTy::TypeKind::ERROR)
+	{
+	  rust_error_at (args.get_locus (), "failed to resolve type arguments");
+	  return SubstitutionArgumentMappings::error ();
+	}
+
+      SubstitutionArg subst_arg (&substitutions.at (offs), resolved);
+      offs++;
+      mappings.push_back (std::move (subst_arg));
+    }
+
+  // we must need to fill out defaults
+  size_t left_over
+    = num_required_substitutions () - min_required_substitutions ();
+  if (left_over > 0)
+    {
+      for (size_t offs = mappings.size (); offs < substitutions.size (); offs++)
+	{
+	  SubstitutionParamMapping &param = substitutions.at (offs);
+	  rust_assert (param.param_has_default_ty ());
+
+	  BaseType *resolved = param.get_default_ty ();
+	  if (resolved->get_kind () == TypeKind::ERROR)
+	    return SubstitutionArgumentMappings::error ();
+
+	  // this resolved default might already contain default parameters
+	  if (resolved->contains_type_parameters ())
+	    {
+	      SubstitutionArgumentMappings intermediate (mappings,
+							 args.get_locus ());
+	      resolved = Resolver::SubstMapperInternal::Resolve (resolved,
+								 intermediate);
+
+	      if (resolved->get_kind () == TypeKind::ERROR)
+		return SubstitutionArgumentMappings::error ();
+	    }
+
+	  SubstitutionArg subst_arg (&param, resolved);
+	  mappings.push_back (std::move (subst_arg));
+	}
+    }
+
+  return SubstitutionArgumentMappings (mappings, args.get_locus ());
+}
+
+SubstitutionArgumentMappings
+SubstitutionRef::adjust_mappings_for_this (
+  SubstitutionArgumentMappings &mappings)
+{
+  std::vector<SubstitutionArg> resolved_mappings;
+  for (size_t i = 0; i < substitutions.size (); i++)
+    {
+      auto &subst = substitutions.at (i);
+
+      SubstitutionArg arg = SubstitutionArg::error ();
+      if (mappings.size () == substitutions.size ())
+	{
+	  mappings.get_argument_at (i, &arg);
+	}
+      else
+	{
+	  if (subst.needs_substitution ())
+	    {
+	      // get from passed in mappings
+	      mappings.get_argument_for_symbol (subst.get_param_ty (), &arg);
+	    }
+	  else
+	    {
+	      // we should already have this somewhere
+	      used_arguments.get_argument_for_symbol (subst.get_param_ty (),
+						      &arg);
+	    }
+	}
+
+      bool ok = !arg.is_error ();
+      if (ok)
+	{
+	  SubstitutionArg adjusted (&subst, arg.get_tyty ());
+	  resolved_mappings.push_back (std::move (adjusted));
+	}
+    }
+
+  if (resolved_mappings.empty ())
+    return SubstitutionArgumentMappings::error ();
+
+  return SubstitutionArgumentMappings (resolved_mappings, mappings.get_locus (),
+				       mappings.get_subst_cb (),
+				       mappings.trait_item_mode ());
+}
+
+bool
+SubstitutionRef::are_mappings_bound (SubstitutionArgumentMappings &mappings)
+{
+  std::vector<SubstitutionArg> resolved_mappings;
+  for (size_t i = 0; i < substitutions.size (); i++)
+    {
+      auto &subst = substitutions.at (i);
+
+      SubstitutionArg arg = SubstitutionArg::error ();
+      if (mappings.size () == substitutions.size ())
+	{
+	  mappings.get_argument_at (i, &arg);
+	}
+      else
+	{
+	  if (subst.needs_substitution ())
+	    {
+	      // get from passed in mappings
+	      mappings.get_argument_for_symbol (subst.get_param_ty (), &arg);
+	    }
+	  else
+	    {
+	      // we should already have this somewhere
+	      used_arguments.get_argument_for_symbol (subst.get_param_ty (),
+						      &arg);
+	    }
+	}
+
+      bool ok = !arg.is_error ();
+      if (ok)
+	{
+	  SubstitutionArg adjusted (&subst, arg.get_tyty ());
+	  resolved_mappings.push_back (std::move (adjusted));
+	}
+    }
+
+  return !resolved_mappings.empty ();
+}
+
+// this function assumes that the mappings being passed are for the same type as
+// this new substitution reference so ordering matters here
+SubstitutionArgumentMappings
+SubstitutionRef::solve_mappings_from_receiver_for_self (
+  SubstitutionArgumentMappings &mappings) const
+{
+  std::vector<SubstitutionArg> resolved_mappings;
+
+  rust_assert (mappings.size () == get_num_substitutions ());
+  for (size_t i = 0; i < get_num_substitutions (); i++)
+    {
+      const SubstitutionParamMapping &param_mapping = substitutions.at (i);
+      SubstitutionArg &arg = mappings.get_mappings ().at (i);
+
+      if (param_mapping.needs_substitution ())
+	{
+	  SubstitutionArg adjusted (&param_mapping, arg.get_tyty ());
+	  resolved_mappings.push_back (std::move (adjusted));
+	}
+    }
+
+  return SubstitutionArgumentMappings (resolved_mappings,
+				       mappings.get_locus ());
+}
+
+SubstitutionArgumentMappings
+SubstitutionRef::solve_missing_mappings_from_this (SubstitutionRef &ref,
+						   SubstitutionRef &to)
+{
+  rust_assert (!ref.needs_substitution ());
+  rust_assert (needs_substitution ());
+  rust_assert (get_num_substitutions () == ref.get_num_substitutions ());
+
+  Location locus = used_arguments.get_locus ();
+  std::vector<SubstitutionArg> resolved_mappings;
+
+  std::map<HirId, std::pair<ParamType *, BaseType *>> substs;
+  for (size_t i = 0; i < get_num_substitutions (); i++)
+    {
+      SubstitutionParamMapping &a = substitutions.at (i);
+      SubstitutionParamMapping &b = ref.substitutions.at (i);
+
+      if (a.need_substitution ())
+	{
+	  const BaseType *root = a.get_param_ty ()->resolve ()->get_root ();
+	  rust_assert (root->get_kind () == TyTy::TypeKind::PARAM);
+	  const ParamType *p = static_cast<const TyTy::ParamType *> (root);
+
+	  substs[p->get_ty_ref ()] = {static_cast<ParamType *> (p->clone ()),
+				      b.get_param_ty ()->resolve ()};
+	}
+    }
+
+  for (auto it = substs.begin (); it != substs.end (); it++)
+    {
+      HirId param_id = it->first;
+      BaseType *arg = it->second.second;
+
+      const SubstitutionParamMapping *associate_param = nullptr;
+      for (SubstitutionParamMapping &p : to.substitutions)
+	{
+	  if (p.get_param_ty ()->get_ty_ref () == param_id)
+	    {
+	      associate_param = &p;
+	      break;
+	    }
+	}
+
+      rust_assert (associate_param != nullptr);
+      SubstitutionArg argument (associate_param, arg);
+      resolved_mappings.push_back (std::move (argument));
+    }
+
+  return SubstitutionArgumentMappings (resolved_mappings, locus);
+}
+
+bool
+SubstitutionRef::monomorphize ()
+{
+  auto context = Resolver::TypeCheckContext::get ();
+  for (const auto &subst : get_substs ())
+    {
+      const TyTy::ParamType *pty = subst.get_param_ty ();
+
+      if (!pty->can_resolve ())
+	continue;
+
+      const TyTy::BaseType *binding = pty->resolve ();
+      if (binding->get_kind () == TyTy::TypeKind::PARAM)
+	continue;
+
+      for (const auto &bound : pty->get_specified_bounds ())
+	{
+	  const Resolver::TraitReference *specified_bound_ref = bound.get ();
+
+	  // setup any associated type mappings for the specified bonds and this
+	  // type
+	  auto candidates = Resolver::TypeBoundsProbe::Probe (binding);
+
+	  Resolver::AssociatedImplTrait *associated_impl_trait = nullptr;
+	  for (auto &probed_bound : candidates)
+	    {
+	      const Resolver::TraitReference *bound_trait_ref
+		= probed_bound.first;
+	      const HIR::ImplBlock *associated_impl = probed_bound.second;
+
+	      HirId impl_block_id
+		= associated_impl->get_mappings ().get_hirid ();
+	      Resolver::AssociatedImplTrait *associated = nullptr;
+	      bool found_impl_trait
+		= context->lookup_associated_trait_impl (impl_block_id,
+							 &associated);
+	      if (found_impl_trait)
+		{
+		  bool found_trait
+		    = specified_bound_ref->is_equal (*bound_trait_ref);
+		  bool found_self
+		    = associated->get_self ()->can_eq (binding, false);
+		  if (found_trait && found_self)
+		    {
+		      associated_impl_trait = associated;
+		      break;
+		    }
+		}
+	    }
+
+	  if (associated_impl_trait != nullptr)
+	    {
+	      associated_impl_trait->setup_associated_types (binding, bound);
+	    }
+	}
+    }
+
+  return true;
+}
+
+void
+ADTType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ADTType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+ADTType::as_string () const
+{
+  std::string variants_buffer;
+  for (size_t i = 0; i < number_of_variants (); ++i)
+    {
+      TyTy::VariantDef *variant = variants.at (i);
+      variants_buffer += variant->as_string ();
+      if ((i + 1) < number_of_variants ())
+	variants_buffer += ", ";
+    }
+
+  return identifier + subst_as_string () + "{" + variants_buffer + "}";
+}
+
+BaseType *
+ADTType::unify (BaseType *other)
+{
+  ADTRules r (this);
+  return r.unify (other);
+}
+
+bool
+ADTType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  ADTCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+bool
+ADTType::is_equal (const BaseType &other) const
+{
+  if (get_kind () != other.get_kind ())
+    return false;
+
+  auto other2 = static_cast<const ADTType &> (other);
+  if (get_adt_kind () != other2.get_adt_kind ())
+    return false;
+
+  if (number_of_variants () != other2.number_of_variants ())
+    return false;
+
+  if (has_subsititions_defined () != other2.has_subsititions_defined ())
+    return false;
+
+  if (has_subsititions_defined ())
+    {
+      if (get_num_substitutions () != other2.get_num_substitutions ())
+	return false;
+
+      for (size_t i = 0; i < get_num_substitutions (); i++)
+	{
+	  const SubstitutionParamMapping &a = substitutions.at (i);
+	  const SubstitutionParamMapping &b = other2.substitutions.at (i);
+
+	  const ParamType *aa = a.get_param_ty ();
+	  const ParamType *bb = b.get_param_ty ();
+	  BaseType *aaa = aa->resolve ();
+	  BaseType *bbb = bb->resolve ();
+	  if (!aaa->is_equal (*bbb))
+	    return false;
+	}
+    }
+
+  for (size_t i = 0; i < number_of_variants (); i++)
+    {
+      const TyTy::VariantDef *a = get_variants ().at (i);
+      const TyTy::VariantDef *b = other2.get_variants ().at (i);
+
+      if (!a->is_equal (*b))
+	return false;
+    }
+
+  return true;
+}
+
+BaseType *
+ADTType::clone () const
+{
+  std::vector<VariantDef *> cloned_variants;
+  for (auto &variant : variants)
+    cloned_variants.push_back (variant->clone ());
+
+  return new ADTType (get_ref (), get_ty_ref (), identifier, ident,
+		      get_adt_kind (), cloned_variants, clone_substs (),
+		      get_repr_options (), used_arguments,
+		      get_combined_refs ());
+}
+
+BaseType *
+ADTType::monomorphized_clone () const
+{
+  std::vector<VariantDef *> cloned_variants;
+  for (auto &variant : variants)
+    cloned_variants.push_back (variant->monomorphized_clone ());
+
+  return new ADTType (get_ref (), get_ty_ref (), identifier, ident,
+		      get_adt_kind (), cloned_variants, clone_substs (),
+		      get_repr_options (), used_arguments,
+		      get_combined_refs ());
+}
+
+static bool
+handle_substitions (SubstitutionArgumentMappings &subst_mappings,
+		    StructFieldType *field)
+{
+  auto fty = field->get_field_type ();
+  bool is_param_ty = fty->get_kind () == TypeKind::PARAM;
+  if (is_param_ty)
+    {
+      ParamType *p = static_cast<ParamType *> (fty);
+
+      SubstitutionArg arg = SubstitutionArg::error ();
+      bool ok = subst_mappings.get_argument_for_symbol (p, &arg);
+      if (ok)
+	{
+	  auto argt = arg.get_tyty ();
+	  bool arg_is_param = argt->get_kind () == TyTy::TypeKind::PARAM;
+	  bool arg_is_concrete = argt->get_kind () != TyTy::TypeKind::INFER;
+
+	  if (arg_is_param || arg_is_concrete)
+	    {
+	      auto new_field = argt->clone ();
+	      new_field->set_ref (fty->get_ref ());
+	      field->set_field_type (new_field);
+	    }
+	  else
+	    {
+	      field->get_field_type ()->set_ty_ref (argt->get_ref ());
+	    }
+	}
+    }
+  else if (fty->has_subsititions_defined () || fty->contains_type_parameters ())
+    {
+      BaseType *concrete
+	= Resolver::SubstMapperInternal::Resolve (fty, subst_mappings);
+
+      if (concrete->get_kind () == TyTy::TypeKind::ERROR)
+	{
+	  rust_error_at (subst_mappings.get_locus (),
+			 "Failed to resolve field substitution type: %s",
+			 fty->as_string ().c_str ());
+	  return false;
+	}
+
+      auto new_field = concrete->clone ();
+      new_field->set_ref (fty->get_ref ());
+      field->set_field_type (new_field);
+    }
+
+  return true;
+}
+
+ADTType *
+ADTType::handle_substitions (SubstitutionArgumentMappings subst_mappings)
+{
+  ADTType *adt = static_cast<ADTType *> (clone ());
+  adt->set_ty_ref (mappings->get_next_hir_id ());
+  adt->used_arguments = subst_mappings;
+
+  for (auto &sub : adt->get_substs ())
+    {
+      SubstitutionArg arg = SubstitutionArg::error ();
+      bool ok
+	= subst_mappings.get_argument_for_symbol (sub.get_param_ty (), &arg);
+      if (ok)
+	sub.fill_param_ty (subst_mappings, subst_mappings.get_locus ());
+    }
+
+  for (auto &variant : adt->get_variants ())
+    {
+      if (variant->is_dataless_variant ())
+	continue;
+
+      for (auto &field : variant->get_fields ())
+	{
+	  bool ok = ::Rust::TyTy::handle_substitions (subst_mappings, field);
+	  if (!ok)
+	    return adt;
+	}
+    }
+
+  return adt;
+}
+
+void
+TupleType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+TupleType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+TupleType::as_string () const
+{
+  std::string fields_buffer;
+  for (const TyVar &field : get_fields ())
+    {
+      fields_buffer += field.get_tyty ()->as_string ();
+      fields_buffer += ", ";
+    }
+  return "(" + fields_buffer + ")";
+}
+
+BaseType *
+TupleType::get_field (size_t index) const
+{
+  return fields.at (index).get_tyty ();
+}
+
+BaseType *
+TupleType::unify (BaseType *other)
+{
+  TupleRules r (this);
+  return r.unify (other);
+}
+
+bool
+TupleType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  TupleCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+bool
+TupleType::is_equal (const BaseType &other) const
+{
+  if (get_kind () != other.get_kind ())
+    return false;
+
+  auto other2 = static_cast<const TupleType &> (other);
+  if (num_fields () != other2.num_fields ())
+    return false;
+
+  for (size_t i = 0; i < num_fields (); i++)
+    {
+      if (!get_field (i)->is_equal (*other2.get_field (i)))
+	return false;
+    }
+  return true;
+}
+
+BaseType *
+TupleType::clone () const
+{
+  std::vector<TyVar> cloned_fields;
+  for (const auto &f : fields)
+    cloned_fields.push_back (f.clone ());
+
+  return new TupleType (get_ref (), get_ty_ref (), get_ident ().locus,
+			cloned_fields, get_combined_refs ());
+}
+
+BaseType *
+TupleType::monomorphized_clone () const
+{
+  std::vector<TyVar> cloned_fields;
+  for (const auto &f : fields)
+    cloned_fields.push_back (f.monomorphized_clone ());
+
+  return new TupleType (get_ref (), get_ty_ref (), get_ident ().locus,
+			cloned_fields, get_combined_refs ());
+}
+
+TupleType *
+TupleType::handle_substitions (SubstitutionArgumentMappings mappings)
+{
+  auto mappings_table = Analysis::Mappings::get ();
+
+  TupleType *tuple = static_cast<TupleType *> (clone ());
+  tuple->set_ref (mappings_table->get_next_hir_id ());
+  tuple->set_ty_ref (mappings_table->get_next_hir_id ());
+
+  for (size_t i = 0; i < tuple->fields.size (); i++)
+    {
+      TyVar &field = fields.at (i);
+      if (field.get_tyty ()->contains_type_parameters ())
+	{
+	  BaseType *concrete
+	    = Resolver::SubstMapperInternal::Resolve (field.get_tyty (),
+						      mappings);
+	  tuple->fields[i]
+	    = TyVar::subst_covariant_var (field.get_tyty (), concrete);
+	}
+    }
+
+  return tuple;
+}
+
+void
+FnType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+FnType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+FnType::as_string () const
+{
+  std::string params_str = "";
+  for (auto &param : params)
+    {
+      auto pattern = param.first;
+      auto ty = param.second;
+      params_str += pattern->as_string () + " " + ty->as_string ();
+      params_str += ",";
+    }
+
+  std::string ret_str = type->as_string ();
+  return "fn" + subst_as_string () + " (" + params_str + ") -> " + ret_str;
+}
+
+BaseType *
+FnType::unify (BaseType *other)
+{
+  FnRules r (this);
+  return r.unify (other);
+}
+
+bool
+FnType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  FnCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+bool
+FnType::is_equal (const BaseType &other) const
+{
+  if (get_kind () != other.get_kind ())
+    return false;
+
+  auto other2 = static_cast<const FnType &> (other);
+  if (get_identifier ().compare (other2.get_identifier ()) != 0)
+    return false;
+
+  if (!get_return_type ()->is_equal (*other2.get_return_type ()))
+    return false;
+
+  if (has_subsititions_defined () != other2.has_subsititions_defined ())
+    return false;
+
+  if (has_subsititions_defined ())
+    {
+      if (get_num_substitutions () != other2.get_num_substitutions ())
+	return false;
+
+      const FnType &ofn = static_cast<const FnType &> (other);
+      for (size_t i = 0; i < get_num_substitutions (); i++)
+	{
+	  const SubstitutionParamMapping &a = get_substs ().at (i);
+	  const SubstitutionParamMapping &b = ofn.get_substs ().at (i);
+
+	  const ParamType *pa = a.get_param_ty ();
+	  const ParamType *pb = b.get_param_ty ();
+
+	  if (!pa->is_equal (*pb))
+	    return false;
+	}
+    }
+
+  if (num_params () != other2.num_params ())
+    return false;
+
+  for (size_t i = 0; i < num_params (); i++)
+    {
+      auto lhs = param_at (i).second;
+      auto rhs = other2.param_at (i).second;
+      if (!lhs->is_equal (*rhs))
+	return false;
+    }
+  return true;
+}
+
+BaseType *
+FnType::clone () const
+{
+  std::vector<std::pair<HIR::Pattern *, BaseType *>> cloned_params;
+  for (auto &p : params)
+    cloned_params.push_back ({p.first, p.second->clone ()});
+
+  return new FnType (get_ref (), get_ty_ref (), get_id (), get_identifier (),
+		     ident, flags, abi, std::move (cloned_params),
+		     get_return_type ()->clone (), clone_substs (),
+		     get_combined_refs ());
+}
+
+BaseType *
+FnType::monomorphized_clone () const
+{
+  std::vector<std::pair<HIR::Pattern *, BaseType *>> cloned_params;
+  for (auto &p : params)
+    cloned_params.push_back ({p.first, p.second->monomorphized_clone ()});
+
+  return new FnType (get_ref (), get_ty_ref (), get_id (), get_identifier (),
+		     ident, flags, abi, std::move (cloned_params),
+		     get_return_type ()->clone (), clone_substs (),
+		     get_combined_refs ());
+}
+
+FnType *
+FnType::handle_substitions (SubstitutionArgumentMappings subst_mappings)
+{
+  FnType *fn = static_cast<FnType *> (clone ());
+  fn->set_ty_ref (mappings->get_next_hir_id ());
+  fn->used_arguments = subst_mappings;
+
+  for (auto &sub : fn->get_substs ())
+    {
+      SubstitutionArg arg = SubstitutionArg::error ();
+
+      bool ok
+	= subst_mappings.get_argument_for_symbol (sub.get_param_ty (), &arg);
+      if (ok)
+	{
+	  sub.fill_param_ty (subst_mappings, subst_mappings.get_locus ());
+	}
+    }
+
+  auto fty = fn->get_return_type ();
+  bool is_param_ty = fty->get_kind () == TypeKind::PARAM;
+  if (is_param_ty)
+    {
+      ParamType *p = static_cast<ParamType *> (fty);
+
+      SubstitutionArg arg = SubstitutionArg::error ();
+      bool ok = subst_mappings.get_argument_for_symbol (p, &arg);
+      if (ok)
+	{
+	  auto argt = arg.get_tyty ();
+	  bool arg_is_param = argt->get_kind () == TyTy::TypeKind::PARAM;
+	  bool arg_is_concrete = argt->get_kind () != TyTy::TypeKind::INFER;
+
+	  if (arg_is_param || arg_is_concrete)
+	    {
+	      auto new_field = argt->clone ();
+	      new_field->set_ref (fty->get_ref ());
+	      fn->type = new_field;
+	    }
+	  else
+	    {
+	      fty->set_ty_ref (argt->get_ref ());
+	    }
+	}
+    }
+  else if (fty->needs_generic_substitutions ()
+	   || fty->contains_type_parameters ())
+    {
+      BaseType *concrete
+	= Resolver::SubstMapperInternal::Resolve (fty, subst_mappings);
+
+      if (concrete == nullptr || concrete->get_kind () == TyTy::TypeKind::ERROR)
+	{
+	  rust_error_at (subst_mappings.get_locus (),
+			 "Failed to resolve field substitution type: %s",
+			 fty->as_string ().c_str ());
+	  return nullptr;
+	}
+
+      auto new_field = concrete->clone ();
+      new_field->set_ref (fty->get_ref ());
+      fn->type = new_field;
+    }
+
+  for (auto &param : fn->get_params ())
+    {
+      auto fty = param.second;
+
+      bool is_param_ty = fty->get_kind () == TypeKind::PARAM;
+      if (is_param_ty)
+	{
+	  ParamType *p = static_cast<ParamType *> (fty);
+
+	  SubstitutionArg arg = SubstitutionArg::error ();
+	  bool ok = subst_mappings.get_argument_for_symbol (p, &arg);
+	  if (ok)
+	    {
+	      auto argt = arg.get_tyty ();
+	      bool arg_is_param = argt->get_kind () == TyTy::TypeKind::PARAM;
+	      bool arg_is_concrete = argt->get_kind () != TyTy::TypeKind::INFER;
+
+	      if (arg_is_param || arg_is_concrete)
+		{
+		  auto new_field = argt->clone ();
+		  new_field->set_ref (fty->get_ref ());
+		  param.second = new_field;
+		}
+	      else
+		{
+		  fty->set_ty_ref (argt->get_ref ());
+		}
+	    }
+	}
+      else if (fty->has_subsititions_defined ()
+	       || fty->contains_type_parameters ())
+	{
+	  BaseType *concrete
+	    = Resolver::SubstMapperInternal::Resolve (fty, subst_mappings);
+
+	  if (concrete == nullptr
+	      || concrete->get_kind () == TyTy::TypeKind::ERROR)
+	    {
+	      rust_error_at (subst_mappings.get_locus (),
+			     "Failed to resolve field substitution type: %s",
+			     fty->as_string ().c_str ());
+	      return nullptr;
+	    }
+
+	  auto new_field = concrete->clone ();
+	  new_field->set_ref (fty->get_ref ());
+	  param.second = new_field;
+	}
+    }
+
+  return fn;
+}
+
+void
+FnPtr::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+FnPtr::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+FnPtr::as_string () const
+{
+  std::string params_str;
+
+  auto &params = get_params ();
+  for (auto &p : params)
+    {
+      params_str += p.get_tyty ()->as_string () + " ,";
+    }
+
+  return "fnptr (" + params_str + ") -> " + get_return_type ()->as_string ();
+}
+
+BaseType *
+FnPtr::unify (BaseType *other)
+{
+  FnptrRules r (this);
+  return r.unify (other);
+}
+
+bool
+FnPtr::can_eq (const BaseType *other, bool emit_errors) const
+{
+  FnptrCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+bool
+FnPtr::is_equal (const BaseType &other) const
+{
+  if (get_kind () != other.get_kind ())
+    return false;
+
+  auto other2 = static_cast<const FnPtr &> (other);
+  auto this_ret_type = get_return_type ();
+  auto other_ret_type = other2.get_return_type ();
+  if (this_ret_type->is_equal (*other_ret_type))
+    return false;
+
+  if (num_params () != other2.num_params ())
+    return false;
+
+  for (size_t i = 0; i < num_params (); i++)
+    {
+      if (!param_at (i)->is_equal (*other2.param_at (i)))
+	return false;
+    }
+  return true;
+}
+
+BaseType *
+FnPtr::clone () const
+{
+  std::vector<TyVar> cloned_params;
+  for (auto &p : params)
+    cloned_params.push_back (TyVar (p.get_ref ()));
+
+  return new FnPtr (get_ref (), get_ty_ref (), ident.locus,
+		    std::move (cloned_params), result_type,
+		    get_combined_refs ());
+}
+
+BaseType *
+FnPtr::monomorphized_clone () const
+{
+  std::vector<TyVar> cloned_params;
+  for (auto &p : params)
+    cloned_params.push_back (p.monomorphized_clone ());
+
+  return new FnPtr (get_ref (), get_ty_ref (), ident.locus,
+		    std::move (cloned_params), result_type,
+		    get_combined_refs ());
+}
+
+void
+ClosureType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ClosureType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+ClosureType::as_string () const
+{
+  return "TODO";
+}
+
+BaseType *
+ClosureType::unify (BaseType *other)
+{
+  ClosureRules r (this);
+  return r.unify (other);
+}
+
+bool
+ClosureType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  ClosureCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+bool
+ClosureType::is_equal (const BaseType &other) const
+{
+  gcc_unreachable ();
+  return false;
+}
+
+BaseType *
+ClosureType::clone () const
+{
+  return new ClosureType (get_ref (), get_ty_ref (), ident, id, parameter_types,
+			  result_type, clone_substs (), get_combined_refs ());
+}
+
+BaseType *
+ClosureType::monomorphized_clone () const
+{
+  return clone ();
+}
+
+ClosureType *
+ClosureType::handle_substitions (SubstitutionArgumentMappings mappings)
+{
+  gcc_unreachable ();
+  return nullptr;
+}
+
+void
+ArrayType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ArrayType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+ArrayType::as_string () const
+{
+  return "[" + get_element_type ()->as_string () + ":" + "CAPACITY" + "]";
+}
+
+BaseType *
+ArrayType::unify (BaseType *other)
+{
+  ArrayRules r (this);
+  return r.unify (other);
+}
+
+bool
+ArrayType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  ArrayCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+bool
+ArrayType::is_equal (const BaseType &other) const
+{
+  if (get_kind () != other.get_kind ())
+    return false;
+
+  auto other2 = static_cast<const ArrayType &> (other);
+
+  auto this_element_type = get_element_type ();
+  auto other_element_type = other2.get_element_type ();
+
+  return this_element_type->is_equal (*other_element_type);
+}
+
+BaseType *
+ArrayType::get_element_type () const
+{
+  return element_type.get_tyty ();
+}
+
+BaseType *
+ArrayType::clone () const
+{
+  return new ArrayType (get_ref (), get_ty_ref (), ident.locus, capacity_expr,
+			element_type, get_combined_refs ());
+}
+
+BaseType *
+ArrayType::monomorphized_clone () const
+{
+  return new ArrayType (get_ref (), get_ty_ref (), ident.locus, capacity_expr,
+			element_type.monomorphized_clone (),
+			get_combined_refs ());
+}
+
+ArrayType *
+ArrayType::handle_substitions (SubstitutionArgumentMappings mappings)
+{
+  auto mappings_table = Analysis::Mappings::get ();
+
+  ArrayType *ref = static_cast<ArrayType *> (clone ());
+  ref->set_ty_ref (mappings_table->get_next_hir_id ());
+
+  // might be &T or &ADT so this needs to be recursive
+  auto base = ref->get_element_type ();
+  BaseType *concrete = Resolver::SubstMapperInternal::Resolve (base, mappings);
+  ref->element_type = TyVar::subst_covariant_var (base, concrete);
+
+  return ref;
+}
+
+void
+SliceType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+SliceType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+SliceType::as_string () const
+{
+  return "[" + get_element_type ()->as_string () + "]";
+}
+
+BaseType *
+SliceType::unify (BaseType *other)
+{
+  SliceRules r (this);
+  return r.unify (other);
+}
+
+bool
+SliceType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  SliceCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+bool
+SliceType::is_equal (const BaseType &other) const
+{
+  if (get_kind () != other.get_kind ())
+    return false;
+
+  auto other2 = static_cast<const SliceType &> (other);
+
+  auto this_element_type = get_element_type ();
+  auto other_element_type = other2.get_element_type ();
+
+  return this_element_type->is_equal (*other_element_type);
+}
+
+BaseType *
+SliceType::get_element_type () const
+{
+  return element_type.get_tyty ();
+}
+
+BaseType *
+SliceType::clone () const
+{
+  return new SliceType (get_ref (), get_ty_ref (), ident.locus,
+			element_type.clone (), get_combined_refs ());
+}
+
+BaseType *
+SliceType::monomorphized_clone () const
+{
+  return new SliceType (get_ref (), get_ty_ref (), ident.locus,
+			element_type.monomorphized_clone (),
+			get_combined_refs ());
+}
+
+SliceType *
+SliceType::handle_substitions (SubstitutionArgumentMappings mappings)
+{
+  auto mappings_table = Analysis::Mappings::get ();
+
+  SliceType *ref = static_cast<SliceType *> (clone ());
+  ref->set_ty_ref (mappings_table->get_next_hir_id ());
+
+  // might be &T or &ADT so this needs to be recursive
+  auto base = ref->get_element_type ();
+  BaseType *concrete = Resolver::SubstMapperInternal::Resolve (base, mappings);
+  ref->element_type = TyVar::subst_covariant_var (base, concrete);
+
+  return ref;
+}
+
+void
+BoolType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+BoolType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+BoolType::as_string () const
+{
+  return "bool";
+}
+
+BaseType *
+BoolType::unify (BaseType *other)
+{
+  BoolRules r (this);
+  return r.unify (other);
+}
+
+bool
+BoolType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  BoolCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+BaseType *
+BoolType::clone () const
+{
+  return new BoolType (get_ref (), get_ty_ref (), get_combined_refs ());
+}
+
+BaseType *
+BoolType::monomorphized_clone () const
+{
+  return clone ();
+}
+
+void
+IntType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+IntType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+IntType::as_string () const
+{
+  switch (int_kind)
+    {
+    case I8:
+      return "i8";
+    case I16:
+      return "i16";
+    case I32:
+      return "i32";
+    case I64:
+      return "i64";
+    case I128:
+      return "i128";
+    }
+  gcc_unreachable ();
+  return "__unknown_int_type";
+}
+
+BaseType *
+IntType::unify (BaseType *other)
+{
+  IntRules r (this);
+  return r.unify (other);
+}
+
+bool
+IntType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  IntCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+BaseType *
+IntType::clone () const
+{
+  return new IntType (get_ref (), get_ty_ref (), get_int_kind (),
+		      get_combined_refs ());
+}
+
+BaseType *
+IntType::monomorphized_clone () const
+{
+  return clone ();
+}
+
+bool
+IntType::is_equal (const BaseType &other) const
+{
+  if (!BaseType::is_equal (other))
+    return false;
+
+  const IntType &o = static_cast<const IntType &> (other);
+  return get_int_kind () == o.get_int_kind ();
+}
+
+void
+UintType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+UintType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+UintType::as_string () const
+{
+  switch (uint_kind)
+    {
+    case U8:
+      return "u8";
+    case U16:
+      return "u16";
+    case U32:
+      return "u32";
+    case U64:
+      return "u64";
+    case U128:
+      return "u128";
+    }
+  gcc_unreachable ();
+  return "__unknown_uint_type";
+}
+
+BaseType *
+UintType::unify (BaseType *other)
+{
+  UintRules r (this);
+  return r.unify (other);
+}
+
+bool
+UintType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  UintCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+BaseType *
+UintType::clone () const
+{
+  return new UintType (get_ref (), get_ty_ref (), get_uint_kind (),
+		       get_combined_refs ());
+}
+
+BaseType *
+UintType::monomorphized_clone () const
+{
+  return clone ();
+}
+
+bool
+UintType::is_equal (const BaseType &other) const
+{
+  if (!BaseType::is_equal (other))
+    return false;
+
+  const UintType &o = static_cast<const UintType &> (other);
+  return get_uint_kind () == o.get_uint_kind ();
+}
+
+void
+FloatType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+FloatType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+FloatType::as_string () const
+{
+  switch (float_kind)
+    {
+    case F32:
+      return "f32";
+    case F64:
+      return "f64";
+    }
+  gcc_unreachable ();
+  return "__unknown_float_type";
+}
+
+BaseType *
+FloatType::unify (BaseType *other)
+{
+  FloatRules r (this);
+  return r.unify (other);
+}
+
+bool
+FloatType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  FloatCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+BaseType *
+FloatType::clone () const
+{
+  return new FloatType (get_ref (), get_ty_ref (), get_float_kind (),
+			get_combined_refs ());
+}
+
+BaseType *
+FloatType::monomorphized_clone () const
+{
+  return clone ();
+}
+
+bool
+FloatType::is_equal (const BaseType &other) const
+{
+  if (!BaseType::is_equal (other))
+    return false;
+
+  const FloatType &o = static_cast<const FloatType &> (other);
+  return get_float_kind () == o.get_float_kind ();
+}
+
+void
+USizeType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+USizeType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+USizeType::as_string () const
+{
+  return "usize";
+}
+
+BaseType *
+USizeType::unify (BaseType *other)
+{
+  USizeRules r (this);
+  return r.unify (other);
+}
+
+bool
+USizeType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  USizeCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+BaseType *
+USizeType::clone () const
+{
+  return new USizeType (get_ref (), get_ty_ref (), get_combined_refs ());
+}
+
+BaseType *
+USizeType::monomorphized_clone () const
+{
+  return clone ();
+}
+
+void
+ISizeType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ISizeType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+ISizeType::as_string () const
+{
+  return "isize";
+}
+
+BaseType *
+ISizeType::unify (BaseType *other)
+{
+  ISizeRules r (this);
+  return r.unify (other);
+}
+
+bool
+ISizeType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  ISizeCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+BaseType *
+ISizeType::clone () const
+{
+  return new ISizeType (get_ref (), get_ty_ref (), get_combined_refs ());
+}
+
+BaseType *
+ISizeType::monomorphized_clone () const
+{
+  return clone ();
+}
+
+void
+CharType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+CharType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+CharType::as_string () const
+{
+  return "char";
+}
+
+BaseType *
+CharType::unify (BaseType *other)
+{
+  CharRules r (this);
+  return r.unify (other);
+}
+
+bool
+CharType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  CharCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+BaseType *
+CharType::clone () const
+{
+  return new CharType (get_ref (), get_ty_ref (), get_combined_refs ());
+}
+
+BaseType *
+CharType::monomorphized_clone () const
+{
+  return clone ();
+}
+
+void
+ReferenceType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ReferenceType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+ReferenceType::as_string () const
+{
+  return std::string ("&") + (is_mutable () ? "mut" : "") + " "
+	 + get_base ()->as_string ();
+}
+
+BaseType *
+ReferenceType::unify (BaseType *other)
+{
+  ReferenceRules r (this);
+  return r.unify (other);
+}
+
+bool
+ReferenceType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  ReferenceCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+bool
+ReferenceType::is_equal (const BaseType &other) const
+{
+  if (get_kind () != other.get_kind ())
+    return false;
+
+  auto other2 = static_cast<const ReferenceType &> (other);
+  if (mutability () != other2.mutability ())
+    return false;
+
+  return get_base ()->is_equal (*other2.get_base ());
+}
+
+BaseType *
+ReferenceType::get_base () const
+{
+  return base.get_tyty ();
+}
+
+BaseType *
+ReferenceType::clone () const
+{
+  return new ReferenceType (get_ref (), get_ty_ref (), base, mutability (),
+			    get_combined_refs ());
+}
+
+BaseType *
+ReferenceType::monomorphized_clone () const
+{
+  return new ReferenceType (get_ref (), get_ty_ref (),
+			    base.monomorphized_clone (), mutability (),
+			    get_combined_refs ());
+}
+
+ReferenceType *
+ReferenceType::handle_substitions (SubstitutionArgumentMappings mappings)
+{
+  auto mappings_table = Analysis::Mappings::get ();
+
+  ReferenceType *ref = static_cast<ReferenceType *> (clone ());
+  ref->set_ty_ref (mappings_table->get_next_hir_id ());
+
+  // might be &T or &ADT so this needs to be recursive
+  auto base = ref->get_base ();
+  BaseType *concrete = Resolver::SubstMapperInternal::Resolve (base, mappings);
+  ref->base = TyVar::subst_covariant_var (base, concrete);
+
+  return ref;
+}
+
+void
+PointerType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+PointerType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+PointerType::as_string () const
+{
+  return std::string ("* ") + (is_mutable () ? "mut" : "const") + " "
+	 + get_base ()->as_string ();
+}
+
+BaseType *
+PointerType::unify (BaseType *other)
+{
+  PointerRules r (this);
+  return r.unify (other);
+}
+
+bool
+PointerType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  PointerCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+bool
+PointerType::is_equal (const BaseType &other) const
+{
+  if (get_kind () != other.get_kind ())
+    return false;
+
+  auto other2 = static_cast<const PointerType &> (other);
+  if (mutability () != other2.mutability ())
+    return false;
+
+  return get_base ()->is_equal (*other2.get_base ());
+}
+
+BaseType *
+PointerType::get_base () const
+{
+  return base.get_tyty ();
+}
+
+BaseType *
+PointerType::clone () const
+{
+  return new PointerType (get_ref (), get_ty_ref (), base, mutability (),
+			  get_combined_refs ());
+}
+
+BaseType *
+PointerType::monomorphized_clone () const
+{
+  return new PointerType (get_ref (), get_ty_ref (),
+			  base.monomorphized_clone (), mutability (),
+			  get_combined_refs ());
+}
+
+PointerType *
+PointerType::handle_substitions (SubstitutionArgumentMappings mappings)
+{
+  auto mappings_table = Analysis::Mappings::get ();
+
+  PointerType *ref = static_cast<PointerType *> (clone ());
+  ref->set_ty_ref (mappings_table->get_next_hir_id ());
+
+  // might be &T or &ADT so this needs to be recursive
+  auto base = ref->get_base ();
+  BaseType *concrete = Resolver::SubstMapperInternal::Resolve (base, mappings);
+  ref->base = TyVar::subst_covariant_var (base, concrete);
+
+  return ref;
+}
+
+void
+ParamType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ParamType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+ParamType::as_string () const
+{
+  if (!can_resolve ())
+    {
+      return get_symbol () + " REF: " + std::to_string (get_ref ());
+    }
+
+  BaseType *lookup = resolve ();
+  return get_symbol () + "=" + lookup->as_string ();
+}
+
+std::string
+ParamType::get_name () const
+{
+  if (!can_resolve ())
+    return get_symbol ();
+
+  return resolve ()->get_name ();
+}
+
+BaseType *
+ParamType::unify (BaseType *other)
+{
+  ParamRules r (this);
+  return r.unify (other);
+}
+
+bool
+ParamType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  ParamCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+BaseType *
+ParamType::clone () const
+{
+  return new ParamType (get_symbol (), ident.locus, get_ref (), get_ty_ref (),
+			param, get_specified_bounds (), get_combined_refs ());
+}
+
+BaseType *
+ParamType::monomorphized_clone () const
+{
+  return resolve ()->clone ();
+}
+
+std::string
+ParamType::get_symbol () const
+{
+  return symbol;
+}
+
+BaseType *
+ParamType::resolve () const
+{
+  TyVar var (get_ty_ref ());
+  BaseType *r = var.get_tyty ();
+
+  while (r->get_kind () == TypeKind::PARAM)
+    {
+      ParamType *rr = static_cast<ParamType *> (r);
+      if (!rr->can_resolve ())
+	break;
+
+      TyVar v (rr->get_ty_ref ());
+      r = v.get_tyty ();
+    }
+
+  if (r->get_kind () == TypeKind::PARAM && (r->get_ref () == r->get_ty_ref ()))
+    return TyVar (r->get_ty_ref ()).get_tyty ();
+
+  return r;
+}
+
+bool
+ParamType::is_equal (const BaseType &other) const
+{
+  if (get_kind () != other.get_kind ())
+    {
+      if (!can_resolve ())
+	return false;
+
+      return resolve ()->is_equal (other);
+    }
+
+  auto other2 = static_cast<const ParamType &> (other);
+  if (can_resolve () != other2.can_resolve ())
+    return false;
+
+  if (can_resolve ())
+    return resolve ()->can_eq (other2.resolve (), false);
+
+  return get_symbol ().compare (other2.get_symbol ()) == 0;
+}
+
+ParamType *
+ParamType::handle_substitions (SubstitutionArgumentMappings subst_mappings)
+{
+  SubstitutionArg arg = SubstitutionArg::error ();
+  bool ok = subst_mappings.get_argument_for_symbol (this, &arg);
+  if (!ok || arg.is_error ())
+    return this;
+
+  ParamType *p = static_cast<ParamType *> (clone ());
+  subst_mappings.on_param_subst (*p, arg);
+
+  // there are two cases one where we substitute directly to a new PARAM and
+  // otherwise
+  if (arg.get_tyty ()->get_kind () == TyTy::TypeKind::PARAM)
+    {
+      p->set_ty_ref (arg.get_tyty ()->get_ref ());
+      return p;
+    }
+
+  // this is the new subst that this needs to pass
+  p->set_ref (mappings->get_next_hir_id ());
+  p->set_ty_ref (arg.get_tyty ()->get_ref ());
+
+  return p;
+}
+
+BaseType *
+StrType::clone () const
+{
+  return new StrType (get_ref (), get_ty_ref (), get_combined_refs ());
+}
+
+BaseType *
+StrType::monomorphized_clone () const
+{
+  return clone ();
+}
+
+void
+StrType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+StrType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+StrType::as_string () const
+{
+  return "str";
+}
+
+BaseType *
+StrType::unify (BaseType *other)
+{
+  StrRules r (this);
+  return r.unify (other);
+}
+
+bool
+StrType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  StrCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+bool
+StrType::is_equal (const BaseType &other) const
+{
+  return get_kind () == other.get_kind ();
+}
+
+void
+NeverType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+NeverType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+NeverType::as_string () const
+{
+  return "!";
+}
+
+BaseType *
+NeverType::unify (BaseType *other)
+{
+  NeverRules r (this);
+  return r.unify (other);
+}
+
+bool
+NeverType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  NeverCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+BaseType *
+NeverType::clone () const
+{
+  return new NeverType (get_ref (), get_ty_ref (), get_combined_refs ());
+}
+
+BaseType *
+NeverType::monomorphized_clone () const
+{
+  return clone ();
+}
+
+// placeholder type
+
+void
+PlaceholderType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+PlaceholderType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+PlaceholderType::as_string () const
+{
+  return "<placeholder:" + (can_resolve () ? resolve ()->as_string () : "")
+	 + ">";
+}
+
+BaseType *
+PlaceholderType::unify (BaseType *other)
+{
+  PlaceholderRules r (this);
+  return r.unify (other);
+}
+
+bool
+PlaceholderType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  PlaceholderCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+BaseType *
+PlaceholderType::clone () const
+{
+  return new PlaceholderType (get_symbol (), get_ref (), get_ty_ref (),
+			      get_combined_refs ());
+}
+
+BaseType *
+PlaceholderType::monomorphized_clone () const
+{
+  if (can_resolve ())
+    return resolve ()->monomorphized_clone ();
+
+  return clone ();
+}
+
+void
+PlaceholderType::set_associated_type (HirId ref)
+{
+  auto context = Resolver::TypeCheckContext::get ();
+  context->insert_associated_type_mapping (get_ty_ref (), ref);
+}
+
+void
+PlaceholderType::clear_associated_type ()
+{
+  auto context = Resolver::TypeCheckContext::get ();
+  context->clear_associated_type_mapping (get_ty_ref ());
+}
+
+bool
+PlaceholderType::can_resolve () const
+{
+  auto context = Resolver::TypeCheckContext::get ();
+  return context->lookup_associated_type_mapping (get_ty_ref (), nullptr);
+}
+
+BaseType *
+PlaceholderType::resolve () const
+{
+  auto context = Resolver::TypeCheckContext::get ();
+
+  HirId mapping;
+  bool ok = context->lookup_associated_type_mapping (get_ty_ref (), &mapping);
+  rust_assert (ok);
+
+  return TyVar (mapping).get_tyty ();
+}
+
+bool
+PlaceholderType::is_equal (const BaseType &other) const
+{
+  if (get_kind () != other.get_kind ())
+    {
+      if (!can_resolve ())
+	return false;
+
+      return resolve ()->is_equal (other);
+    }
+
+  auto other2 = static_cast<const PlaceholderType &> (other);
+  return get_symbol ().compare (other2.get_symbol ()) == 0;
+}
+
+// Projection type
+
+void
+ProjectionType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+ProjectionType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+ProjectionType::as_string () const
+{
+  return "<Projection=" + subst_as_string () + "::" + base->as_string () + ">";
+}
+
+BaseType *
+ProjectionType::unify (BaseType *other)
+{
+  return base->unify (other);
+}
+
+bool
+ProjectionType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  return base->can_eq (other, emit_errors);
+}
+
+BaseType *
+ProjectionType::clone () const
+{
+  return new ProjectionType (get_ref (), get_ty_ref (), base->clone (), trait,
+			     item, clone_substs (), used_arguments,
+			     get_combined_refs ());
+}
+
+BaseType *
+ProjectionType::monomorphized_clone () const
+{
+  return get ()->monomorphized_clone ();
+}
+
+ProjectionType *
+ProjectionType::handle_substitions (SubstitutionArgumentMappings subst_mappings)
+{
+  // // do we really need to substitute this?
+  // if (base->needs_generic_substitutions () || base->contains_type_parameters
+  // ())
+  //   {
+  //     return this;
+  //   }
+
+  ProjectionType *projection = static_cast<ProjectionType *> (clone ());
+  projection->set_ty_ref (mappings->get_next_hir_id ());
+  projection->used_arguments = subst_mappings;
+
+  auto context = Resolver::TypeCheckContext::get ();
+  context->insert_implicit_type (projection->get_ty_ref (), projection);
+
+  for (auto &sub : projection->get_substs ())
+    {
+      SubstitutionArg arg = SubstitutionArg::error ();
+      bool ok
+	= subst_mappings.get_argument_for_symbol (sub.get_param_ty (), &arg);
+      if (ok)
+	sub.fill_param_ty (subst_mappings, subst_mappings.get_locus ());
+    }
+
+  auto fty = projection->base;
+  bool is_param_ty = fty->get_kind () == TypeKind::PARAM;
+  if (is_param_ty)
+    {
+      ParamType *p = static_cast<ParamType *> (fty);
+
+      SubstitutionArg arg = SubstitutionArg::error ();
+      bool ok = subst_mappings.get_argument_for_symbol (p, &arg);
+      if (ok)
+	{
+	  auto argt = arg.get_tyty ();
+	  bool arg_is_param = argt->get_kind () == TyTy::TypeKind::PARAM;
+	  bool arg_is_concrete = argt->get_kind () != TyTy::TypeKind::INFER;
+
+	  if (arg_is_param || arg_is_concrete)
+	    {
+	      auto new_field = argt->clone ();
+	      new_field->set_ref (fty->get_ref ());
+	      projection->base = new_field;
+	    }
+	  else
+	    {
+	      fty->set_ty_ref (argt->get_ref ());
+	    }
+	}
+    }
+  else if (fty->needs_generic_substitutions ()
+	   || fty->contains_type_parameters ())
+    {
+      BaseType *concrete
+	= Resolver::SubstMapperInternal::Resolve (fty, subst_mappings);
+
+      if (concrete == nullptr || concrete->get_kind () == TyTy::TypeKind::ERROR)
+	{
+	  rust_error_at (subst_mappings.get_locus (),
+			 "Failed to resolve field substitution type: %s",
+			 fty->as_string ().c_str ());
+	  return nullptr;
+	}
+
+      projection->base = concrete;
+    }
+
+  return projection;
+}
+
+void
+DynamicObjectType::accept_vis (TyVisitor &vis)
+{
+  vis.visit (*this);
+}
+
+void
+DynamicObjectType::accept_vis (TyConstVisitor &vis) const
+{
+  vis.visit (*this);
+}
+
+std::string
+DynamicObjectType::as_string () const
+{
+  return "dyn [" + raw_bounds_as_string () + "]";
+}
+
+BaseType *
+DynamicObjectType::unify (BaseType *other)
+{
+  DynamicRules r (this);
+  return r.unify (other);
+}
+
+bool
+DynamicObjectType::can_eq (const BaseType *other, bool emit_errors) const
+{
+  DynamicCmp r (this, emit_errors);
+  return r.can_eq (other);
+}
+
+BaseType *
+DynamicObjectType::clone () const
+{
+  return new DynamicObjectType (get_ref (), get_ty_ref (), ident,
+				specified_bounds, get_combined_refs ());
+}
+
+BaseType *
+DynamicObjectType::monomorphized_clone () const
+{
+  return clone ();
+}
+
+std::string
+DynamicObjectType::get_name () const
+{
+  return "dyn [" + raw_bounds_as_name () + "]";
+}
+
+bool
+DynamicObjectType::is_equal (const BaseType &other) const
+{
+  if (get_kind () != other.get_kind ())
+    return false;
+
+  if (num_specified_bounds () != other.num_specified_bounds ())
+    return false;
+
+  return bounds_compatible (other, Location (), false);
+}
+
+const std::vector<
+  std::pair<const Resolver::TraitItemReference *, const TypeBoundPredicate *>>
+DynamicObjectType::get_object_items () const
+{
+  std::vector<
+    std::pair<const Resolver::TraitItemReference *, const TypeBoundPredicate *>>
+    items;
+  for (auto &bound : get_specified_bounds ())
+    {
+      const Resolver::TraitReference *trait = bound.get ();
+      for (auto &item : trait->get_trait_items ())
+	{
+	  if (item.get_trait_item_type ()
+		== Resolver::TraitItemReference::TraitItemType::FN
+	      && item.is_object_safe ())
+	    items.push_back ({&item, &bound});
+	}
+
+      for (auto &super_trait : trait->get_super_traits ())
+	{
+	  for (auto &item : super_trait->get_trait_items ())
+	    {
+	      if (item.get_trait_item_type ()
+		    == Resolver::TraitItemReference::TraitItemType::FN
+		  && item.is_object_safe ())
+		items.push_back ({&item, &bound});
+	    }
+	}
+    }
+  return items;
+}
+
+} // namespace TyTy
+} // namespace Rust
diff --git a/gcc/rust/typecheck/rust-tyty.h b/gcc/rust/typecheck/rust-tyty.h
new file mode 100644
index 00000000000..c47921d44d7
--- /dev/null
+++ b/gcc/rust/typecheck/rust-tyty.h
@@ -0,0 +1,2533 @@
+// 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_TYTY
+#define RUST_TYTY
+
+#include "rust-hir-map.h"
+#include "rust-hir-full.h"
+#include "rust-diagnostics.h"
+#include "rust-abi.h"
+#include "rust-common.h"
+#include "rust-identifier.h"
+
+namespace Rust {
+
+namespace Resolver {
+class TraitReference;
+class TraitItemReference;
+class AssociatedImplTrait;
+} // namespace Resolver
+
+namespace TyTy {
+
+// https://rustc-dev-guide.rust-lang.org/type-inference.html#inference-variables
+// https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html#variants
+enum TypeKind
+{
+  INFER,
+  ADT,
+  STR,
+  REF,
+  POINTER,
+  PARAM,
+  ARRAY,
+  SLICE,
+  FNDEF,
+  FNPTR,
+  TUPLE,
+  BOOL,
+  CHAR,
+  INT,
+  UINT,
+  FLOAT,
+  USIZE,
+  ISIZE,
+  NEVER,
+  PLACEHOLDER,
+  PROJECTION,
+  DYNAMIC,
+  CLOSURE,
+  // there are more to add...
+  ERROR
+};
+
+extern bool
+is_primitive_type_kind (TypeKind kind);
+
+class TypeKindFormat
+{
+public:
+  static std::string to_string (TypeKind kind);
+};
+
+class BaseType;
+class TypeBoundPredicate;
+class TypeBoundPredicateItem
+{
+public:
+  TypeBoundPredicateItem (const TypeBoundPredicate *parent,
+			  const Resolver::TraitItemReference *trait_item_ref)
+    : parent (parent), trait_item_ref (trait_item_ref)
+  {}
+
+  static TypeBoundPredicateItem error ()
+  {
+    return TypeBoundPredicateItem (nullptr, nullptr);
+  }
+
+  bool is_error () const
+  {
+    return parent == nullptr || trait_item_ref == nullptr;
+  }
+
+  BaseType *get_tyty_for_receiver (const TyTy::BaseType *receiver);
+
+  const Resolver::TraitItemReference *get_raw_item () const;
+
+  bool needs_implementation () const;
+
+  const TypeBoundPredicate *get_parent () const { return parent; }
+
+  Location get_locus () const;
+
+private:
+  const TypeBoundPredicate *parent;
+  const Resolver::TraitItemReference *trait_item_ref;
+};
+
+class TypeBoundsMappings
+{
+protected:
+  TypeBoundsMappings (std::vector<TypeBoundPredicate> specified_bounds);
+
+public:
+  std::vector<TypeBoundPredicate> &get_specified_bounds ();
+
+  const std::vector<TypeBoundPredicate> &get_specified_bounds () const;
+
+  size_t num_specified_bounds () const;
+
+  std::string raw_bounds_as_string () const;
+
+  std::string bounds_as_string () const;
+
+  std::string raw_bounds_as_name () const;
+
+protected:
+  void add_bound (TypeBoundPredicate predicate);
+
+  std::vector<TypeBoundPredicate> specified_bounds;
+};
+
+class TyVisitor;
+class TyConstVisitor;
+class BaseType : public TypeBoundsMappings
+{
+public:
+  virtual ~BaseType () {}
+
+  HirId get_ref () const { return ref; }
+
+  void set_ref (HirId id)
+  {
+    if (id != ref)
+      append_reference (ref);
+    ref = id;
+  }
+
+  HirId get_ty_ref () const { return ty_ref; }
+
+  void set_ty_ref (HirId id) { ty_ref = id; }
+
+  virtual void accept_vis (TyVisitor &vis) = 0;
+
+  virtual void accept_vis (TyConstVisitor &vis) const = 0;
+
+  virtual std::string as_string () const = 0;
+
+  virtual std::string get_name () const = 0;
+
+  // Unify two types. Returns a pointer to the newly-created unified ty, or
+  // nullptr if the two ty cannot be unified. The caller is responsible for
+  // releasing the memory of the returned ty.
+  virtual BaseType *unify (BaseType *other) = 0;
+
+  // similar to unify but does not actually perform type unification but
+  // determines whether they are compatible. Consider the following
+  //
+  // fn foo<T>() -> T { ... }
+  // fn foo() -> i32 { ... }
+  //
+  // when the function has been substituted they can be considered equal.
+  //
+  // It can also be used to optional emit errors for trait item compatibility
+  // checks
+  virtual bool can_eq (const BaseType *other, bool emit_errors) const = 0;
+
+  // Check value equality between two ty. Type inference rules are ignored. Two
+  //   ty are considered equal if they're of the same kind, and
+  //     1. (For ADTs, arrays, tuples, refs) have the same underlying ty
+  //     2. (For functions) have the same signature
+  virtual bool is_equal (const BaseType &other) const
+  {
+    return get_kind () == other.get_kind ();
+  }
+
+  bool satisfies_bound (const TypeBoundPredicate &predicate) const;
+
+  bool bounds_compatible (const BaseType &other, Location locus,
+			  bool emit_error) const;
+
+  void inherit_bounds (const BaseType &other);
+
+  void inherit_bounds (
+    const std::vector<TyTy::TypeBoundPredicate> &specified_bounds);
+
+  virtual bool is_unit () const { return false; }
+
+  virtual bool is_concrete () const = 0;
+
+  TypeKind get_kind () const { return kind; }
+
+  /* Returns a pointer to a clone of this. The caller is responsible for
+   * releasing the memory of the returned ty. */
+  virtual BaseType *clone () const = 0;
+
+  // TODO
+  virtual BaseType *monomorphized_clone () const = 0;
+
+  // get_combined_refs returns the chain of node refs involved in unification
+  std::set<HirId> get_combined_refs () const { return combined; }
+
+  void append_reference (HirId id) { combined.insert (id); }
+
+  virtual bool supports_substitutions () const { return false; }
+
+  virtual bool has_subsititions_defined () const { return false; }
+
+  virtual bool can_substitute () const
+  {
+    return supports_substitutions () && has_subsititions_defined ();
+  }
+
+  virtual bool needs_generic_substitutions () const { return false; }
+
+  bool contains_type_parameters () const { return !is_concrete (); }
+
+  std::string mappings_str () const
+  {
+    std::string buffer = "Ref: " + std::to_string (get_ref ())
+			 + " TyRef: " + std::to_string (get_ty_ref ());
+    buffer += "[";
+    for (auto &ref : combined)
+      buffer += std::to_string (ref) + ",";
+    buffer += "]";
+    return "(" + buffer + ")";
+  }
+
+  std::string debug_str () const
+  {
+    return TypeKindFormat::to_string (get_kind ()) + ":" + as_string () + ":"
+	   + mappings_str () + ":" + bounds_as_string ();
+  }
+
+  void debug () const
+  {
+    rust_debug ("[%p] %s", static_cast<const void *> (this),
+		debug_str ().c_str ());
+  }
+
+  // FIXME this will eventually go away
+  const BaseType *get_root () const;
+
+  // This will get the monomorphized type from Params, Placeholders or
+  // Projections if available or error
+  const BaseType *destructure () const;
+
+  const RustIdent &get_ident () const { return ident; }
+
+  Location get_locus () const { return ident.locus; }
+
+protected:
+  BaseType (HirId ref, HirId ty_ref, TypeKind kind, RustIdent ident,
+	    std::set<HirId> refs = std::set<HirId> ())
+    : TypeBoundsMappings ({}), kind (kind), ref (ref), ty_ref (ty_ref),
+      combined (refs), ident (ident), mappings (Analysis::Mappings::get ())
+  {}
+
+  BaseType (HirId ref, HirId ty_ref, TypeKind kind, RustIdent ident,
+	    std::vector<TypeBoundPredicate> specified_bounds,
+	    std::set<HirId> refs = std::set<HirId> ())
+    : TypeBoundsMappings (specified_bounds), kind (kind), ref (ref),
+      ty_ref (ty_ref), combined (refs), ident (ident),
+      mappings (Analysis::Mappings::get ())
+  {}
+
+  TypeKind kind;
+  HirId ref;
+  HirId ty_ref;
+  std::set<HirId> combined;
+  RustIdent ident;
+
+  Analysis::Mappings *mappings;
+};
+
+// this is a placeholder for types that can change like inference variables
+class TyVar
+{
+public:
+  explicit TyVar (HirId ref);
+
+  HirId get_ref () const { return ref; }
+
+  BaseType *get_tyty () const;
+
+  TyVar clone () const;
+
+  TyVar monomorphized_clone () const;
+
+  static TyVar get_implicit_infer_var (Location locus);
+
+  static TyVar subst_covariant_var (TyTy::BaseType *orig,
+				    TyTy::BaseType *subst);
+
+private:
+  HirId ref;
+};
+
+class TyWithLocation
+{
+public:
+  TyWithLocation (BaseType *ty, Location locus);
+  TyWithLocation (BaseType *ty);
+
+  BaseType *get_ty () const { return ty; }
+  Location get_locus () const { return locus; }
+
+private:
+  BaseType *ty;
+  Location locus;
+};
+
+class InferType : public BaseType
+{
+public:
+  enum InferTypeKind
+  {
+    GENERAL,
+    INTEGRAL,
+    FLOAT
+  };
+
+  InferType (HirId ref, InferTypeKind infer_kind, Location locus,
+	     std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::INFER,
+		{Resolver::CanonicalPath::create_empty (), locus}, refs),
+      infer_kind (infer_kind)
+  {}
+
+  InferType (HirId ref, HirId ty_ref, InferTypeKind infer_kind, Location locus,
+	     std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::INFER,
+		{Resolver::CanonicalPath::create_empty (), locus}, refs),
+      infer_kind (infer_kind)
+  {}
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  BaseType *unify (BaseType *other) override;
+
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+
+  InferTypeKind get_infer_kind () const { return infer_kind; }
+
+  std::string get_name () const override final { return as_string (); }
+
+  bool default_type (BaseType **type) const;
+
+  bool is_concrete () const final override { return true; }
+
+private:
+  InferTypeKind infer_kind;
+};
+
+class ErrorType : public BaseType
+{
+public:
+  ErrorType (HirId ref, std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::ERROR,
+		{Resolver::CanonicalPath::create_empty (), Location ()}, refs)
+  {}
+
+  ErrorType (HirId ref, HirId ty_ref, std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::ERROR,
+		{Resolver::CanonicalPath::create_empty (), Location ()}, refs)
+  {}
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  bool is_unit () const override { return true; }
+
+  std::string as_string () const override;
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+
+  std::string get_name () const override final { return as_string (); }
+
+  bool is_concrete () const final override { return false; }
+};
+
+class SubstitutionArgumentMappings;
+class ParamType : public BaseType
+{
+public:
+  ParamType (std::string symbol, Location locus, HirId ref,
+	     HIR::GenericParam &param,
+	     std::vector<TypeBoundPredicate> specified_bounds,
+	     std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::PARAM,
+		{Resolver::CanonicalPath::new_seg (UNKNOWN_NODEID, symbol),
+		 locus},
+		specified_bounds, refs),
+      symbol (symbol), param (param)
+  {}
+
+  ParamType (std::string symbol, Location locus, HirId ref, HirId ty_ref,
+	     HIR::GenericParam &param,
+	     std::vector<TypeBoundPredicate> specified_bounds,
+	     std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::PARAM,
+		{Resolver::CanonicalPath::new_seg (UNKNOWN_NODEID, symbol),
+		 locus},
+		specified_bounds, refs),
+      symbol (symbol), param (param)
+  {}
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+
+  std::string get_symbol () const;
+
+  HIR::GenericParam &get_generic_param () { return param; }
+
+  bool can_resolve () const { return get_ref () != get_ty_ref (); }
+
+  BaseType *resolve () const;
+
+  std::string get_name () const override final;
+
+  bool is_equal (const BaseType &other) const override;
+
+  bool is_concrete () const override final
+  {
+    auto r = resolve ();
+    if (r == this)
+      return false;
+
+    return r->is_concrete ();
+  }
+
+  ParamType *handle_substitions (SubstitutionArgumentMappings mappings);
+
+private:
+  std::string symbol;
+  HIR::GenericParam &param;
+};
+
+class StructFieldType
+{
+public:
+  StructFieldType (HirId ref, std::string name, BaseType *ty)
+    : ref (ref), name (name), ty (ty)
+  {}
+
+  HirId get_ref () const { return ref; }
+
+  std::string as_string () const;
+
+  bool is_equal (const StructFieldType &other) const;
+
+  std::string get_name () const { return name; }
+
+  BaseType *get_field_type () const { return ty; }
+
+  void set_field_type (BaseType *fty) { ty = fty; }
+
+  StructFieldType *clone () const;
+
+  StructFieldType *monomorphized_clone () const;
+
+  bool is_concrete () const { return ty->is_concrete (); }
+
+  void debug () const { rust_debug ("%s", as_string ().c_str ()); }
+
+private:
+  HirId ref;
+  std::string name;
+  BaseType *ty;
+};
+
+class TupleType : public BaseType
+{
+public:
+  TupleType (HirId ref, Location locus,
+	     std::vector<TyVar> fields = std::vector<TyVar> (),
+	     std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::TUPLE,
+		{Resolver::CanonicalPath::create_empty (), locus}, refs),
+      fields (fields)
+  {}
+
+  TupleType (HirId ref, HirId ty_ref, Location locus,
+	     std::vector<TyVar> fields = std::vector<TyVar> (),
+	     std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::TUPLE,
+		{Resolver::CanonicalPath::create_empty (), locus}, refs),
+      fields (fields)
+  {}
+
+  static TupleType *get_unit_type (HirId ref)
+  {
+    return new TupleType (ref, Linemap::predeclared_location ());
+  }
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  bool is_unit () const override { return this->fields.empty (); }
+
+  std::string as_string () const override;
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  bool is_equal (const BaseType &other) const override;
+
+  size_t num_fields () const { return fields.size (); }
+
+  BaseType *get_field (size_t index) const;
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+
+  bool is_concrete () const override final
+  {
+    for (size_t i = 0; i < num_fields (); i++)
+      {
+	if (!get_field (i)->is_concrete ())
+	  return false;
+      }
+    return true;
+  }
+
+  const std::vector<TyVar> &get_fields () const { return fields; }
+
+  std::string get_name () const override final { return as_string (); }
+
+  TupleType *handle_substitions (SubstitutionArgumentMappings mappings);
+
+private:
+  std::vector<TyVar> fields;
+};
+
+class SubstitutionParamMapping
+{
+public:
+  SubstitutionParamMapping (const HIR::TypeParam &generic, ParamType *param)
+    : generic (generic), param (param)
+  {}
+
+  SubstitutionParamMapping (const SubstitutionParamMapping &other)
+    : generic (other.generic), param (other.param)
+  {}
+
+  std::string as_string () const
+  {
+    if (param == nullptr)
+      return "nullptr";
+
+    return param->get_name ();
+  }
+
+  bool fill_param_ty (SubstitutionArgumentMappings &subst_mappings,
+		      Location locus);
+
+  SubstitutionParamMapping clone () const
+  {
+    return SubstitutionParamMapping (generic, static_cast<ParamType *> (
+						param->clone ()));
+  }
+
+  ParamType *get_param_ty () { return param; }
+
+  const ParamType *get_param_ty () const { return param; }
+
+  const HIR::TypeParam &get_generic_param () { return generic; };
+
+  // this is used for the backend to override the HirId ref of the param to
+  // what the concrete type is for the rest of the context
+  void override_context ();
+
+  bool needs_substitution () const
+  {
+    return !(get_param_ty ()->is_concrete ());
+  }
+
+  Location get_param_locus () const { return generic.get_locus (); }
+
+  bool param_has_default_ty () const { return generic.has_type (); }
+
+  BaseType *get_default_ty () const
+  {
+    TyVar var (generic.get_type_mappings ().get_hirid ());
+    return var.get_tyty ();
+  }
+
+  bool need_substitution () const;
+
+private:
+  const HIR::TypeParam &generic;
+  ParamType *param;
+};
+
+class SubstitutionArg
+{
+public:
+  SubstitutionArg (const SubstitutionParamMapping *param, BaseType *argument)
+    : param (param), argument (argument)
+  {}
+
+  // FIXME
+  // the copy constructors need removed - they are unsafe see
+  // TypeBoundPredicate
+  SubstitutionArg (const SubstitutionArg &other)
+    : param (other.param), argument (other.argument)
+  {}
+
+  SubstitutionArg &operator= (const SubstitutionArg &other)
+  {
+    param = other.param;
+    argument = other.argument;
+    return *this;
+  }
+
+  BaseType *get_tyty () { return argument; }
+
+  const BaseType *get_tyty () const { return argument; }
+
+  const SubstitutionParamMapping *get_param_mapping () const { return param; }
+
+  static SubstitutionArg error () { return SubstitutionArg (nullptr, nullptr); }
+
+  bool is_error () const { return param == nullptr || argument == nullptr; }
+
+  bool is_conrete () const
+  {
+    if (argument != nullptr)
+      return true;
+
+    if (argument->get_kind () == TyTy::TypeKind::PARAM)
+      return false;
+
+    return argument->is_concrete ();
+  }
+
+  std::string as_string () const
+  {
+    return param->as_string ()
+	   + (argument != nullptr ? ":" + argument->as_string () : "");
+  }
+
+private:
+  const SubstitutionParamMapping *param;
+  BaseType *argument;
+};
+
+typedef std::function<void (const ParamType &, const SubstitutionArg &)>
+  ParamSubstCb;
+class SubstitutionArgumentMappings
+{
+public:
+  SubstitutionArgumentMappings (std::vector<SubstitutionArg> mappings,
+				Location locus,
+				ParamSubstCb param_subst_cb = nullptr,
+				bool trait_item_flag = false)
+    : mappings (mappings), locus (locus), param_subst_cb (param_subst_cb),
+      trait_item_flag (trait_item_flag)
+  {}
+
+  SubstitutionArgumentMappings (const SubstitutionArgumentMappings &other)
+    : mappings (other.mappings), locus (other.locus),
+      param_subst_cb (other.param_subst_cb),
+      trait_item_flag (other.trait_item_flag)
+  {}
+
+  SubstitutionArgumentMappings &
+  operator= (const SubstitutionArgumentMappings &other)
+  {
+    mappings = other.mappings;
+    locus = other.locus;
+    param_subst_cb = other.param_subst_cb;
+    trait_item_flag = other.trait_item_flag;
+
+    return *this;
+  }
+
+  static SubstitutionArgumentMappings error ()
+  {
+    return SubstitutionArgumentMappings ({}, Location (), nullptr, false);
+  }
+
+  bool is_error () const { return mappings.size () == 0; }
+
+  bool get_argument_for_symbol (const ParamType *param_to_find,
+				SubstitutionArg *argument)
+  {
+    for (auto &mapping : mappings)
+      {
+	const SubstitutionParamMapping *param = mapping.get_param_mapping ();
+	const ParamType *p = param->get_param_ty ();
+
+	if (p->get_symbol ().compare (param_to_find->get_symbol ()) == 0)
+	  {
+	    *argument = mapping;
+	    return true;
+	  }
+      }
+    return false;
+  }
+
+  bool get_argument_at (size_t index, SubstitutionArg *argument)
+  {
+    if (index > mappings.size ())
+      return false;
+
+    *argument = mappings.at (index);
+    return true;
+  }
+
+  // is_concrete means if the used args is non error, ie: non empty this will
+  // verify if actual real types have been put in place of are they still
+  // ParamTy
+  bool is_concrete () const
+  {
+    for (auto &mapping : mappings)
+      {
+	if (!mapping.is_conrete ())
+	  return false;
+      }
+    return true;
+  }
+
+  Location get_locus () const { return locus; }
+
+  size_t size () const { return mappings.size (); }
+
+  bool is_empty () const { return size () == 0; }
+
+  std::vector<SubstitutionArg> &get_mappings () { return mappings; }
+
+  const std::vector<SubstitutionArg> &get_mappings () const { return mappings; }
+
+  std::string as_string () const
+  {
+    std::string buffer;
+    for (auto &mapping : mappings)
+      {
+	buffer += mapping.as_string () + ", ";
+      }
+    return "<" + buffer + ">";
+  }
+
+  void on_param_subst (const ParamType &p, const SubstitutionArg &a) const
+  {
+    if (param_subst_cb == nullptr)
+      return;
+
+    param_subst_cb (p, a);
+  }
+
+  ParamSubstCb get_subst_cb () const { return param_subst_cb; }
+
+  bool trait_item_mode () const { return trait_item_flag; }
+
+private:
+  std::vector<SubstitutionArg> mappings;
+  Location locus;
+  ParamSubstCb param_subst_cb;
+  bool trait_item_flag;
+};
+
+class SubstitutionRef
+{
+public:
+  SubstitutionRef (std::vector<SubstitutionParamMapping> substitutions,
+		   SubstitutionArgumentMappings arguments)
+    : substitutions (substitutions), used_arguments (arguments)
+  {}
+
+  bool has_substitutions () const { return substitutions.size () > 0; }
+
+  std::string subst_as_string () const
+  {
+    std::string buffer;
+    for (size_t i = 0; i < substitutions.size (); i++)
+      {
+	const SubstitutionParamMapping &sub = substitutions.at (i);
+	buffer += sub.as_string ();
+
+	if ((i + 1) < substitutions.size ())
+	  buffer += ", ";
+      }
+
+    return buffer.empty () ? "" : "<" + buffer + ">";
+  }
+
+  size_t get_num_substitutions () const { return substitutions.size (); }
+
+  std::vector<SubstitutionParamMapping> &get_substs () { return substitutions; }
+
+  const std::vector<SubstitutionParamMapping> &get_substs () const
+  {
+    return substitutions;
+  }
+
+  std::vector<SubstitutionParamMapping> clone_substs () const
+  {
+    std::vector<SubstitutionParamMapping> clone;
+
+    for (auto &sub : substitutions)
+      clone.push_back (sub.clone ());
+
+    return clone;
+  }
+
+  void override_context ()
+  {
+    for (auto &sub : substitutions)
+      {
+	sub.override_context ();
+      }
+  }
+
+  bool needs_substitution () const
+  {
+    for (auto &sub : substitutions)
+      {
+	if (sub.need_substitution ())
+	  return true;
+      }
+    return false;
+  }
+
+  bool was_substituted () const { return !needs_substitution (); }
+
+  SubstitutionArgumentMappings get_substitution_arguments () const
+  {
+    return used_arguments;
+  }
+
+  // this is the count of type params that are not substituted fuly
+  size_t num_required_substitutions () const
+  {
+    size_t n = 0;
+    for (auto &p : substitutions)
+      {
+	if (p.needs_substitution ())
+	  n++;
+      }
+    return n;
+  }
+
+  // this is the count of type params that need substituted taking into account
+  // possible defaults
+  size_t min_required_substitutions () const
+  {
+    size_t n = 0;
+    for (auto &p : substitutions)
+      {
+	if (p.needs_substitution () && !p.param_has_default_ty ())
+	  n++;
+      }
+    return n;
+  }
+
+  // We are trying to subst <i32, f32> into Struct Foo<X,Y> {}
+  // in the case of Foo<i32,f32>{...}
+  //
+  // the substitions we have here define X,Y but the arguments have no bindings
+  // so its a matter of ordering
+  SubstitutionArgumentMappings
+  get_mappings_from_generic_args (HIR::GenericArgs &args);
+
+  // Recursive substitutions
+  // Foo <A,B> { a:A, b: B}; Bar <X,Y,Z>{a:X, b: Foo<Y,Z>}
+  //
+  // we have bindings for X Y Z and need to propagate the binding Y,Z into Foo
+  // Which binds to A,B
+  SubstitutionArgumentMappings
+  adjust_mappings_for_this (SubstitutionArgumentMappings &mappings);
+
+  // Are the mappings here actually bound to this type. For example imagine the
+  // case:
+  //
+  // struct Foo<T>(T);
+  // impl<T> Foo<T> {
+  //   fn test(self) { ... }
+  // }
+  //
+  // In this case we have a generic ADT of Foo and an impl block of a generic T
+  // on Foo for the Self type. When we it comes to path resolution we can have:
+  //
+  // Foo::<i32>::test()
+  //
+  // This means the first segment of Foo::<i32> returns the ADT Foo<i32> not the
+  // Self ADT bound to the T from the impl block. This means when it comes to
+  // the next segment of test which resolves to the function we need to check
+  // wether the arguments in the struct definition of foo can be bound here
+  // before substituting the previous segments type here. This functions acts as
+  // a guard for the solve_mappings_from_receiver_for_self to handle the case
+  // where arguments are not bound. This is important for this next case:
+  //
+  // struct Baz<A, B>(A, B);
+  // impl Baz<i32, f32> {
+  //   fn test<X>(a: X) -> X {
+  //       a
+  //   }
+  // }
+  //
+  // In this case Baz has been already substituted for the impl's Self to become
+  // ADT<i32, f32> so that the function test only has 1 generic argument of X.
+  // The path for this will be:
+  //
+  // Baz::test::<_>(123)
+  //
+  // So the first segment here will be Baz<_, _> to try and infer the arguments
+  // which will be taken from the impl's Self type in this case since it is
+  // already substituted and like the previous case the check to see if we need
+  // to inherit the previous segments generic arguments takes place but the
+  // generic arguments are not bound to this type as they have already been
+  // substituted.
+  //
+  // Its important to remember from the first example the FnType actually looks
+  // like:
+  //
+  // fn <T>test(self :Foo<T>(T))
+  //
+  // As the generic parameters are "bound" to each of the items in the impl
+  // block. So this check is about wether the arguments we have here can
+  // actually be bound to this type.
+  bool are_mappings_bound (SubstitutionArgumentMappings &mappings);
+
+  // struct Foo<A, B>(A, B);
+  //
+  // impl<T> Foo<T, f32>;
+  //     -> fn test<X>(self, a: X) -> X
+  //
+  // We might invoke this via:
+  //
+  // a = Foo(123, 456f32);
+  // b = a.test::<bool>(false);
+  //
+  // we need to figure out relevant generic arguemts for self to apply to the
+  // fntype
+  SubstitutionArgumentMappings solve_mappings_from_receiver_for_self (
+    SubstitutionArgumentMappings &mappings) const;
+
+  // TODO comment
+  SubstitutionArgumentMappings
+  solve_missing_mappings_from_this (SubstitutionRef &ref, SubstitutionRef &to);
+
+  // TODO comment
+  BaseType *infer_substitions (Location locus)
+  {
+    std::vector<SubstitutionArg> args;
+    std::map<std::string, BaseType *> argument_mappings;
+    for (auto &p : get_substs ())
+      {
+	if (p.needs_substitution ())
+	  {
+	    const std::string &symbol = p.get_param_ty ()->get_symbol ();
+	    auto it = argument_mappings.find (symbol);
+	    if (it == argument_mappings.end ())
+	      {
+		TyVar infer_var = TyVar::get_implicit_infer_var (locus);
+		args.push_back (SubstitutionArg (&p, infer_var.get_tyty ()));
+		argument_mappings[symbol] = infer_var.get_tyty ();
+	      }
+	    else
+	      {
+		args.push_back (SubstitutionArg (&p, it->second));
+	      }
+	  }
+	else
+	  {
+	    args.push_back (
+	      SubstitutionArg (&p, p.get_param_ty ()->resolve ()));
+	  }
+      }
+
+    SubstitutionArgumentMappings infer_arguments (std::move (args), locus);
+    return handle_substitions (std::move (infer_arguments));
+  }
+
+  // TODO comment
+  bool monomorphize ();
+
+  // TODO comment
+  virtual BaseType *handle_substitions (SubstitutionArgumentMappings mappings)
+    = 0;
+
+  SubstitutionArgumentMappings get_used_arguments () const
+  {
+    return used_arguments;
+  }
+
+protected:
+  std::vector<SubstitutionParamMapping> substitutions;
+  SubstitutionArgumentMappings used_arguments;
+};
+
+class TypeBoundPredicate : public SubstitutionRef
+{
+public:
+  TypeBoundPredicate (const Resolver::TraitReference &trait_reference,
+		      Location locus);
+
+  TypeBoundPredicate (DefId reference,
+		      std::vector<SubstitutionParamMapping> substitutions,
+		      Location locus);
+
+  TypeBoundPredicate (const TypeBoundPredicate &other);
+
+  TypeBoundPredicate &operator= (const TypeBoundPredicate &other);
+
+  static TypeBoundPredicate error ();
+
+  std::string as_string () const;
+
+  std::string as_name () const;
+
+  const Resolver::TraitReference *get () const;
+
+  Location get_locus () const { return locus; }
+
+  std::string get_name () const;
+
+  // check that this predicate is object-safe see:
+  // https://doc.rust-lang.org/reference/items/traits.html#object-safety
+  bool is_object_safe (bool emit_error, Location locus) const;
+
+  void apply_generic_arguments (HIR::GenericArgs *generic_args);
+
+  bool contains_item (const std::string &search) const;
+
+  TypeBoundPredicateItem
+  lookup_associated_item (const std::string &search) const;
+
+  TypeBoundPredicateItem
+  lookup_associated_item (const Resolver::TraitItemReference *ref) const;
+
+  // WARNING THIS WILL ALWAYS RETURN NULLPTR
+  BaseType *
+  handle_substitions (SubstitutionArgumentMappings mappings) override final;
+
+  bool is_error () const;
+
+  bool requires_generic_args () const;
+
+private:
+  DefId reference;
+  Location locus;
+  bool error_flag;
+};
+
+// https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.VariantDef.html
+class VariantDef
+{
+public:
+  enum VariantType
+  {
+    NUM,
+    TUPLE,
+    STRUCT
+  };
+
+  static std::string variant_type_string (VariantType type)
+  {
+    switch (type)
+      {
+      case NUM:
+	return "enumeral";
+      case TUPLE:
+	return "tuple";
+      case STRUCT:
+	return "struct";
+      }
+    gcc_unreachable ();
+    return "";
+  }
+
+  VariantDef (HirId id, std::string identifier, RustIdent ident,
+	      HIR::Expr *discriminant)
+    : id (id), identifier (identifier), ident (ident),
+      discriminant (discriminant)
+
+  {
+    type = VariantType::NUM;
+    fields = {};
+  }
+
+  VariantDef (HirId id, std::string identifier, RustIdent ident,
+	      VariantType type, HIR::Expr *discriminant,
+	      std::vector<StructFieldType *> fields)
+    : id (id), identifier (identifier), ident (ident), type (type),
+      discriminant (discriminant), fields (fields)
+  {
+    rust_assert (
+      (type == VariantType::NUM && fields.empty ())
+      || (type == VariantType::TUPLE || type == VariantType::STRUCT));
+  }
+
+  VariantDef (const VariantDef &other)
+    : id (other.id), identifier (other.identifier), ident (other.ident),
+      type (other.type), discriminant (other.discriminant),
+      fields (other.fields)
+  {}
+
+  VariantDef &operator= (const VariantDef &other)
+  {
+    id = other.id;
+    identifier = other.identifier;
+    type = other.type;
+    discriminant = other.discriminant;
+    fields = other.fields;
+    ident = other.ident;
+
+    return *this;
+  }
+
+  static VariantDef &get_error_node ()
+  {
+    static VariantDef node
+      = VariantDef (UNKNOWN_HIRID, "",
+		    {Resolver::CanonicalPath::create_empty (),
+		     Linemap::unknown_location ()},
+		    nullptr);
+
+    return node;
+  }
+
+  bool is_error () const { return get_id () == UNKNOWN_HIRID; }
+
+  HirId get_id () const { return id; }
+
+  VariantType get_variant_type () const { return type; }
+  bool is_data_variant () const { return type != VariantType::NUM; }
+  bool is_dataless_variant () const { return type == VariantType::NUM; }
+
+  std::string get_identifier () const { return identifier; }
+
+  size_t num_fields () const { return fields.size (); }
+  StructFieldType *get_field_at_index (size_t index)
+  {
+    rust_assert (index < fields.size ());
+    return fields.at (index);
+  }
+
+  std::vector<StructFieldType *> &get_fields ()
+  {
+    rust_assert (type != NUM);
+    return fields;
+  }
+
+  bool lookup_field (const std::string &lookup, StructFieldType **field_lookup,
+		     size_t *index) const
+  {
+    size_t i = 0;
+    for (auto &field : fields)
+      {
+	if (field->get_name ().compare (lookup) == 0)
+	  {
+	    if (index != nullptr)
+	      *index = i;
+
+	    if (field_lookup != nullptr)
+	      *field_lookup = field;
+
+	    return true;
+	  }
+	i++;
+      }
+    return false;
+  }
+
+  HIR::Expr *get_discriminant () const
+  {
+    rust_assert (discriminant != nullptr);
+    return discriminant;
+  }
+
+  std::string as_string () const
+  {
+    if (type == VariantType::NUM)
+      return identifier + " = " + discriminant->as_string ();
+
+    std::string buffer;
+    for (size_t i = 0; i < fields.size (); ++i)
+      {
+	buffer += fields.at (i)->as_string ();
+	if ((i + 1) < fields.size ())
+	  buffer += ", ";
+      }
+
+    if (type == VariantType::TUPLE)
+      return identifier + " (" + buffer + ")";
+    else
+      return identifier + " {" + buffer + "}";
+  }
+
+  bool is_equal (const VariantDef &other) const
+  {
+    if (type != other.type)
+      return false;
+
+    if (identifier.compare (other.identifier) != 0)
+      return false;
+
+    if (discriminant != other.discriminant)
+      return false;
+
+    if (fields.size () != other.fields.size ())
+      return false;
+
+    for (size_t i = 0; i < fields.size (); i++)
+      {
+	if (!fields.at (i)->is_equal (*other.fields.at (i)))
+	  return false;
+      }
+
+    return true;
+  }
+
+  VariantDef *clone () const
+  {
+    std::vector<StructFieldType *> cloned_fields;
+    for (auto &f : fields)
+      cloned_fields.push_back ((StructFieldType *) f->clone ());
+
+    return new VariantDef (id, identifier, ident, type, discriminant,
+			   cloned_fields);
+  }
+
+  VariantDef *monomorphized_clone () const
+  {
+    std::vector<StructFieldType *> cloned_fields;
+    for (auto &f : fields)
+      cloned_fields.push_back ((StructFieldType *) f->monomorphized_clone ());
+
+    return new VariantDef (id, identifier, ident, type, discriminant,
+			   cloned_fields);
+  }
+
+  const RustIdent &get_ident () const { return ident; }
+
+private:
+  HirId id;
+  std::string identifier;
+  RustIdent ident;
+  VariantType type;
+  // can either be a structure or a discriminant value
+  HIR::Expr *discriminant;
+  std::vector<StructFieldType *> fields;
+};
+
+class ADTType : public BaseType, public SubstitutionRef
+{
+public:
+  enum ADTKind
+  {
+    STRUCT_STRUCT,
+    TUPLE_STRUCT,
+    UNION,
+    ENUM
+  };
+
+  // Representation options, specified via attributes e.g. #[repr(packed)]
+  struct ReprOptions
+  {
+    // bool is_c;
+    // bool is_transparent;
+    //...
+
+    // For align and pack: 0 = unspecified. Nonzero = byte alignment.
+    // It is an error for both to be nonzero, this should be caught when
+    // parsing the #[repr] attribute.
+    unsigned char align = 0;
+    unsigned char pack = 0;
+  };
+
+  ADTType (HirId ref, std::string identifier, RustIdent ident, ADTKind adt_kind,
+	   std::vector<VariantDef *> variants,
+	   std::vector<SubstitutionParamMapping> subst_refs,
+	   SubstitutionArgumentMappings generic_arguments
+	   = SubstitutionArgumentMappings::error (),
+	   std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::ADT, ident, refs),
+      SubstitutionRef (std::move (subst_refs), std::move (generic_arguments)),
+      identifier (identifier), variants (variants), adt_kind (adt_kind)
+  {}
+
+  ADTType (HirId ref, HirId ty_ref, std::string identifier, RustIdent ident,
+	   ADTKind adt_kind, std::vector<VariantDef *> variants,
+	   std::vector<SubstitutionParamMapping> subst_refs,
+	   SubstitutionArgumentMappings generic_arguments
+	   = SubstitutionArgumentMappings::error (),
+	   std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::ADT, ident, refs),
+      SubstitutionRef (std::move (subst_refs), std::move (generic_arguments)),
+      identifier (identifier), variants (variants), adt_kind (adt_kind)
+  {}
+
+  ADTType (HirId ref, HirId ty_ref, std::string identifier, RustIdent ident,
+	   ADTKind adt_kind, std::vector<VariantDef *> variants,
+	   std::vector<SubstitutionParamMapping> subst_refs, ReprOptions repr,
+	   SubstitutionArgumentMappings generic_arguments
+	   = SubstitutionArgumentMappings::error (),
+	   std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::ADT, ident, refs),
+      SubstitutionRef (std::move (subst_refs), std::move (generic_arguments)),
+      identifier (identifier), variants (variants), adt_kind (adt_kind),
+      repr (repr)
+  {}
+
+  ADTKind get_adt_kind () const { return adt_kind; }
+  ReprOptions get_repr_options () const { return repr; }
+
+  bool is_struct_struct () const { return adt_kind == STRUCT_STRUCT; }
+  bool is_tuple_struct () const { return adt_kind == TUPLE_STRUCT; }
+  bool is_union () const { return adt_kind == UNION; }
+  bool is_enum () const { return adt_kind == ENUM; }
+
+  bool is_unit () const override
+  {
+    if (number_of_variants () == 0)
+      return true;
+
+    if (number_of_variants () == 1)
+      return variants.at (0)->num_fields () == 0;
+
+    return false;
+  }
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  bool is_equal (const BaseType &other) const override;
+
+  std::string get_identifier () const { return identifier; }
+
+  std::string get_name () const override final
+  {
+    return identifier + subst_as_string ();
+  }
+
+  bool is_concrete () const override final
+  {
+    for (auto &variant : variants)
+      {
+	for (auto &field : variant->get_fields ())
+	  {
+	    if (!field->is_concrete ())
+	      return false;
+	  }
+      }
+    return true;
+  }
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+
+  bool needs_generic_substitutions () const override final
+  {
+    return needs_substitution ();
+  }
+
+  bool supports_substitutions () const override final { return true; }
+
+  bool has_subsititions_defined () const override final
+  {
+    return has_substitutions ();
+  }
+
+  size_t number_of_variants () const { return variants.size (); }
+
+  std::vector<VariantDef *> &get_variants () { return variants; }
+  const std::vector<VariantDef *> &get_variants () const { return variants; }
+
+  bool lookup_variant (const std::string &lookup,
+		       VariantDef **found_variant) const
+  {
+    for (auto &variant : variants)
+      {
+	if (variant->get_identifier ().compare (lookup) == 0)
+	  {
+	    *found_variant = variant;
+	    return true;
+	  }
+      }
+    return false;
+  }
+
+  bool lookup_variant_by_id (HirId id, VariantDef **found_variant,
+			     int *index = nullptr) const
+  {
+    int i = 0;
+    for (auto &variant : variants)
+      {
+	if (variant->get_id () == id)
+	  {
+	    if (index != nullptr)
+	      *index = i;
+
+	    *found_variant = variant;
+	    return true;
+	  }
+	i++;
+      }
+    return false;
+  }
+
+  ADTType *
+  handle_substitions (SubstitutionArgumentMappings mappings) override final;
+
+private:
+  std::string identifier;
+  std::vector<VariantDef *> variants;
+  ADTType::ADTKind adt_kind;
+  ReprOptions repr;
+};
+
+class FnType : public BaseType, public SubstitutionRef
+{
+public:
+  static const uint8_t FNTYPE_DEFAULT_FLAGS = 0x00;
+  static const uint8_t FNTYPE_IS_METHOD_FLAG = 0x01;
+  static const uint8_t FNTYPE_IS_EXTERN_FLAG = 0x02;
+  static const uint8_t FNTYPE_IS_VARADIC_FLAG = 0X04;
+
+  FnType (HirId ref, DefId id, std::string identifier, RustIdent ident,
+	  uint8_t flags, ABI abi,
+	  std::vector<std::pair<HIR::Pattern *, BaseType *>> params,
+	  BaseType *type, std::vector<SubstitutionParamMapping> subst_refs,
+	  std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::FNDEF, ident, refs),
+      SubstitutionRef (std::move (subst_refs),
+		       SubstitutionArgumentMappings::error ()),
+      params (std::move (params)), type (type), flags (flags),
+      identifier (identifier), id (id), abi (abi)
+  {
+    LocalDefId local_def_id = id.localDefId;
+    rust_assert (local_def_id != UNKNOWN_LOCAL_DEFID);
+  }
+
+  FnType (HirId ref, HirId ty_ref, DefId id, std::string identifier,
+	  RustIdent ident, uint8_t flags, ABI abi,
+	  std::vector<std::pair<HIR::Pattern *, BaseType *>> params,
+	  BaseType *type, std::vector<SubstitutionParamMapping> subst_refs,
+	  std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::FNDEF, ident, refs),
+      SubstitutionRef (std::move (subst_refs),
+		       SubstitutionArgumentMappings::error ()),
+      params (params), type (type), flags (flags), identifier (identifier),
+      id (id), abi (abi)
+  {
+    LocalDefId local_def_id = id.localDefId;
+    rust_assert (local_def_id != UNKNOWN_LOCAL_DEFID);
+  }
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  std::string get_name () const override final { return as_string (); }
+
+  std::string get_identifier () const { return identifier; }
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  bool is_equal (const BaseType &other) const override;
+
+  size_t num_params () const { return params.size (); }
+
+  bool is_method () const
+  {
+    if (num_params () == 0)
+      return false;
+
+    return (flags & FNTYPE_IS_METHOD_FLAG) != 0;
+  }
+
+  bool is_extern () const { return (flags & FNTYPE_IS_EXTERN_FLAG) != 0; }
+
+  bool is_varadic () const { return (flags & FNTYPE_IS_VARADIC_FLAG) != 0; }
+
+  DefId get_id () const { return id; }
+
+  // get the Self type for the method
+  BaseType *get_self_type () const
+  {
+    rust_assert (is_method ());
+    return param_at (0).second;
+  }
+
+  bool is_concrete () const override final
+  {
+    for (const auto &param : params)
+      {
+	const BaseType *p = param.second;
+	if (!p->is_concrete ())
+	  return false;
+      }
+    return get_return_type ()->is_concrete ();
+  }
+
+  std::vector<std::pair<HIR::Pattern *, BaseType *>> &get_params ()
+  {
+    return params;
+  }
+
+  const std::vector<std::pair<HIR::Pattern *, BaseType *>> &get_params () const
+  {
+    return params;
+  }
+
+  std::pair<HIR::Pattern *, BaseType *> &param_at (size_t idx)
+  {
+    return params.at (idx);
+  }
+
+  const std::pair<HIR::Pattern *, BaseType *> &param_at (size_t idx) const
+  {
+    return params.at (idx);
+  }
+
+  BaseType *get_return_type () const { return type; }
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+
+  bool needs_generic_substitutions () const override final
+  {
+    return needs_substitution ();
+  }
+
+  bool supports_substitutions () const override final { return true; }
+
+  bool has_subsititions_defined () const override final
+  {
+    return has_substitutions ();
+  }
+
+  FnType *
+  handle_substitions (SubstitutionArgumentMappings mappings) override final;
+
+  ABI get_abi () const { return abi; }
+
+private:
+  std::vector<std::pair<HIR::Pattern *, BaseType *>> params;
+  BaseType *type;
+  uint8_t flags;
+  std::string identifier;
+  DefId id;
+  ABI abi;
+};
+
+class FnPtr : public BaseType
+{
+public:
+  FnPtr (HirId ref, Location locus, std::vector<TyVar> params,
+	 TyVar result_type, std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::FNPTR,
+		{Resolver::CanonicalPath::create_empty (), locus}, refs),
+      params (std::move (params)), result_type (result_type)
+  {}
+
+  FnPtr (HirId ref, HirId ty_ref, Location locus, std::vector<TyVar> params,
+	 TyVar result_type, std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::FNPTR,
+		{Resolver::CanonicalPath::create_empty (), locus}, refs),
+      params (params), result_type (result_type)
+  {}
+
+  std::string get_name () const override final { return as_string (); }
+
+  BaseType *get_return_type () const { return result_type.get_tyty (); }
+
+  size_t num_params () const { return params.size (); }
+
+  BaseType *param_at (size_t idx) const { return params.at (idx).get_tyty (); }
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  bool is_equal (const BaseType &other) const override;
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+
+  void iterate_params (std::function<bool (BaseType *)> cb) const
+  {
+    for (auto &p : params)
+      {
+	if (!cb (p.get_tyty ()))
+	  return;
+      }
+  }
+
+  std::vector<TyVar> &get_params () { return params; }
+  const std::vector<TyVar> &get_params () const { return params; }
+
+  bool is_concrete () const override final
+  {
+    for (auto &p : params)
+      {
+	if (!p.get_tyty ()->is_concrete ())
+	  return false;
+      }
+    return result_type.get_tyty ()->is_concrete ();
+  }
+
+private:
+  std::vector<TyVar> params;
+  TyVar result_type;
+};
+
+class ClosureType : public BaseType, public SubstitutionRef
+{
+public:
+  ClosureType (HirId ref, DefId id, RustIdent ident,
+	       std::vector<TyVar> parameter_types, TyVar result_type,
+	       std::vector<SubstitutionParamMapping> subst_refs,
+	       std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::CLOSURE, ident, refs),
+      SubstitutionRef (std::move (subst_refs),
+		       SubstitutionArgumentMappings::error ()),
+      parameter_types (std::move (parameter_types)),
+      result_type (std::move (result_type)), id (id)
+  {
+    LocalDefId local_def_id = id.localDefId;
+    rust_assert (local_def_id != UNKNOWN_LOCAL_DEFID);
+  }
+
+  ClosureType (HirId ref, HirId ty_ref, RustIdent ident, DefId id,
+	       std::vector<TyVar> parameter_types, TyVar result_type,
+	       std::vector<SubstitutionParamMapping> subst_refs,
+	       std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::CLOSURE, ident, refs),
+      SubstitutionRef (std::move (subst_refs),
+		       SubstitutionArgumentMappings::error ()),
+      parameter_types (std::move (parameter_types)),
+      result_type (std::move (result_type)), id (id)
+  {
+    LocalDefId local_def_id = id.localDefId;
+    rust_assert (local_def_id != UNKNOWN_LOCAL_DEFID);
+  }
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+  std::string get_name () const override final { return as_string (); }
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  bool is_equal (const BaseType &other) const override;
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+
+  bool is_concrete () const override final
+  {
+    for (auto &param : parameter_types)
+      {
+	auto p = param.get_tyty ();
+	if (!p->is_concrete ())
+	  return false;
+      }
+    return result_type.get_tyty ()->is_concrete ();
+  }
+
+  bool needs_generic_substitutions () const override final
+  {
+    return needs_substitution ();
+  }
+
+  bool supports_substitutions () const override final { return true; }
+
+  bool has_subsititions_defined () const override final
+  {
+    return has_substitutions ();
+  }
+
+  ClosureType *
+  handle_substitions (SubstitutionArgumentMappings mappings) override final;
+
+private:
+  std::vector<TyVar> parameter_types;
+  TyVar result_type;
+  DefId id;
+};
+
+class ArrayType : public BaseType
+{
+public:
+  ArrayType (HirId ref, Location locus, HIR::Expr &capacity_expr, TyVar base,
+	     std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::ARRAY,
+		{Resolver::CanonicalPath::create_empty (), locus}, refs),
+      element_type (base), capacity_expr (capacity_expr)
+  {}
+
+  ArrayType (HirId ref, HirId ty_ref, Location locus, HIR::Expr &capacity_expr,
+	     TyVar base, std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::ARRAY,
+		{Resolver::CanonicalPath::create_empty (), locus}, refs),
+      element_type (base), capacity_expr (capacity_expr)
+  {}
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  std::string get_name () const override final { return as_string (); }
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  bool is_equal (const BaseType &other) const override;
+
+  BaseType *get_element_type () const;
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+
+  bool is_concrete () const final override
+  {
+    return get_element_type ()->is_concrete ();
+  }
+
+  HIR::Expr &get_capacity_expr () const { return capacity_expr; }
+
+  ArrayType *handle_substitions (SubstitutionArgumentMappings mappings);
+
+private:
+  TyVar element_type;
+  HIR::Expr &capacity_expr;
+};
+
+class SliceType : public BaseType
+{
+public:
+  SliceType (HirId ref, Location locus, TyVar base,
+	     std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::SLICE,
+		{Resolver::CanonicalPath::create_empty (), locus}, refs),
+      element_type (base)
+  {}
+
+  SliceType (HirId ref, HirId ty_ref, Location locus, TyVar base,
+	     std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::SLICE,
+		{Resolver::CanonicalPath::create_empty (), locus}, refs),
+      element_type (base)
+  {}
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  std::string get_name () const override final { return as_string (); }
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  bool is_equal (const BaseType &other) const override;
+
+  BaseType *get_element_type () const;
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+
+  bool is_concrete () const final override
+  {
+    return get_element_type ()->is_concrete ();
+  }
+
+  SliceType *handle_substitions (SubstitutionArgumentMappings mappings);
+
+private:
+  TyVar element_type;
+};
+
+class BoolType : public BaseType
+{
+public:
+  BoolType (HirId ref, std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::BOOL,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs)
+  {}
+
+  BoolType (HirId ref, HirId ty_ref, std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::BOOL,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs)
+  {}
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  std::string get_name () const override final { return as_string (); }
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+  bool is_concrete () const override final { return true; }
+};
+
+class IntType : public BaseType
+{
+public:
+  enum IntKind
+  {
+    I8,
+    I16,
+    I32,
+    I64,
+    I128
+  };
+
+  IntType (HirId ref, IntKind kind, std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::INT,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs),
+      int_kind (kind)
+  {}
+
+  IntType (HirId ref, HirId ty_ref, IntKind kind,
+	   std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::INT,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs),
+      int_kind (kind)
+  {}
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  std::string get_name () const override final { return as_string (); }
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  IntKind get_int_kind () const { return int_kind; }
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+
+  bool is_equal (const BaseType &other) const override;
+  bool is_concrete () const override final { return true; }
+
+private:
+  IntKind int_kind;
+};
+
+class UintType : public BaseType
+{
+public:
+  enum UintKind
+  {
+    U8,
+    U16,
+    U32,
+    U64,
+    U128
+  };
+
+  UintType (HirId ref, UintKind kind, std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::UINT,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs),
+      uint_kind (kind)
+  {}
+
+  UintType (HirId ref, HirId ty_ref, UintKind kind,
+	    std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::UINT,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs),
+      uint_kind (kind)
+  {}
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  std::string get_name () const override final { return as_string (); }
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  UintKind get_uint_kind () const { return uint_kind; }
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+
+  bool is_equal (const BaseType &other) const override;
+  bool is_concrete () const override final { return true; }
+
+private:
+  UintKind uint_kind;
+};
+
+class FloatType : public BaseType
+{
+public:
+  enum FloatKind
+  {
+    F32,
+    F64
+  };
+
+  FloatType (HirId ref, FloatKind kind,
+	     std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::FLOAT,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs),
+      float_kind (kind)
+  {}
+
+  FloatType (HirId ref, HirId ty_ref, FloatKind kind,
+	     std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::FLOAT,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs),
+      float_kind (kind)
+  {}
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  std::string get_name () const override final { return as_string (); }
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  FloatKind get_float_kind () const { return float_kind; }
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+
+  bool is_equal (const BaseType &other) const override;
+  bool is_concrete () const override final { return true; }
+
+private:
+  FloatKind float_kind;
+};
+
+class USizeType : public BaseType
+{
+public:
+  USizeType (HirId ref, std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::USIZE,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs)
+  {}
+
+  USizeType (HirId ref, HirId ty_ref, std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::USIZE,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs)
+  {}
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  std::string get_name () const override final { return as_string (); }
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+  bool is_concrete () const override final { return true; }
+};
+
+class ISizeType : public BaseType
+{
+public:
+  ISizeType (HirId ref, std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::ISIZE,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs)
+  {}
+
+  ISizeType (HirId ref, HirId ty_ref, std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::ISIZE,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs)
+  {}
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  std::string get_name () const override final { return as_string (); }
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+  bool is_concrete () const override final { return true; }
+};
+
+class CharType : public BaseType
+{
+public:
+  CharType (HirId ref, std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::CHAR,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs)
+  {}
+
+  CharType (HirId ref, HirId ty_ref, std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::CHAR,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs)
+  {}
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  std::string get_name () const override final { return as_string (); }
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+  bool is_concrete () const override final { return true; }
+};
+
+class StrType : public BaseType
+{
+public:
+  StrType (HirId ref, std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::STR,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs)
+  {}
+
+  StrType (HirId ref, HirId ty_ref, std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::STR,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs)
+  {}
+
+  std::string get_name () const override final { return as_string (); }
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  bool is_equal (const BaseType &other) const override;
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+  bool is_concrete () const override final { return true; }
+};
+
+class ReferenceType : public BaseType
+{
+public:
+  ReferenceType (HirId ref, TyVar base, Mutability mut,
+		 std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::REF,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs),
+      base (base), mut (mut)
+  {}
+
+  ReferenceType (HirId ref, HirId ty_ref, TyVar base, Mutability mut,
+		 std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::REF,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs),
+      base (base), mut (mut)
+  {}
+
+  BaseType *get_base () const;
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  std::string get_name () const override final
+  {
+    return "&" + get_base ()->get_name ();
+  }
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  bool is_equal (const BaseType &other) const override;
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+
+  bool is_concrete () const override final
+  {
+    return get_base ()->is_concrete ();
+  }
+
+  ReferenceType *handle_substitions (SubstitutionArgumentMappings mappings);
+
+  Mutability mutability () const { return mut; }
+
+  bool is_mutable () const { return mut == Mutability::Mut; }
+
+  bool is_dyn_object () const
+  {
+    return is_dyn_slice_type () || is_dyn_str_type ();
+  }
+
+  bool is_dyn_slice_type (const TyTy::SliceType **slice = nullptr) const
+  {
+    const TyTy::BaseType *element = get_base ()->destructure ();
+    if (element->get_kind () != TyTy::TypeKind::SLICE)
+      return false;
+    if (slice == nullptr)
+      return true;
+
+    *slice = static_cast<const TyTy::SliceType *> (element);
+    return true;
+  }
+
+  bool is_dyn_str_type (const TyTy::StrType **str = nullptr) const
+  {
+    const TyTy::BaseType *element = get_base ()->destructure ();
+    if (element->get_kind () != TyTy::TypeKind::STR)
+      return false;
+    if (str == nullptr)
+      return true;
+
+    *str = static_cast<const TyTy::StrType *> (element);
+    return true;
+  }
+
+private:
+  TyVar base;
+  Mutability mut;
+};
+
+class PointerType : public BaseType
+{
+public:
+  PointerType (HirId ref, TyVar base, Mutability mut,
+	       std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::POINTER,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs),
+      base (base), mut (mut)
+  {}
+
+  PointerType (HirId ref, HirId ty_ref, TyVar base, Mutability mut,
+	       std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::POINTER,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs),
+      base (base), mut (mut)
+  {}
+
+  BaseType *get_base () const;
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  std::string get_name () const override final
+  {
+    return "*" + get_base ()->get_name ();
+  }
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  bool is_equal (const BaseType &other) const override;
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+
+  bool is_concrete () const override final
+  {
+    return get_base ()->is_concrete ();
+  }
+
+  PointerType *handle_substitions (SubstitutionArgumentMappings mappings);
+
+  Mutability mutability () const { return mut; }
+
+  bool is_mutable () const { return mut == Mutability::Mut; }
+
+  bool is_const () const { return mut == Mutability::Imm; }
+
+  bool is_dyn_object () const
+  {
+    return is_dyn_slice_type () || is_dyn_str_type ();
+  }
+
+  bool is_dyn_slice_type (const TyTy::SliceType **slice = nullptr) const
+  {
+    const TyTy::BaseType *element = get_base ()->destructure ();
+    if (element->get_kind () != TyTy::TypeKind::SLICE)
+      return false;
+    if (slice == nullptr)
+      return true;
+
+    *slice = static_cast<const TyTy::SliceType *> (element);
+    return true;
+  }
+
+  bool is_dyn_str_type (const TyTy::StrType **str = nullptr) const
+  {
+    const TyTy::BaseType *element = get_base ()->destructure ();
+    if (element->get_kind () != TyTy::TypeKind::STR)
+      return false;
+    if (str == nullptr)
+      return true;
+
+    *str = static_cast<const TyTy::StrType *> (element);
+    return true;
+  }
+
+private:
+  TyVar base;
+  Mutability mut;
+};
+
+// https://doc.rust-lang.org/std/primitive.never.html
+//
+// Since the `!` type is really complicated and it is even still unstable
+// in rustc, only fairly limited support for this type is introduced here.
+// Unification between `!` and ANY other type (including `<T?>`) is simply
+// not allowed. If it is needed, it should be handled manually. For example,
+// unifying `!` with other types is very necessary when resolving types of
+// `if/else` expressions.
+//
+// See related discussion at https://github.com/Rust-GCC/gccrs/pull/364
+class NeverType : public BaseType
+{
+public:
+  NeverType (HirId ref, std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::NEVER,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs)
+  {}
+
+  NeverType (HirId ref, HirId ty_ref, std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::NEVER,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs)
+  {}
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+
+  std::string get_name () const override final { return as_string (); }
+
+  bool is_unit () const override { return true; }
+  bool is_concrete () const override final { return true; }
+};
+
+// used at the type in associated types in traits
+// see: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
+class PlaceholderType : public BaseType
+{
+public:
+  PlaceholderType (std::string symbol, HirId ref,
+		   std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::PLACEHOLDER,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs),
+      symbol (symbol)
+  {}
+
+  PlaceholderType (std::string symbol, HirId ref, HirId ty_ref,
+		   std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::PLACEHOLDER,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs),
+      symbol (symbol)
+  {}
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+
+  std::string get_name () const override final { return as_string (); }
+
+  bool is_unit () const override
+  {
+    rust_assert (can_resolve ());
+    return resolve ()->is_unit ();
+  }
+
+  std::string get_symbol () const { return symbol; }
+
+  void set_associated_type (HirId ref);
+
+  void clear_associated_type ();
+
+  bool can_resolve () const;
+
+  BaseType *resolve () const;
+
+  bool is_equal (const BaseType &other) const override;
+
+  bool is_concrete () const override final
+  {
+    if (!can_resolve ())
+      return true;
+
+    return resolve ()->is_concrete ();
+  }
+
+private:
+  std::string symbol;
+};
+
+class ProjectionType : public BaseType, public SubstitutionRef
+{
+public:
+  ProjectionType (HirId ref, BaseType *base,
+		  const Resolver::TraitReference *trait, DefId item,
+		  std::vector<SubstitutionParamMapping> subst_refs,
+		  SubstitutionArgumentMappings generic_arguments
+		  = SubstitutionArgumentMappings::error (),
+		  std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::PROJECTION,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs),
+      SubstitutionRef (std::move (subst_refs), std::move (generic_arguments)),
+      base (base), trait (trait), item (item)
+  {}
+
+  ProjectionType (HirId ref, HirId ty_ref, BaseType *base,
+		  const Resolver::TraitReference *trait, DefId item,
+		  std::vector<SubstitutionParamMapping> subst_refs,
+		  SubstitutionArgumentMappings generic_arguments
+		  = SubstitutionArgumentMappings::error (),
+		  std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::PROJECTION,
+		{Resolver::CanonicalPath::create_empty (),
+		 Linemap::predeclared_location ()},
+		refs),
+      SubstitutionRef (std::move (subst_refs), std::move (generic_arguments)),
+      base (base), trait (trait), item (item)
+  {}
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+
+  std::string get_name () const override final { return as_string (); }
+
+  bool is_unit () const override { return false; }
+
+  bool needs_generic_substitutions () const override final
+  {
+    return needs_substitution ();
+  }
+
+  bool supports_substitutions () const override final { return true; }
+
+  bool has_subsititions_defined () const override final
+  {
+    return has_substitutions ();
+  }
+
+  const BaseType *get () const { return base; }
+  BaseType *get () { return base; }
+
+  bool is_concrete () const override final { return base->is_concrete (); }
+
+  ProjectionType *
+  handle_substitions (SubstitutionArgumentMappings mappings) override final;
+
+private:
+  BaseType *base;
+  const Resolver::TraitReference *trait;
+  DefId item;
+};
+
+class DynamicObjectType : public BaseType
+{
+public:
+  DynamicObjectType (HirId ref, RustIdent ident,
+		     std::vector<TypeBoundPredicate> specified_bounds,
+		     std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ref, TypeKind::DYNAMIC, ident, specified_bounds, refs)
+  {}
+
+  DynamicObjectType (HirId ref, HirId ty_ref, RustIdent ident,
+		     std::vector<TypeBoundPredicate> specified_bounds,
+		     std::set<HirId> refs = std::set<HirId> ())
+    : BaseType (ref, ty_ref, TypeKind::DYNAMIC, ident, specified_bounds, refs)
+  {}
+
+  void accept_vis (TyVisitor &vis) override;
+  void accept_vis (TyConstVisitor &vis) const override;
+
+  std::string as_string () const override;
+
+  BaseType *unify (BaseType *other) override;
+  bool can_eq (const BaseType *other, bool emit_errors) const override final;
+
+  bool is_equal (const BaseType &other) const override;
+
+  BaseType *clone () const final override;
+  BaseType *monomorphized_clone () const final override;
+
+  std::string get_name () const override final;
+
+  bool is_concrete () const override final { return true; }
+
+  // this returns a flat list of items including super trait bounds
+  const std::vector<
+    std::pair<const Resolver::TraitItemReference *, const TypeBoundPredicate *>>
+  get_object_items () const;
+};
+
+} // namespace TyTy
+} // namespace Rust
+
+#endif // RUST_TYTY
-- 
2.25.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [PATCH Rust front-end v2 23/37] gccrs: Add unsafe checks for Rust
  2022-08-24 11:59 Rust frontend patches v2 herron.philip
                   ` (21 preceding siblings ...)
  2022-08-24 11:59 ` [PATCH Rust front-end v2 22/37] gccrs: Add type resolution and trait solving pass herron.philip
@ 2022-08-24 11:59 ` herron.philip
  2022-08-24 11:59 ` [PATCH Rust front-end v2 24/37] gccrs: Add const checker herron.philip
                   ` (14 subsequent siblings)
  37 siblings, 0 replies; 59+ messages in thread
From: herron.philip @ 2022-08-24 11:59 UTC (permalink / raw)
  To: gcc-patches; +Cc: gcc-rust, Arthur Cohen

From: Arthur Cohen <arthur.cohen@embecosm.com>

The UnsafeChecker visitor verifies that unsafe actions are only performed
in unsafe contexts. Otherwise, an error should be reported to the user and
the compilation pipeline should be halted. These contexts, which include
unsafe blocks or unsafe functions, are allowed to perform more actions
than regular safe Rust code. These actions currently include:

- Dereferencing raw pointers
- Calls to unsafe functions
- Use of inline assembly
- Use of mutable static
- Use of extern static
- Access to a union's field
- Call to functions with #[target(feature)] attribute
- Initializing type with rustc_layout_scalar_valid_range attribute
- Mutation of layout constrained field
- Borrow of layout constrained field
---
 gcc/rust/checks/errors/rust-unsafe-checker.cc | 963 ++++++++++++++++++
 gcc/rust/checks/errors/rust-unsafe-checker.h  | 191 ++++
 2 files changed, 1154 insertions(+)
 create mode 100644 gcc/rust/checks/errors/rust-unsafe-checker.cc
 create mode 100644 gcc/rust/checks/errors/rust-unsafe-checker.h

diff --git a/gcc/rust/checks/errors/rust-unsafe-checker.cc b/gcc/rust/checks/errors/rust-unsafe-checker.cc
new file mode 100644
index 00000000000..e3f32539562
--- /dev/null
+++ b/gcc/rust/checks/errors/rust-unsafe-checker.cc
@@ -0,0 +1,963 @@
+// 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/>.
+
+#include "rust-unsafe-checker.h"
+#include "rust-hir.h"
+#include "rust-hir-expr.h"
+#include "rust-hir-stmt.h"
+#include "rust-hir-item.h"
+
+namespace Rust {
+namespace HIR {
+
+UnsafeChecker::UnsafeChecker ()
+  : context (*Resolver::TypeCheckContext::get ()),
+    resolver (*Resolver::Resolver::get ()),
+    mappings (*Analysis::Mappings::get ())
+{}
+
+void
+UnsafeChecker::go (HIR::Crate &crate)
+{
+  for (auto &item : crate.items)
+    item->accept_vis (*this);
+}
+
+static void
+check_static_mut (HIR::Item *maybe_static, Location locus)
+{
+  if (maybe_static->get_hir_kind () == Node::BaseKind::VIS_ITEM)
+    {
+      auto item = static_cast<Item *> (maybe_static);
+      if (item->get_item_kind () == Item::ItemKind::Static)
+	{
+	  auto static_item = static_cast<StaticItem *> (item);
+	  if (static_item->is_mut ())
+	    rust_error_at (
+	      locus, "use of mutable static requires unsafe function or block");
+	}
+    }
+}
+
+static void
+check_extern_static (HIR::ExternalItem *maybe_static, Location locus)
+{
+  if (maybe_static->get_extern_kind () == ExternalItem::ExternKind::Static)
+    rust_error_at (locus,
+		   "use of extern static requires unsafe function or block");
+}
+
+void
+UnsafeChecker::check_use_of_static (HirId node_id, Location locus)
+{
+  if (unsafe_context.is_in_context ())
+    return;
+
+  auto maybe_static_mut = mappings.lookup_hir_item (node_id);
+
+  HirId extern_block;
+  auto maybe_extern_static
+    = mappings.lookup_hir_extern_item (node_id, &extern_block);
+
+  if (maybe_static_mut)
+    check_static_mut (maybe_static_mut, locus);
+
+  if (maybe_extern_static)
+    check_extern_static (static_cast<ExternalItem *> (maybe_extern_static),
+			 locus);
+}
+
+static void
+check_unsafe_call (HIR::Function *fn, Location locus, const std::string &kind)
+{
+  if (fn->get_qualifiers ().is_unsafe ())
+    rust_error_at (locus, "call to unsafe %s requires unsafe function or block",
+		   kind.c_str ());
+}
+
+static bool
+is_safe_intrinsic (const std::string &fn_name)
+{
+  static const std::unordered_set<std::string> safe_intrinsics = {
+    "abort",
+    "size_of",
+    "min_align_of",
+    "needs_drop",
+    "caller_location",
+    "add_with_overflow",
+    "sub_with_overflow",
+    "mul_with_overflow",
+    "wrapping_add",
+    "wrapping_sub",
+    "wrapping_mul",
+    "saturating_add",
+    "saturating_sub",
+    "rotate_left",
+    "rotate_right",
+    "ctpop",
+    "ctlz",
+    "cttz",
+    "bswap",
+    "bitreverse",
+    "discriminant_value",
+    "type_id",
+    "likely",
+    "unlikely",
+    "ptr_guaranteed_eq",
+    "ptr_guaranteed_ne",
+    "minnumf32",
+    "minnumf64",
+    "maxnumf32",
+    "rustc_peek",
+    "maxnumf64",
+    "type_name",
+    "forget",
+    "black_box",
+    "variant_count",
+  };
+
+  return safe_intrinsics.find (fn_name) != safe_intrinsics.end ();
+}
+
+static void
+check_extern_call (HIR::ExternalItem *maybe_fn, HIR::ExternBlock *parent_block,
+		   Location locus)
+{
+  // We have multiple operations to perform here
+  //     1. Is the item an actual function we're calling
+  //     2. Is the block it's defined in an FFI block or an `extern crate` block
+  //
+  // It is not unsafe to call into other crates, so items defined in an `extern
+  // crate` must be callable without being in an unsafe context. On the other
+  // hand, any function defined in a block with a specific ABI (even `extern
+  // "Rust"` blocks) is unsafe to call
+
+  if (maybe_fn->get_extern_kind () != ExternalItem::ExternKind::Function)
+    return;
+
+  // Some intrinsics are safe to call
+  if (parent_block->get_abi () == Rust::ABI::INTRINSIC
+      && is_safe_intrinsic (maybe_fn->get_item_name ()))
+    return;
+
+  rust_error_at (locus,
+		 "call to extern function requires unsafe function or block");
+}
+
+void
+UnsafeChecker::check_function_call (HirId node_id, Location locus)
+{
+  if (unsafe_context.is_in_context ())
+    return;
+
+  HirId parent_extern_block;
+  auto maybe_fn = mappings.lookup_hir_item (node_id);
+  auto maybe_extern
+    = mappings.lookup_hir_extern_item (node_id, &parent_extern_block);
+
+  if (maybe_fn && maybe_fn->get_item_kind () == Item::ItemKind::Function)
+    check_unsafe_call (static_cast<Function *> (maybe_fn), locus, "function");
+
+  if (maybe_extern)
+    check_extern_call (static_cast<ExternalItem *> (maybe_extern),
+		       mappings.lookup_hir_extern_block (parent_extern_block),
+		       locus);
+}
+
+void
+UnsafeChecker::visit (Lifetime &lifetime)
+{}
+
+void
+UnsafeChecker::visit (LifetimeParam &lifetime_param)
+{}
+
+void
+UnsafeChecker::visit (PathInExpression &path)
+{
+  NodeId ast_node_id = path.get_mappings ().get_nodeid ();
+  NodeId ref_node_id;
+  HirId definition_id;
+
+  if (!resolver.lookup_resolved_name (ast_node_id, &ref_node_id))
+    return;
+
+  rust_assert (mappings.lookup_node_to_hir (ref_node_id, &definition_id));
+
+  check_use_of_static (definition_id, path.get_locus ());
+}
+
+void
+UnsafeChecker::visit (TypePathSegment &segment)
+{}
+
+void
+UnsafeChecker::visit (TypePathSegmentGeneric &segment)
+{}
+
+void
+UnsafeChecker::visit (TypePathSegmentFunction &segment)
+{}
+
+void
+UnsafeChecker::visit (TypePath &path)
+{}
+
+void
+UnsafeChecker::visit (QualifiedPathInExpression &path)
+{}
+
+void
+UnsafeChecker::visit (QualifiedPathInType &path)
+{}
+
+void
+UnsafeChecker::visit (LiteralExpr &expr)
+{}
+
+void
+UnsafeChecker::visit (BorrowExpr &expr)
+{
+  expr.get_expr ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (DereferenceExpr &expr)
+{
+  TyTy::BaseType *to_deref_type;
+  auto to_deref = expr.get_expr ()->get_mappings ().get_hirid ();
+
+  rust_assert (context.lookup_type (to_deref, &to_deref_type));
+
+  if (to_deref_type->get_kind () == TyTy::TypeKind::POINTER
+      && !unsafe_context.is_in_context ())
+    rust_error_at (expr.get_locus (), "dereference of raw pointer requires "
+				      "unsafe function or block");
+}
+
+void
+UnsafeChecker::visit (ErrorPropagationExpr &expr)
+{
+  expr.get_expr ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (NegationExpr &expr)
+{
+  expr.get_expr ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (ArithmeticOrLogicalExpr &expr)
+{
+  expr.get_lhs ()->accept_vis (*this);
+  expr.get_rhs ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (ComparisonExpr &expr)
+{
+  expr.get_lhs ()->accept_vis (*this);
+  expr.get_rhs ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (LazyBooleanExpr &expr)
+{
+  expr.get_lhs ()->accept_vis (*this);
+  expr.get_rhs ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (TypeCastExpr &expr)
+{
+  expr.get_expr ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (AssignmentExpr &expr)
+{
+  expr.get_lhs ()->accept_vis (*this);
+  expr.get_rhs ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (CompoundAssignmentExpr &expr)
+{
+  expr.get_left_expr ()->accept_vis (*this);
+  expr.get_right_expr ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (GroupedExpr &expr)
+{
+  expr.get_expr_in_parens ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (ArrayElemsValues &elems)
+{
+  for (auto &elem : elems.get_values ())
+    elem->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (ArrayElemsCopied &elems)
+{
+  elems.get_elem_to_copy ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (ArrayExpr &expr)
+{
+  expr.get_internal_elements ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (ArrayIndexExpr &expr)
+{
+  expr.get_array_expr ()->accept_vis (*this);
+  expr.get_index_expr ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (TupleExpr &expr)
+{
+  for (auto &elem : expr.get_tuple_elems ())
+    elem->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (TupleIndexExpr &expr)
+{
+  expr.get_tuple_expr ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (StructExprStruct &expr)
+{}
+
+void
+UnsafeChecker::visit (StructExprFieldIdentifier &field)
+{}
+
+void
+UnsafeChecker::visit (StructExprFieldIdentifierValue &field)
+{
+  field.get_value ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (StructExprFieldIndexValue &field)
+{
+  field.get_value ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (StructExprStructFields &expr)
+{
+  for (auto &field : expr.get_fields ())
+    field->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (StructExprStructBase &expr)
+{}
+
+void
+UnsafeChecker::visit (CallExpr &expr)
+{
+  auto fn = expr.get_fnexpr ();
+  if (!fn)
+    return;
+
+  NodeId ast_node_id = fn->get_mappings ().get_nodeid ();
+  NodeId ref_node_id;
+  HirId definition_id;
+
+  // There are no unsafe types, and functions are defined in the name resolver.
+  // If we can't find the name, then we're dealing with a type and should return
+  // early.
+  if (!resolver.lookup_resolved_name (ast_node_id, &ref_node_id))
+    return;
+
+  rust_assert (mappings.lookup_node_to_hir (ref_node_id, &definition_id));
+
+  // At this point we have the function's HIR Id. There are two checks we
+  // must perform:
+  //     1. The function is an unsafe one
+  //     2. The function is an extern one
+  check_function_call (definition_id, expr.get_locus ());
+
+  if (expr.has_params ())
+    for (auto &arg : expr.get_arguments ())
+      arg->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (MethodCallExpr &expr)
+{
+  TyTy::BaseType *method_type;
+  context.lookup_type (expr.get_method_name ().get_mappings ().get_hirid (),
+		       &method_type);
+
+  auto fn = *static_cast<TyTy::FnType *> (method_type);
+  auto method = mappings.lookup_hir_implitem (fn.get_ref (), nullptr);
+
+  if (!unsafe_context.is_in_context () && method)
+    check_unsafe_call (static_cast<Function *> (method), expr.get_locus (),
+		       "method");
+
+  expr.get_receiver ()->accept_vis (*this);
+
+  for (auto &arg : expr.get_arguments ())
+    arg->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (FieldAccessExpr &expr)
+{
+  expr.get_receiver_expr ()->accept_vis (*this);
+
+  if (unsafe_context.is_in_context ())
+    return;
+
+  TyTy::BaseType *receiver_ty;
+  auto ok = context.lookup_type (
+    expr.get_receiver_expr ()->get_mappings ().get_hirid (), &receiver_ty);
+  rust_assert (ok);
+
+  if (receiver_ty->get_kind () == TyTy::TypeKind::ADT)
+    {
+      auto maybe_union = static_cast<TyTy::ADTType *> (receiver_ty);
+      if (maybe_union->is_union ())
+	rust_error_at (
+	  expr.get_locus (),
+	  "access to union field requires unsafe function or block");
+    }
+}
+
+void
+UnsafeChecker::visit (ClosureExprInner &expr)
+{}
+
+void
+UnsafeChecker::visit (BlockExpr &expr)
+{
+  for (auto &stmt : expr.get_statements ())
+    stmt->accept_vis (*this);
+
+  if (expr.has_expr ())
+    expr.get_final_expr ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (ClosureExprInnerTyped &expr)
+{}
+
+void
+UnsafeChecker::visit (ContinueExpr &expr)
+{}
+
+void
+UnsafeChecker::visit (BreakExpr &expr)
+{
+  if (expr.has_break_expr ())
+    expr.get_expr ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (RangeFromToExpr &expr)
+{
+  expr.get_from_expr ()->accept_vis (*this);
+  expr.get_to_expr ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (RangeFromExpr &expr)
+{
+  expr.get_from_expr ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (RangeToExpr &expr)
+{
+  expr.get_to_expr ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (RangeFullExpr &expr)
+{}
+
+void
+UnsafeChecker::visit (RangeFromToInclExpr &expr)
+{
+  expr.get_from_expr ()->accept_vis (*this);
+  expr.get_to_expr ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (RangeToInclExpr &expr)
+{
+  expr.get_to_expr ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (ReturnExpr &expr)
+{
+  if (expr.has_return_expr ())
+    expr.get_expr ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (UnsafeBlockExpr &expr)
+{
+  unsafe_context.enter (expr.get_mappings ().get_hirid ());
+
+  expr.get_block_expr ()->accept_vis (*this);
+
+  unsafe_context.exit ();
+}
+
+void
+UnsafeChecker::visit (LoopExpr &expr)
+{
+  expr.get_loop_block ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (WhileLoopExpr &expr)
+{
+  expr.get_predicate_expr ()->accept_vis (*this);
+  expr.get_loop_block ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (WhileLetLoopExpr &expr)
+{
+  expr.get_cond ()->accept_vis (*this);
+  expr.get_loop_block ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (ForLoopExpr &expr)
+{
+  expr.get_iterator_expr ()->accept_vis (*this);
+  expr.get_loop_block ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (IfExpr &expr)
+{
+  expr.get_if_condition ()->accept_vis (*this);
+  expr.get_if_block ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (IfExprConseqElse &expr)
+{
+  expr.get_if_condition ()->accept_vis (*this);
+  expr.get_if_block ()->accept_vis (*this);
+  expr.get_else_block ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (IfExprConseqIf &expr)
+{
+  expr.get_if_condition ()->accept_vis (*this);
+  expr.get_if_block ()->accept_vis (*this);
+  expr.get_conseq_if_expr ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (IfExprConseqIfLet &expr)
+{
+  expr.get_if_condition ()->accept_vis (*this);
+  expr.get_if_block ()->accept_vis (*this);
+
+  // TODO: Visit conseq if let expression
+}
+
+void
+UnsafeChecker::visit (IfLetExpr &expr)
+{
+  expr.get_scrutinee_expr ()->accept_vis (*this);
+  expr.get_if_block ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (IfLetExprConseqElse &expr)
+{
+  expr.get_scrutinee_expr ()->accept_vis (*this);
+  expr.get_if_block ()->accept_vis (*this);
+
+  // TODO: Visit else expression
+}
+
+void
+UnsafeChecker::visit (IfLetExprConseqIf &expr)
+{
+  expr.get_scrutinee_expr ()->accept_vis (*this);
+  expr.get_if_block ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (IfLetExprConseqIfLet &expr)
+{
+  expr.get_scrutinee_expr ()->accept_vis (*this);
+  expr.get_if_block ()->accept_vis (*this);
+
+  // TODO: Visit conseq if let expression
+}
+
+void
+UnsafeChecker::visit (MatchExpr &expr)
+{
+  expr.get_scrutinee_expr ()->accept_vis (*this);
+
+  for (auto &match_arm : expr.get_match_cases ())
+    match_arm.get_expr ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (AwaitExpr &expr)
+{
+  // TODO: Visit expression
+}
+
+void
+UnsafeChecker::visit (AsyncBlockExpr &expr)
+{
+  // TODO: Visit block expression
+}
+
+void
+UnsafeChecker::visit (TypeParam &param)
+{}
+
+void
+UnsafeChecker::visit (ConstGenericParam &param)
+{}
+
+void
+UnsafeChecker::visit (LifetimeWhereClauseItem &item)
+{}
+
+void
+UnsafeChecker::visit (TypeBoundWhereClauseItem &item)
+{}
+
+void
+UnsafeChecker::visit (Module &module)
+{
+  for (auto &item : module.get_items ())
+    item->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (ExternCrate &crate)
+{}
+
+void
+UnsafeChecker::visit (UseTreeGlob &use_tree)
+{}
+
+void
+UnsafeChecker::visit (UseTreeList &use_tree)
+{}
+
+void
+UnsafeChecker::visit (UseTreeRebind &use_tree)
+{}
+
+void
+UnsafeChecker::visit (UseDeclaration &use_decl)
+{}
+
+void
+UnsafeChecker::visit (Function &function)
+{
+  auto is_unsafe_fn = function.get_qualifiers ().is_unsafe ();
+
+  if (is_unsafe_fn)
+    unsafe_context.enter (function.get_mappings ().get_hirid ());
+
+  function.get_definition ()->accept_vis (*this);
+
+  if (is_unsafe_fn)
+    unsafe_context.exit ();
+}
+
+void
+UnsafeChecker::visit (TypeAlias &type_alias)
+{
+  // FIXME: What do we need to do to handle type aliasing? Is it possible to
+  // have unsafe types? Type aliases on unsafe functions?
+}
+
+void
+UnsafeChecker::visit (StructStruct &struct_item)
+{}
+
+void
+UnsafeChecker::visit (TupleStruct &tuple_struct)
+{}
+
+void
+UnsafeChecker::visit (EnumItem &item)
+{}
+
+void
+UnsafeChecker::visit (EnumItemTuple &item)
+{}
+
+void
+UnsafeChecker::visit (EnumItemStruct &item)
+{}
+
+void
+UnsafeChecker::visit (EnumItemDiscriminant &item)
+{}
+
+void
+UnsafeChecker::visit (Enum &enum_item)
+{}
+
+void
+UnsafeChecker::visit (Union &union_item)
+{}
+
+void
+UnsafeChecker::visit (ConstantItem &const_item)
+{
+  const_item.get_expr ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (StaticItem &static_item)
+{
+  static_item.get_expr ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (TraitItemFunc &item)
+{
+  if (item.has_block_defined ())
+    item.get_block_expr ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (TraitItemConst &item)
+{
+  if (item.has_expr ())
+    item.get_expr ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (TraitItemType &item)
+{}
+
+void
+UnsafeChecker::visit (Trait &trait)
+{
+  // FIXME: Handle unsafe traits
+  for (auto &item : trait.get_trait_items ())
+    item->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (ImplBlock &impl)
+{
+  // FIXME: Handle unsafe impls
+  for (auto &item : impl.get_impl_items ())
+    item->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (ExternalStaticItem &item)
+{}
+
+void
+UnsafeChecker::visit (ExternalFunctionItem &item)
+{}
+
+void
+UnsafeChecker::visit (ExternBlock &block)
+{
+  // FIXME: Do we need to do this?
+  for (auto &item : block.get_extern_items ())
+    item->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (LiteralPattern &pattern)
+{}
+
+void
+UnsafeChecker::visit (IdentifierPattern &pattern)
+{}
+
+void
+UnsafeChecker::visit (WildcardPattern &pattern)
+{}
+
+void
+UnsafeChecker::visit (RangePatternBoundLiteral &bound)
+{}
+
+void
+UnsafeChecker::visit (RangePatternBoundPath &bound)
+{}
+
+void
+UnsafeChecker::visit (RangePatternBoundQualPath &bound)
+{}
+
+void
+UnsafeChecker::visit (RangePattern &pattern)
+{}
+
+void
+UnsafeChecker::visit (ReferencePattern &pattern)
+{}
+
+void
+UnsafeChecker::visit (StructPatternFieldTuplePat &field)
+{}
+
+void
+UnsafeChecker::visit (StructPatternFieldIdentPat &field)
+{}
+
+void
+UnsafeChecker::visit (StructPatternFieldIdent &field)
+{}
+
+void
+UnsafeChecker::visit (StructPattern &pattern)
+{}
+
+void
+UnsafeChecker::visit (TupleStructItemsNoRange &tuple_items)
+{}
+
+void
+UnsafeChecker::visit (TupleStructItemsRange &tuple_items)
+{}
+
+void
+UnsafeChecker::visit (TupleStructPattern &pattern)
+{}
+
+void
+UnsafeChecker::visit (TuplePatternItemsMultiple &tuple_items)
+{}
+
+void
+UnsafeChecker::visit (TuplePatternItemsRanged &tuple_items)
+{}
+
+void
+UnsafeChecker::visit (TuplePattern &pattern)
+{}
+
+void
+UnsafeChecker::visit (GroupedPattern &pattern)
+{}
+
+void
+UnsafeChecker::visit (SlicePattern &pattern)
+{}
+
+void
+UnsafeChecker::visit (EmptyStmt &stmt)
+{}
+
+void
+UnsafeChecker::visit (LetStmt &stmt)
+{
+  if (stmt.has_init_expr ())
+    stmt.get_init_expr ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (ExprStmtWithoutBlock &stmt)
+{
+  stmt.get_expr ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (ExprStmtWithBlock &stmt)
+{
+  stmt.get_expr ()->accept_vis (*this);
+}
+
+void
+UnsafeChecker::visit (TraitBound &bound)
+{}
+
+void
+UnsafeChecker::visit (ImplTraitType &type)
+{}
+
+void
+UnsafeChecker::visit (TraitObjectType &type)
+{}
+
+void
+UnsafeChecker::visit (ParenthesisedType &type)
+{}
+
+void
+UnsafeChecker::visit (ImplTraitTypeOneBound &type)
+{}
+
+void
+UnsafeChecker::visit (TupleType &type)
+{}
+
+void
+UnsafeChecker::visit (NeverType &type)
+{}
+
+void
+UnsafeChecker::visit (RawPointerType &type)
+{}
+
+void
+UnsafeChecker::visit (ReferenceType &type)
+{}
+
+void
+UnsafeChecker::visit (ArrayType &type)
+{}
+
+void
+UnsafeChecker::visit (SliceType &type)
+{}
+
+void
+UnsafeChecker::visit (InferredType &type)
+{}
+
+void
+UnsafeChecker::visit (BareFunctionType &type)
+{}
+
+} // namespace HIR
+} // namespace Rust
diff --git a/gcc/rust/checks/errors/rust-unsafe-checker.h b/gcc/rust/checks/errors/rust-unsafe-checker.h
new file mode 100644
index 00000000000..ae1eb509d78
--- /dev/null
+++ b/gcc/rust/checks/errors/rust-unsafe-checker.h
@@ -0,0 +1,191 @@
+// 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_UNSAFE_CHECKER_H
+#define RUST_UNSAFE_CHECKER_H
+
+#include "rust-hir-visitor.h"
+#include "rust-name-resolver.h"
+#include "rust-hir-type-check.h"
+#include "rust-stacked-contexts.h"
+
+namespace Rust {
+namespace HIR {
+class UnsafeChecker : public HIRFullVisitor
+{
+public:
+  UnsafeChecker ();
+
+  void go (HIR::Crate &crate);
+
+private:
+  /**
+   * Check if a mutable static or external static item is used outside of an
+   * unsafe context
+   */
+  void check_use_of_static (HirId node_id, Location locus);
+
+  /**
+   * Check if a call to an unsafe or external function is outside of an unsafe
+   * context
+   */
+  void check_function_call (HirId node_id, Location locus);
+
+  StackedContexts<HirId> unsafe_context;
+
+  Resolver::TypeCheckContext &context;
+  Resolver::Resolver &resolver;
+  Analysis::Mappings &mappings;
+
+  virtual void visit (Lifetime &lifetime) override;
+  virtual void visit (LifetimeParam &lifetime_param) override;
+  virtual void visit (PathInExpression &path) override;
+  virtual void visit (TypePathSegment &segment) override;
+  virtual void visit (TypePathSegmentGeneric &segment) override;
+  virtual void visit (TypePathSegmentFunction &segment) override;
+  virtual void visit (TypePath &path) override;
+  virtual void visit (QualifiedPathInExpression &path) override;
+  virtual void visit (QualifiedPathInType &path) override;
+  virtual void visit (LiteralExpr &expr) override;
+  virtual void visit (BorrowExpr &expr) override;
+  virtual void visit (DereferenceExpr &expr) override;
+  virtual void visit (ErrorPropagationExpr &expr) override;
+  virtual void visit (NegationExpr &expr) override;
+  virtual void visit (ArithmeticOrLogicalExpr &expr) override;
+  virtual void visit (ComparisonExpr &expr) override;
+  virtual void visit (LazyBooleanExpr &expr) override;
+  virtual void visit (TypeCastExpr &expr) override;
+  virtual void visit (AssignmentExpr &expr) override;
+  virtual void visit (CompoundAssignmentExpr &expr) override;
+  virtual void visit (GroupedExpr &expr) override;
+  virtual void visit (ArrayElemsValues &elems) override;
+  virtual void visit (ArrayElemsCopied &elems) override;
+  virtual void visit (ArrayExpr &expr) override;
+  virtual void visit (ArrayIndexExpr &expr) override;
+  virtual void visit (TupleExpr &expr) override;
+  virtual void visit (TupleIndexExpr &expr) override;
+  virtual void visit (StructExprStruct &expr) override;
+  virtual void visit (StructExprFieldIdentifier &field) override;
+  virtual void visit (StructExprFieldIdentifierValue &field) override;
+  virtual void visit (StructExprFieldIndexValue &field) override;
+  virtual void visit (StructExprStructFields &expr) override;
+  virtual void visit (StructExprStructBase &expr) override;
+  virtual void visit (CallExpr &expr) override;
+  virtual void visit (MethodCallExpr &expr) override;
+  virtual void visit (FieldAccessExpr &expr) override;
+  virtual void visit (ClosureExprInner &expr) override;
+  virtual void visit (BlockExpr &expr) override;
+  virtual void visit (ClosureExprInnerTyped &expr) override;
+  virtual void visit (ContinueExpr &expr) override;
+  virtual void visit (BreakExpr &expr) override;
+  virtual void visit (RangeFromToExpr &expr) override;
+  virtual void visit (RangeFromExpr &expr) override;
+  virtual void visit (RangeToExpr &expr) override;
+  virtual void visit (RangeFullExpr &expr) override;
+  virtual void visit (RangeFromToInclExpr &expr) override;
+  virtual void visit (RangeToInclExpr &expr) override;
+  virtual void visit (ReturnExpr &expr) override;
+  virtual void visit (UnsafeBlockExpr &expr) override;
+  virtual void visit (LoopExpr &expr) override;
+  virtual void visit (WhileLoopExpr &expr) override;
+  virtual void visit (WhileLetLoopExpr &expr) override;
+  virtual void visit (ForLoopExpr &expr) override;
+  virtual void visit (IfExpr &expr) override;
+  virtual void visit (IfExprConseqElse &expr) override;
+  virtual void visit (IfExprConseqIf &expr) override;
+  virtual void visit (IfExprConseqIfLet &expr) override;
+  virtual void visit (IfLetExpr &expr) override;
+  virtual void visit (IfLetExprConseqElse &expr) override;
+  virtual void visit (IfLetExprConseqIf &expr) override;
+  virtual void visit (IfLetExprConseqIfLet &expr) override;
+  virtual void visit (MatchExpr &expr) override;
+  virtual void visit (AwaitExpr &expr) override;
+  virtual void visit (AsyncBlockExpr &expr) override;
+  virtual void visit (TypeParam &param) override;
+  virtual void visit (ConstGenericParam &param) override;
+  virtual void visit (LifetimeWhereClauseItem &item) override;
+  virtual void visit (TypeBoundWhereClauseItem &item) override;
+  virtual void visit (Module &module) override;
+  virtual void visit (ExternCrate &crate) override;
+  virtual void visit (UseTreeGlob &use_tree) override;
+  virtual void visit (UseTreeList &use_tree) override;
+  virtual void visit (UseTreeRebind &use_tree) override;
+  virtual void visit (UseDeclaration &use_decl) override;
+  virtual void visit (Function &function) override;
+  virtual void visit (TypeAlias &type_alias) override;
+  virtual void visit (StructStruct &struct_item) override;
+  virtual void visit (TupleStruct &tuple_struct) override;
+  virtual void visit (EnumItem &item) override;
+  virtual void visit (EnumItemTuple &item) override;
+  virtual void visit (EnumItemStruct &item) override;
+  virtual void visit (EnumItemDiscriminant &item) override;
+  virtual void visit (Enum &enum_item) override;
+  virtual void visit (Union &union_item) override;
+  virtual void visit (ConstantItem &const_item) override;
+  virtual void visit (StaticItem &static_item) override;
+  virtual void visit (TraitItemFunc &item) override;
+  virtual void visit (TraitItemConst &item) override;
+  virtual void visit (TraitItemType &item) override;
+  virtual void visit (Trait &trait) override;
+  virtual void visit (ImplBlock &impl) override;
+  virtual void visit (ExternalStaticItem &item) override;
+  virtual void visit (ExternalFunctionItem &item) override;
+  virtual void visit (ExternBlock &block) override;
+  virtual void visit (LiteralPattern &pattern) override;
+  virtual void visit (IdentifierPattern &pattern) override;
+  virtual void visit (WildcardPattern &pattern) override;
+  virtual void visit (RangePatternBoundLiteral &bound) override;
+  virtual void visit (RangePatternBoundPath &bound) override;
+  virtual void visit (RangePatternBoundQualPath &bound) override;
+  virtual void visit (RangePattern &pattern) override;
+  virtual void visit (ReferencePattern &pattern) override;
+  virtual void visit (StructPatternFieldTuplePat &field) override;
+  virtual void visit (StructPatternFieldIdentPat &field) override;
+  virtual void visit (StructPatternFieldIdent &field) override;
+  virtual void visit (StructPattern &pattern) override;
+  virtual void visit (TupleStructItemsNoRange &tuple_items) override;
+  virtual void visit (TupleStructItemsRange &tuple_items) override;
+  virtual void visit (TupleStructPattern &pattern) override;
+  virtual void visit (TuplePatternItemsMultiple &tuple_items) override;
+  virtual void visit (TuplePatternItemsRanged &tuple_items) override;
+  virtual void visit (TuplePattern &pattern) override;
+  virtual void visit (GroupedPattern &pattern) override;
+  virtual void visit (SlicePattern &pattern) override;
+  virtual void visit (EmptyStmt &stmt) override;
+  virtual void visit (LetStmt &stmt) override;
+  virtual void visit (ExprStmtWithoutBlock &stmt) override;
+  virtual void visit (ExprStmtWithBlock &stmt) override;
+  virtual void visit (TraitBound &bound) override;
+  virtual void visit (ImplTraitType &type) override;
+  virtual void visit (TraitObjectType &type) override;
+  virtual void visit (ParenthesisedType &type) override;
+  virtual void visit (ImplTraitTypeOneBound &type) override;
+  virtual void visit (TupleType &type) override;
+  virtual void visit (NeverType &type) override;
+  virtual void visit (RawPointerType &type) override;
+  virtual void visit (ReferenceType &type) override;
+  virtual void visit (ArrayType &type) override;
+  virtual void visit (SliceType &type) override;
+  virtual void visit (InferredType &type) override;
+  virtual void visit (BareFunctionType &type) override;
+};
+
+} // namespace HIR
+} // namespace Rust
+
+#endif /* !RUST_UNSAFE_CHECKER_H */
-- 
2.25.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [PATCH Rust front-end v2 24/37] gccrs: Add const checker
  2022-08-24 11:59 Rust frontend patches v2 herron.philip
                   ` (22 preceding siblings ...)
  2022-08-24 11:59 ` [PATCH Rust front-end v2 23/37] gccrs: Add unsafe checks for Rust herron.philip
@ 2022-08-24 11:59 ` herron.philip
  2022-08-24 11:59 ` [PATCH Rust front-end v2 25/37] gccrs: Add privacy checks herron.philip
                   ` (13 subsequent siblings)
  37 siblings, 0 replies; 59+ messages in thread
From: herron.philip @ 2022-08-24 11:59 UTC (permalink / raw)
  To: gcc-patches; +Cc: gcc-rust, Arthur Cohen

From: Arthur Cohen <arthur.cohen@embecosm.com>

Similarly to the unsafe checker, constant evaluation can only be performed
in a few contexts and include restrictions on the Rust language. Should
the user fail to uphold those conditions, errors will be reported and the
compilation pipeline interrupted.

These contexts are as follow:

- Array type length expressions
- Array repeat length expressions
- Constants
- Statics
- Enum discriminants
- Const generic arguments

In these contexts, the user is restricted to calling only functions marked
as `const` or perform arithmetic operations only on certain types, among
other restrictions.
---
 gcc/rust/checks/errors/rust-const-checker.cc | 844 +++++++++++++++++++
 gcc/rust/checks/errors/rust-const-checker.h  | 189 +++++
 2 files changed, 1033 insertions(+)
 create mode 100644 gcc/rust/checks/errors/rust-const-checker.cc
 create mode 100644 gcc/rust/checks/errors/rust-const-checker.h

diff --git a/gcc/rust/checks/errors/rust-const-checker.cc b/gcc/rust/checks/errors/rust-const-checker.cc
new file mode 100644
index 00000000000..35c61fe03f0
--- /dev/null
+++ b/gcc/rust/checks/errors/rust-const-checker.cc
@@ -0,0 +1,844 @@
+// 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/>.
+
+#include "rust-const-checker.h"
+#include "rust-hir.h"
+#include "rust-hir-expr.h"
+#include "rust-hir-stmt.h"
+#include "rust-hir-item.h"
+
+namespace Rust {
+namespace HIR {
+
+ConstChecker::ConstChecker ()
+  : resolver (*Resolver::Resolver::get ()),
+    mappings (*Analysis::Mappings::get ())
+{}
+
+void
+ConstChecker::go (HIR::Crate &crate)
+{
+  for (auto &item : crate.items)
+    item->accept_vis (*this);
+}
+
+bool
+ConstChecker::is_const_extern_fn (HIR::ExternalFunctionItem &fn)
+{
+  // FIXME: Is it really how we want to handle `rustc_const_stable`
+  // and `rustc_const_unstable`?
+  // TODO: Add these attributes to the attribute check and handle
+  // `stable` and `unstable` as well
+  return std::any_of (
+    fn.get_outer_attrs ().begin (), fn.get_outer_attrs ().end (),
+    [] (const AST::Attribute &attr) {
+      // `starts_with` in C++11...
+      return attr.get_path ().as_string ().rfind ("rustc_const_", 0) == 0;
+    });
+}
+
+void
+ConstChecker::visit (Lifetime &lifetime)
+{}
+
+void
+ConstChecker::visit (LifetimeParam &lifetime_param)
+{}
+
+void
+ConstChecker::visit (PathInExpression &path)
+{}
+
+void
+ConstChecker::visit (TypePathSegment &segment)
+{}
+
+void
+ConstChecker::visit (TypePathSegmentGeneric &segment)
+{}
+
+void
+ConstChecker::visit (TypePathSegmentFunction &segment)
+{}
+
+void
+ConstChecker::visit (TypePath &path)
+{}
+
+void
+ConstChecker::visit (QualifiedPathInExpression &path)
+{}
+
+void
+ConstChecker::visit (QualifiedPathInType &path)
+{}
+
+void
+ConstChecker::visit (LiteralExpr &expr)
+{}
+
+void
+ConstChecker::visit (BorrowExpr &expr)
+{
+  expr.get_expr ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (DereferenceExpr &expr)
+{
+  expr.get_expr ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (ErrorPropagationExpr &expr)
+{
+  expr.get_expr ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (NegationExpr &expr)
+{
+  expr.get_expr ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (ArithmeticOrLogicalExpr &expr)
+{
+  expr.get_lhs ()->accept_vis (*this);
+  expr.get_rhs ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (ComparisonExpr &expr)
+{
+  expr.get_lhs ()->accept_vis (*this);
+  expr.get_rhs ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (LazyBooleanExpr &expr)
+{
+  expr.get_lhs ()->accept_vis (*this);
+  expr.get_rhs ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (TypeCastExpr &expr)
+{
+  expr.get_expr ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (AssignmentExpr &expr)
+{
+  expr.get_lhs ()->accept_vis (*this);
+  expr.get_rhs ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (CompoundAssignmentExpr &expr)
+{
+  expr.get_left_expr ()->accept_vis (*this);
+  expr.get_right_expr ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (GroupedExpr &expr)
+{
+  expr.get_expr_in_parens ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (ArrayElemsValues &elems)
+{
+  for (auto &elem : elems.get_values ())
+    elem->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (ArrayElemsCopied &elems)
+{
+  elems.get_elem_to_copy ()->accept_vis (*this);
+
+  const_context.enter (elems.get_mappings ().get_hirid ());
+
+  elems.get_num_copies_expr ()->accept_vis (*this);
+
+  const_context.exit ();
+}
+
+void
+ConstChecker::visit (ArrayExpr &expr)
+{
+  expr.get_internal_elements ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (ArrayIndexExpr &expr)
+{
+  expr.get_array_expr ()->accept_vis (*this);
+  expr.get_index_expr ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (TupleExpr &expr)
+{
+  for (auto &elem : expr.get_tuple_elems ())
+    elem->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (TupleIndexExpr &expr)
+{
+  expr.get_tuple_expr ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (StructExprStruct &expr)
+{}
+
+void
+ConstChecker::visit (StructExprFieldIdentifier &field)
+{}
+
+void
+ConstChecker::visit (StructExprFieldIdentifierValue &field)
+{
+  field.get_value ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (StructExprFieldIndexValue &field)
+{
+  field.get_value ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (StructExprStructFields &expr)
+{
+  for (auto &field : expr.get_fields ())
+    field->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (StructExprStructBase &expr)
+{}
+
+void
+ConstChecker::check_function_call (HirId fn_id, Location locus)
+{
+  if (!const_context.is_in_context ())
+    return;
+
+  auto maybe_fn = mappings.lookup_hir_item (fn_id);
+  if (maybe_fn && maybe_fn->get_item_kind () != Item::ItemKind::Function)
+    return;
+
+  // There are const extern functions (intrinsics)
+  // TODO: Should we check the ABI is only "rust intrinsics"? Is that handled
+  // elsewhere?
+  HirId parent_block;
+  auto maybe_extern_item
+    = mappings.lookup_hir_extern_item (fn_id, &parent_block);
+  if (maybe_extern_item
+      && maybe_extern_item->get_extern_kind ()
+	   != ExternalItem::ExternKind::Function)
+    return;
+
+  auto is_error = false;
+  if (maybe_fn)
+    {
+      auto fn = static_cast<Function *> (maybe_fn);
+      if (!fn->get_qualifiers ().is_const ())
+	is_error = true;
+    }
+
+  if (maybe_extern_item)
+    {
+      {
+	auto fn = static_cast<ExternalFunctionItem *> (maybe_extern_item);
+	if (!is_const_extern_fn (*fn))
+	  is_error = true;
+      }
+    }
+
+  if (is_error)
+    rust_error_at (locus, "only functions marked as %<const%> are allowed to "
+			  "be called from constant contexts");
+}
+
+void
+ConstChecker::visit (CallExpr &expr)
+{
+  auto fn = expr.get_fnexpr ();
+  if (!fn)
+    return;
+
+  NodeId ast_node_id = fn->get_mappings ().get_nodeid ();
+  NodeId ref_node_id;
+  HirId definition_id;
+
+  // We don't care about types here
+  if (!resolver.lookup_resolved_name (ast_node_id, &ref_node_id))
+    return;
+
+  rust_assert (mappings.lookup_node_to_hir (ref_node_id, &definition_id));
+
+  check_function_call (definition_id, expr.get_locus ());
+
+  for (auto &arg : expr.get_arguments ())
+    arg->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (MethodCallExpr &expr)
+{
+  expr.get_receiver ()->accept_vis (*this);
+
+  for (auto &arg : expr.get_arguments ())
+    arg->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (FieldAccessExpr &expr)
+{
+  expr.get_receiver_expr ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (ClosureExprInner &expr)
+{}
+
+void
+ConstChecker::visit (BlockExpr &expr)
+{
+  for (auto &stmt : expr.get_statements ())
+    stmt->accept_vis (*this);
+
+  if (expr.has_expr ())
+    expr.get_final_expr ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (ClosureExprInnerTyped &expr)
+{}
+
+void
+ConstChecker::visit (ContinueExpr &expr)
+{}
+
+void
+ConstChecker::visit (BreakExpr &expr)
+{
+  if (expr.has_break_expr ())
+    expr.get_expr ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (RangeFromToExpr &expr)
+{
+  expr.get_from_expr ()->accept_vis (*this);
+  expr.get_to_expr ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (RangeFromExpr &expr)
+{
+  expr.get_from_expr ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (RangeToExpr &expr)
+{
+  expr.get_to_expr ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (RangeFullExpr &expr)
+{}
+
+void
+ConstChecker::visit (RangeFromToInclExpr &expr)
+{
+  expr.get_from_expr ()->accept_vis (*this);
+  expr.get_to_expr ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (RangeToInclExpr &expr)
+{
+  // FIXME: Visit to_expr
+}
+
+void
+ConstChecker::visit (ReturnExpr &expr)
+{
+  if (expr.has_return_expr ())
+    expr.get_expr ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (UnsafeBlockExpr &expr)
+{
+  expr.get_block_expr ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (LoopExpr &expr)
+{
+  expr.get_loop_block ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (WhileLoopExpr &expr)
+{
+  expr.get_predicate_expr ()->accept_vis (*this);
+  expr.get_loop_block ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (WhileLetLoopExpr &expr)
+{
+  expr.get_cond ()->accept_vis (*this);
+  expr.get_loop_block ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (ForLoopExpr &expr)
+{
+  expr.get_iterator_expr ()->accept_vis (*this);
+  expr.get_loop_block ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (IfExpr &expr)
+{
+  expr.get_if_condition ()->accept_vis (*this);
+  expr.get_if_block ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (IfExprConseqElse &expr)
+{
+  expr.get_if_condition ()->accept_vis (*this);
+  expr.get_if_block ()->accept_vis (*this);
+  expr.get_else_block ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (IfExprConseqIf &expr)
+{
+  expr.get_if_condition ()->accept_vis (*this);
+  expr.get_if_block ()->accept_vis (*this);
+  expr.get_conseq_if_expr ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (IfExprConseqIfLet &expr)
+{
+  expr.get_if_condition ()->accept_vis (*this);
+  expr.get_if_block ()->accept_vis (*this);
+
+  // TODO: Visit conseq if let expression
+}
+
+void
+ConstChecker::visit (IfLetExpr &expr)
+{
+  expr.get_scrutinee_expr ()->accept_vis (*this);
+  expr.get_if_block ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (IfLetExprConseqElse &expr)
+{
+  expr.get_scrutinee_expr ()->accept_vis (*this);
+  expr.get_if_block ()->accept_vis (*this);
+
+  // TODO: Visit else expression
+}
+
+void
+ConstChecker::visit (IfLetExprConseqIf &expr)
+{
+  expr.get_scrutinee_expr ()->accept_vis (*this);
+  expr.get_if_block ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (IfLetExprConseqIfLet &expr)
+{
+  expr.get_scrutinee_expr ()->accept_vis (*this);
+  expr.get_if_block ()->accept_vis (*this);
+
+  // TODO: Visit conseq if let expression
+}
+
+void
+ConstChecker::visit (MatchExpr &expr)
+{
+  expr.get_scrutinee_expr ()->accept_vis (*this);
+
+  for (auto &match_arm : expr.get_match_cases ())
+    match_arm.get_expr ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (AwaitExpr &expr)
+{
+  // TODO: Visit expression
+}
+
+void
+ConstChecker::visit (AsyncBlockExpr &expr)
+{
+  // TODO: Visit block expression
+}
+
+void
+ConstChecker::visit (TypeParam &param)
+{}
+
+void
+ConstChecker::visit (ConstGenericParam &param)
+{}
+
+void
+ConstChecker::visit (LifetimeWhereClauseItem &item)
+{}
+
+void
+ConstChecker::visit (TypeBoundWhereClauseItem &item)
+{}
+
+void
+ConstChecker::visit (Module &module)
+{
+  for (auto &item : module.get_items ())
+    item->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (ExternCrate &crate)
+{}
+
+void
+ConstChecker::visit (UseTreeGlob &use_tree)
+{}
+
+void
+ConstChecker::visit (UseTreeList &use_tree)
+{}
+
+void
+ConstChecker::visit (UseTreeRebind &use_tree)
+{}
+
+void
+ConstChecker::visit (UseDeclaration &use_decl)
+{}
+
+void
+ConstChecker::visit (Function &function)
+{
+  auto const_fn = function.get_qualifiers ().is_const ();
+  if (const_fn)
+    const_context.enter (function.get_mappings ().get_hirid ());
+
+  for (auto &param : function.get_function_params ())
+    param.get_type ()->accept_vis (*this);
+
+  function.get_definition ()->accept_vis (*this);
+
+  if (const_fn)
+    const_context.exit ();
+}
+
+void
+ConstChecker::visit (TypeAlias &type_alias)
+{}
+
+void
+ConstChecker::visit (StructStruct &struct_item)
+{}
+
+void
+ConstChecker::visit (TupleStruct &tuple_struct)
+{}
+
+void
+ConstChecker::visit (EnumItem &item)
+{}
+
+void
+ConstChecker::visit (EnumItemTuple &item)
+{}
+
+void
+ConstChecker::visit (EnumItemStruct &item)
+{}
+
+void
+ConstChecker::visit (EnumItemDiscriminant &item)
+{
+  const_context.enter (item.get_mappings ().get_hirid ());
+
+  item.get_discriminant_expression ()->accept_vis (*this);
+
+  const_context.exit ();
+}
+
+void
+ConstChecker::visit (Enum &enum_item)
+{}
+
+void
+ConstChecker::visit (Union &union_item)
+{}
+
+void
+ConstChecker::visit (ConstantItem &const_item)
+{
+  const_context.enter (const_item.get_mappings ().get_hirid ());
+
+  const_item.get_expr ()->accept_vis (*this);
+
+  const_context.exit ();
+}
+
+void
+ConstChecker::visit (StaticItem &static_item)
+{
+  const_context.enter (static_item.get_mappings ().get_hirid ());
+
+  static_item.get_expr ()->accept_vis (*this);
+
+  const_context.exit ();
+}
+
+void
+ConstChecker::visit (TraitItemFunc &item)
+{
+  if (item.has_block_defined ())
+    item.get_block_expr ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (TraitItemConst &item)
+{
+  if (item.has_expr ())
+    item.get_expr ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (TraitItemType &item)
+{}
+
+void
+ConstChecker::visit (Trait &trait)
+{
+  for (auto &item : trait.get_trait_items ())
+    item->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (ImplBlock &impl)
+{
+  for (auto &item : impl.get_impl_items ())
+    item->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (ExternalStaticItem &item)
+{}
+
+void
+ConstChecker::visit (ExternalFunctionItem &item)
+{}
+
+void
+ConstChecker::visit (ExternBlock &block)
+{
+  // FIXME: Do we need to do this?
+  for (auto &item : block.get_extern_items ())
+    item->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (LiteralPattern &pattern)
+{}
+
+void
+ConstChecker::visit (IdentifierPattern &pattern)
+{}
+
+void
+ConstChecker::visit (WildcardPattern &pattern)
+{}
+
+void
+ConstChecker::visit (RangePatternBoundLiteral &bound)
+{}
+
+void
+ConstChecker::visit (RangePatternBoundPath &bound)
+{}
+
+void
+ConstChecker::visit (RangePatternBoundQualPath &bound)
+{}
+
+void
+ConstChecker::visit (RangePattern &pattern)
+{}
+
+void
+ConstChecker::visit (ReferencePattern &pattern)
+{}
+
+void
+ConstChecker::visit (StructPatternFieldTuplePat &field)
+{}
+
+void
+ConstChecker::visit (StructPatternFieldIdentPat &field)
+{}
+
+void
+ConstChecker::visit (StructPatternFieldIdent &field)
+{}
+
+void
+ConstChecker::visit (StructPattern &pattern)
+{}
+
+void
+ConstChecker::visit (TupleStructItemsNoRange &tuple_items)
+{}
+
+void
+ConstChecker::visit (TupleStructItemsRange &tuple_items)
+{}
+
+void
+ConstChecker::visit (TupleStructPattern &pattern)
+{}
+
+void
+ConstChecker::visit (TuplePatternItemsMultiple &tuple_items)
+{}
+
+void
+ConstChecker::visit (TuplePatternItemsRanged &tuple_items)
+{}
+
+void
+ConstChecker::visit (TuplePattern &pattern)
+{}
+
+void
+ConstChecker::visit (GroupedPattern &pattern)
+{}
+
+void
+ConstChecker::visit (SlicePattern &pattern)
+{}
+
+void
+ConstChecker::visit (EmptyStmt &stmt)
+{}
+
+void
+ConstChecker::visit (LetStmt &stmt)
+{
+  if (stmt.has_init_expr ())
+    stmt.get_init_expr ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (ExprStmtWithoutBlock &stmt)
+{
+  stmt.get_expr ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (ExprStmtWithBlock &stmt)
+{
+  stmt.get_expr ()->accept_vis (*this);
+}
+
+void
+ConstChecker::visit (TraitBound &bound)
+{}
+
+void
+ConstChecker::visit (ImplTraitType &type)
+{}
+
+void
+ConstChecker::visit (TraitObjectType &type)
+{}
+
+void
+ConstChecker::visit (ParenthesisedType &type)
+{}
+
+void
+ConstChecker::visit (ImplTraitTypeOneBound &type)
+{}
+
+void
+ConstChecker::visit (TupleType &type)
+{}
+
+void
+ConstChecker::visit (NeverType &type)
+{}
+
+void
+ConstChecker::visit (RawPointerType &type)
+{}
+
+void
+ConstChecker::visit (ReferenceType &type)
+{}
+
+void
+ConstChecker::visit (ArrayType &type)
+{
+  const_context.enter (type.get_mappings ().get_hirid ());
+
+  type.get_size_expr ()->accept_vis (*this);
+
+  const_context.exit ();
+}
+
+void
+ConstChecker::visit (SliceType &type)
+{}
+
+void
+ConstChecker::visit (InferredType &type)
+{}
+
+void
+ConstChecker::visit (BareFunctionType &type)
+{}
+
+} // namespace HIR
+} // namespace Rust
diff --git a/gcc/rust/checks/errors/rust-const-checker.h b/gcc/rust/checks/errors/rust-const-checker.h
new file mode 100644
index 00000000000..50838d18996
--- /dev/null
+++ b/gcc/rust/checks/errors/rust-const-checker.h
@@ -0,0 +1,189 @@
+// 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_CONST_CHECKER_H
+#define RUST_CONST_CHECKER_H
+
+#include "rust-hir-visitor.h"
+#include "rust-hir-type-check.h"
+#include "rust-stacked-contexts.h"
+#include "rust-name-resolver.h"
+
+namespace Rust {
+namespace HIR {
+class ConstChecker : public HIRFullVisitor
+{
+public:
+  ConstChecker ();
+
+  void go (HIR::Crate &crate);
+
+  /**
+   * Check if an item is a const extern item or not
+   * TODO: Move this to a const compilation context class or an attribute
+   * checking class
+   */
+  static bool is_const_extern_fn (HIR::ExternalFunctionItem &fn);
+
+private:
+  /**
+   * Check that only const functions are called in const contexts
+   */
+  void check_function_call (HirId fn_id, Location locus);
+
+  StackedContexts<HirId> const_context;
+  Resolver::Resolver &resolver;
+  Analysis::Mappings &mappings;
+
+  virtual void visit (Lifetime &lifetime) override;
+  virtual void visit (LifetimeParam &lifetime_param) override;
+  virtual void visit (PathInExpression &path) override;
+  virtual void visit (TypePathSegment &segment) override;
+  virtual void visit (TypePathSegmentGeneric &segment) override;
+  virtual void visit (TypePathSegmentFunction &segment) override;
+  virtual void visit (TypePath &path) override;
+  virtual void visit (QualifiedPathInExpression &path) override;
+  virtual void visit (QualifiedPathInType &path) override;
+  virtual void visit (LiteralExpr &expr) override;
+  virtual void visit (BorrowExpr &expr) override;
+  virtual void visit (DereferenceExpr &expr) override;
+  virtual void visit (ErrorPropagationExpr &expr) override;
+  virtual void visit (NegationExpr &expr) override;
+  virtual void visit (ArithmeticOrLogicalExpr &expr) override;
+  virtual void visit (ComparisonExpr &expr) override;
+  virtual void visit (LazyBooleanExpr &expr) override;
+  virtual void visit (TypeCastExpr &expr) override;
+  virtual void visit (AssignmentExpr &expr) override;
+  virtual void visit (CompoundAssignmentExpr &expr) override;
+  virtual void visit (GroupedExpr &expr) override;
+  virtual void visit (ArrayElemsValues &elems) override;
+  virtual void visit (ArrayElemsCopied &elems) override;
+  virtual void visit (ArrayExpr &expr) override;
+  virtual void visit (ArrayIndexExpr &expr) override;
+  virtual void visit (TupleExpr &expr) override;
+  virtual void visit (TupleIndexExpr &expr) override;
+  virtual void visit (StructExprStruct &expr) override;
+  virtual void visit (StructExprFieldIdentifier &field) override;
+  virtual void visit (StructExprFieldIdentifierValue &field) override;
+  virtual void visit (StructExprFieldIndexValue &field) override;
+  virtual void visit (StructExprStructFields &expr) override;
+  virtual void visit (StructExprStructBase &expr) override;
+  virtual void visit (CallExpr &expr) override;
+  virtual void visit (MethodCallExpr &expr) override;
+  virtual void visit (FieldAccessExpr &expr) override;
+  virtual void visit (ClosureExprInner &expr) override;
+  virtual void visit (BlockExpr &expr) override;
+  virtual void visit (ClosureExprInnerTyped &expr) override;
+  virtual void visit (ContinueExpr &expr) override;
+  virtual void visit (BreakExpr &expr) override;
+  virtual void visit (RangeFromToExpr &expr) override;
+  virtual void visit (RangeFromExpr &expr) override;
+  virtual void visit (RangeToExpr &expr) override;
+  virtual void visit (RangeFullExpr &expr) override;
+  virtual void visit (RangeFromToInclExpr &expr) override;
+  virtual void visit (RangeToInclExpr &expr) override;
+  virtual void visit (ReturnExpr &expr) override;
+  virtual void visit (UnsafeBlockExpr &expr) override;
+  virtual void visit (LoopExpr &expr) override;
+  virtual void visit (WhileLoopExpr &expr) override;
+  virtual void visit (WhileLetLoopExpr &expr) override;
+  virtual void visit (ForLoopExpr &expr) override;
+  virtual void visit (IfExpr &expr) override;
+  virtual void visit (IfExprConseqElse &expr) override;
+  virtual void visit (IfExprConseqIf &expr) override;
+  virtual void visit (IfExprConseqIfLet &expr) override;
+  virtual void visit (IfLetExpr &expr) override;
+  virtual void visit (IfLetExprConseqElse &expr) override;
+  virtual void visit (IfLetExprConseqIf &expr) override;
+  virtual void visit (IfLetExprConseqIfLet &expr) override;
+  virtual void visit (MatchExpr &expr) override;
+  virtual void visit (AwaitExpr &expr) override;
+  virtual void visit (AsyncBlockExpr &expr) override;
+  virtual void visit (TypeParam &param) override;
+  virtual void visit (ConstGenericParam &param) override;
+  virtual void visit (LifetimeWhereClauseItem &item) override;
+  virtual void visit (TypeBoundWhereClauseItem &item) override;
+  virtual void visit (Module &module) override;
+  virtual void visit (ExternCrate &crate) override;
+  virtual void visit (UseTreeGlob &use_tree) override;
+  virtual void visit (UseTreeList &use_tree) override;
+  virtual void visit (UseTreeRebind &use_tree) override;
+  virtual void visit (UseDeclaration &use_decl) override;
+  virtual void visit (Function &function) override;
+  virtual void visit (TypeAlias &type_alias) override;
+  virtual void visit (StructStruct &struct_item) override;
+  virtual void visit (TupleStruct &tuple_struct) override;
+  virtual void visit (EnumItem &item) override;
+  virtual void visit (EnumItemTuple &item) override;
+  virtual void visit (EnumItemStruct &item) override;
+  virtual void visit (EnumItemDiscriminant &item) override;
+  virtual void visit (Enum &enum_item) override;
+  virtual void visit (Union &union_item) override;
+  virtual void visit (ConstantItem &const_item) override;
+  virtual void visit (StaticItem &static_item) override;
+  virtual void visit (TraitItemFunc &item) override;
+  virtual void visit (TraitItemConst &item) override;
+  virtual void visit (TraitItemType &item) override;
+  virtual void visit (Trait &trait) override;
+  virtual void visit (ImplBlock &impl) override;
+  virtual void visit (ExternalStaticItem &item) override;
+  virtual void visit (ExternalFunctionItem &item) override;
+  virtual void visit (ExternBlock &block) override;
+  virtual void visit (LiteralPattern &pattern) override;
+  virtual void visit (IdentifierPattern &pattern) override;
+  virtual void visit (WildcardPattern &pattern) override;
+  virtual void visit (RangePatternBoundLiteral &bound) override;
+  virtual void visit (RangePatternBoundPath &bound) override;
+  virtual void visit (RangePatternBoundQualPath &bound) override;
+  virtual void visit (RangePattern &pattern) override;
+  virtual void visit (ReferencePattern &pattern) override;
+  virtual void visit (StructPatternFieldTuplePat &field) override;
+  virtual void visit (StructPatternFieldIdentPat &field) override;
+  virtual void visit (StructPatternFieldIdent &field) override;
+  virtual void visit (StructPattern &pattern) override;
+  virtual void visit (TupleStructItemsNoRange &tuple_items) override;
+  virtual void visit (TupleStructItemsRange &tuple_items) override;
+  virtual void visit (TupleStructPattern &pattern) override;
+  virtual void visit (TuplePatternItemsMultiple &tuple_items) override;
+  virtual void visit (TuplePatternItemsRanged &tuple_items) override;
+  virtual void visit (TuplePattern &pattern) override;
+  virtual void visit (GroupedPattern &pattern) override;
+  virtual void visit (SlicePattern &pattern) override;
+  virtual void visit (EmptyStmt &stmt) override;
+  virtual void visit (LetStmt &stmt) override;
+  virtual void visit (ExprStmtWithoutBlock &stmt) override;
+  virtual void visit (ExprStmtWithBlock &stmt) override;
+  virtual void visit (TraitBound &bound) override;
+  virtual void visit (ImplTraitType &type) override;
+  virtual void visit (TraitObjectType &type) override;
+  virtual void visit (ParenthesisedType &type) override;
+  virtual void visit (ImplTraitTypeOneBound &type) override;
+  virtual void visit (TupleType &type) override;
+  virtual void visit (NeverType &type) override;
+  virtual void visit (RawPointerType &type) override;
+  virtual void visit (ReferenceType &type) override;
+  virtual void visit (ArrayType &type) override;
+  virtual void visit (SliceType &type) override;
+  virtual void visit (InferredType &type) override;
+  virtual void visit (BareFunctionType &type) override;
+};
+
+} // namespace HIR
+} // namespace Rust
+
+#endif /* !RUST_CONST_CHECKER_H */
-- 
2.25.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [PATCH Rust front-end v2 25/37] gccrs: Add privacy checks
  2022-08-24 11:59 Rust frontend patches v2 herron.philip
                   ` (23 preceding siblings ...)
  2022-08-24 11:59 ` [PATCH Rust front-end v2 24/37] gccrs: Add const checker herron.philip
@ 2022-08-24 11:59 ` herron.philip
  2022-08-24 11:59 ` [PATCH Rust front-end v2 26/37] gccrs: Add dead code scan on HIR herron.philip
                   ` (12 subsequent siblings)
  37 siblings, 0 replies; 59+ messages in thread
From: herron.philip @ 2022-08-24 11:59 UTC (permalink / raw)
  To: gcc-patches; +Cc: gcc-rust, Arthur Cohen

From: Arthur Cohen <arthur.cohen@embecosm.com>

This pass is responsible for resolving the privacy of items and verifying
that access to these items is performed within the limits of that privacy.
By default, items in Rust are private and only public to the current
module and its submodules. However, the user can annotate an item with
various qualifiers such as `pub` to publicly expose an item. Furthermore,
a module path can be given to `pub` to restrict an item's privacy to a
certain module: These paths need to be resolved and later on checked by
the privacy error reporter.
---
 .../errors/privacy/rust-privacy-check.cc      |  63 ++
 .../errors/privacy/rust-privacy-check.h       |  44 +
 .../errors/privacy/rust-privacy-common.h      |  67 ++
 .../checks/errors/privacy/rust-privacy-ctx.cc |  93 +++
 .../checks/errors/privacy/rust-privacy-ctx.h  |  79 ++
 .../errors/privacy/rust-privacy-reporter.cc   | 753 ++++++++++++++++++
 .../errors/privacy/rust-privacy-reporter.h    | 173 ++++
 .../privacy/rust-pub-restricted-visitor.cc    | 182 +++++
 .../privacy/rust-pub-restricted-visitor.h     | 120 +++
 .../errors/privacy/rust-reachability.cc       | 236 ++++++
 .../checks/errors/privacy/rust-reachability.h |  87 ++
 .../privacy/rust-visibility-resolver.cc       | 245 ++++++
 .../errors/privacy/rust-visibility-resolver.h | 103 +++
 13 files changed, 2245 insertions(+)
 create mode 100644 gcc/rust/checks/errors/privacy/rust-privacy-check.cc
 create mode 100644 gcc/rust/checks/errors/privacy/rust-privacy-check.h
 create mode 100644 gcc/rust/checks/errors/privacy/rust-privacy-common.h
 create mode 100644 gcc/rust/checks/errors/privacy/rust-privacy-ctx.cc
 create mode 100644 gcc/rust/checks/errors/privacy/rust-privacy-ctx.h
 create mode 100644 gcc/rust/checks/errors/privacy/rust-privacy-reporter.cc
 create mode 100644 gcc/rust/checks/errors/privacy/rust-privacy-reporter.h
 create mode 100644 gcc/rust/checks/errors/privacy/rust-pub-restricted-visitor.cc
 create mode 100644 gcc/rust/checks/errors/privacy/rust-pub-restricted-visitor.h
 create mode 100644 gcc/rust/checks/errors/privacy/rust-reachability.cc
 create mode 100644 gcc/rust/checks/errors/privacy/rust-reachability.h
 create mode 100644 gcc/rust/checks/errors/privacy/rust-visibility-resolver.cc
 create mode 100644 gcc/rust/checks/errors/privacy/rust-visibility-resolver.h

diff --git a/gcc/rust/checks/errors/privacy/rust-privacy-check.cc b/gcc/rust/checks/errors/privacy/rust-privacy-check.cc
new file mode 100644
index 00000000000..9664d62f65c
--- /dev/null
+++ b/gcc/rust/checks/errors/privacy/rust-privacy-check.cc
@@ -0,0 +1,63 @@
+// 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/>.
+
+#include "rust-privacy-check.h"
+#include "rust-reachability.h"
+#include "rust-hir-type-check.h"
+#include "rust-hir-map.h"
+#include "rust-name-resolver.h"
+#include "rust-visibility-resolver.h"
+#include "rust-pub-restricted-visitor.h"
+#include "rust-privacy-reporter.h"
+
+extern bool
+saw_errors (void);
+
+namespace Rust {
+namespace Privacy {
+
+void
+Resolver::resolve (HIR::Crate &crate)
+{
+  PrivacyContext ctx;
+  auto mappings = Analysis::Mappings::get ();
+  auto resolver = Rust::Resolver::Resolver::get ();
+  auto ty_ctx = ::Rust::Resolver::TypeCheckContext::get ();
+
+  VisibilityResolver (*mappings, *resolver).go (crate);
+  PubRestrictedVisitor (*mappings).go (crate);
+  PrivacyReporter (*mappings, *resolver, *ty_ctx).go (crate);
+
+  auto visitor = ReachabilityVisitor (ctx, *ty_ctx);
+
+  const auto &items = crate.items;
+
+  for (auto &item : items)
+    {
+      if (item->get_hir_kind () == HIR::Node::VIS_ITEM)
+	{
+	  auto vis_item = static_cast<HIR::VisItem *> (item.get ());
+	  vis_item->accept_vis (visitor);
+	}
+    }
+
+  if (saw_errors ())
+    return;
+}
+} // namespace Privacy
+} // namespace Rust
diff --git a/gcc/rust/checks/errors/privacy/rust-privacy-check.h b/gcc/rust/checks/errors/privacy/rust-privacy-check.h
new file mode 100644
index 00000000000..290b5eacb6c
--- /dev/null
+++ b/gcc/rust/checks/errors/privacy/rust-privacy-check.h
@@ -0,0 +1,44 @@
+// 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_PRIVACY_CHECK_H
+#define RUST_PRIVACY_CHECK_H
+
+#include "rust-hir.h"
+#include "rust-hir-expr.h"
+#include "rust-hir-stmt.h"
+#include "rust-hir-item.h"
+#include "rust-hir-type-check.h"
+
+namespace Rust {
+namespace Privacy {
+class Resolver
+{
+public:
+  /**
+   * Perform the full privacy resolving pass on a crate.
+   *
+   * This resolver first computes the reachability of all items in a crate,
+   * before checking for privacy violations.
+   */
+  static void resolve (HIR::Crate &crate);
+};
+} // namespace Privacy
+} // namespace Rust
+
+#endif // !RUST_PRIVACY_CHECK_H
diff --git a/gcc/rust/checks/errors/privacy/rust-privacy-common.h b/gcc/rust/checks/errors/privacy/rust-privacy-common.h
new file mode 100644
index 00000000000..ceafe91d886
--- /dev/null
+++ b/gcc/rust/checks/errors/privacy/rust-privacy-common.h
@@ -0,0 +1,67 @@
+// 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/>.
+
+#include "rust-mapping-common.h"
+
+namespace Rust {
+namespace Privacy {
+
+/**
+ * Visibility class related specifically to DefIds. This class allows defining
+ * the visibility of an item with regard to a specific module.
+ *
+ * Items are either public throughout a crate, or restricted to a specific
+ * module. Private items are simply restricted to the current module.
+ */
+class ModuleVisibility
+{
+public:
+  enum Type
+  {
+    Unknown,
+    Public,
+    Restricted,
+  };
+
+  ModuleVisibility () : kind (Unknown), module_id (UNKNOWN_DEFID) {}
+
+  static ModuleVisibility create_restricted (DefId module_id)
+  {
+    return ModuleVisibility (Type::Restricted, module_id);
+  }
+
+  static ModuleVisibility create_public ()
+  {
+    return ModuleVisibility (Type::Public, UNKNOWN_DEFID);
+  }
+
+  Type get_kind () const { return kind; }
+
+  const DefId &get_module_id () const { return module_id; }
+  DefId &get_module_id () { return module_id; }
+
+private:
+  ModuleVisibility (Type kind, DefId module_id)
+    : kind (kind), module_id (module_id)
+  {}
+
+  Type kind;
+  DefId module_id;
+};
+} // namespace Privacy
+} // namespace Rust
diff --git a/gcc/rust/checks/errors/privacy/rust-privacy-ctx.cc b/gcc/rust/checks/errors/privacy/rust-privacy-ctx.cc
new file mode 100644
index 00000000000..9ebc86988e9
--- /dev/null
+++ b/gcc/rust/checks/errors/privacy/rust-privacy-ctx.cc
@@ -0,0 +1,93 @@
+// 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/>.
+
+#include "rust-privacy-ctx.h"
+#include "selftest.h"
+
+namespace Rust {
+namespace Privacy {
+
+static ReachLevel
+insert_if_higher (ReachLevel new_level,
+		  std::unordered_map<DefId, ReachLevel>::iterator &existing)
+{
+  if (new_level > existing->second)
+    existing->second = new_level;
+
+  return existing->second;
+}
+
+ReachLevel
+PrivacyContext::update_reachability (const Analysis::NodeMapping &mapping,
+				     ReachLevel reach)
+{
+  auto def_id = mapping.get_defid ();
+  auto existing_reach = reachability_map.find (def_id);
+  if (existing_reach != reachability_map.end ())
+    return insert_if_higher (reach, existing_reach);
+
+  reachability_map.insert ({def_id, reach});
+  return reach;
+}
+
+const ReachLevel *
+PrivacyContext::lookup_reachability (const Analysis::NodeMapping &mapping)
+{
+  auto existing_reach = reachability_map.find (mapping.get_defid ());
+  if (existing_reach == reachability_map.end ())
+    return nullptr;
+
+  return &existing_reach->second;
+}
+} // namespace Privacy
+} // namespace Rust
+
+#if CHECKING_P
+namespace selftest {
+static void
+update_reachability_test (void)
+{
+  auto ctx = Rust::Privacy::PrivacyContext ();
+  // Bogus values for the mappings
+  auto mapping = Rust::Analysis::NodeMapping (15, 15, 15, 15);
+
+  auto new_level
+    = ctx.update_reachability (mapping, Rust::Privacy::ReachLevel::Unreachable);
+
+  ASSERT_EQ (new_level, Rust::Privacy::ReachLevel::Unreachable);
+
+  ASSERT_TRUE (ctx.lookup_reachability (mapping));
+  ASSERT_EQ (*ctx.lookup_reachability (mapping),
+	     Rust::Privacy::ReachLevel::Unreachable);
+
+  new_level
+    = ctx.update_reachability (mapping, Rust::Privacy::ReachLevel::Reachable);
+
+  ASSERT_EQ (new_level, Rust::Privacy::ReachLevel::Reachable);
+  ASSERT_TRUE (ctx.lookup_reachability (mapping));
+  ASSERT_EQ (*ctx.lookup_reachability (mapping),
+	     Rust::Privacy::ReachLevel::Reachable);
+}
+
+void
+rust_privacy_ctx_test (void)
+{
+  update_reachability_test ();
+}
+} // namespace selftest
+#endif // !CHECKING_P
diff --git a/gcc/rust/checks/errors/privacy/rust-privacy-ctx.h b/gcc/rust/checks/errors/privacy/rust-privacy-ctx.h
new file mode 100644
index 00000000000..52d790edf63
--- /dev/null
+++ b/gcc/rust/checks/errors/privacy/rust-privacy-ctx.h
@@ -0,0 +1,79 @@
+// 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_PRIVACY_CTX_H
+#define RUST_PRIVACY_CTX_H
+
+#include "rust-hir-map.h"
+#include "rust-privacy-check.h"
+
+namespace Rust {
+namespace Privacy {
+
+/**
+ * Reachability levels of HIR nodes. These levels are computed through the
+ * `ReachabilityVisitor` visitor.
+ */
+enum ReachLevel
+{
+  Unreachable,
+  Reachable,
+};
+
+class PrivacyContext
+{
+public:
+  /**
+   * Insert a new resolved visibility for a given node. If the node is already
+   * present in the reachability map, then its visibility will only be updated
+   * if the given visibility is higher.
+   *
+   * @param mappings Mappings of the node to store the reach level for
+   * @param reach Level of reachability for the given node
+   *
+   * @return The new reachability level for this node. If this was the first
+   * time inserting this node, then return `reach`. Otherwise, return `reach` or
+   * the existing reach level if it was higher.
+   */
+  ReachLevel update_reachability (const Analysis::NodeMapping &mapping,
+				  ReachLevel reach);
+
+  /**
+   * Lookup the visibility of an already declared Node
+   *
+   * @param mapping Mappings of the node to fetch the reach level of
+   *
+   * @return `nullptr` if the reach level for the current node has not been
+   * added, a valid pointer otherwise
+   */
+  const ReachLevel *lookup_reachability (const Analysis::NodeMapping &mapping);
+
+private:
+  std::unordered_map<DefId, ReachLevel> reachability_map;
+};
+} // namespace Privacy
+} // namespace Rust
+
+#if CHECKING_P
+namespace selftest {
+void
+rust_privacy_ctx_test (void);
+}
+#endif // !CHECKING_P
+
+#endif // !RUST_PRIVACY_CTX_H
diff --git a/gcc/rust/checks/errors/privacy/rust-privacy-reporter.cc b/gcc/rust/checks/errors/privacy/rust-privacy-reporter.cc
new file mode 100644
index 00000000000..35fde40782e
--- /dev/null
+++ b/gcc/rust/checks/errors/privacy/rust-privacy-reporter.cc
@@ -0,0 +1,753 @@
+#include "rust-privacy-reporter.h"
+#include "rust-hir-expr.h"
+#include "rust-hir-stmt.h"
+#include "rust-hir-item.h"
+
+namespace Rust {
+namespace Privacy {
+
+PrivacyReporter::PrivacyReporter (
+  Analysis::Mappings &mappings, Resolver::Resolver &resolver,
+  const Rust::Resolver::TypeCheckContext &ty_ctx)
+  : mappings (mappings), resolver (resolver), ty_ctx (ty_ctx),
+    current_module (Optional<NodeId>::none ())
+{}
+
+void
+PrivacyReporter::go (HIR::Crate &crate)
+{
+  for (auto &item : crate.items)
+    item->accept_vis (*this);
+}
+
+static bool
+is_child_module (Analysis::Mappings &mappings, NodeId parent,
+		 NodeId possible_child)
+{
+  auto children = mappings.lookup_module_children (parent);
+
+  if (!children)
+    return false;
+
+  // Visit all toplevel children
+  for (auto &child : *children)
+    if (child == possible_child)
+      return true;
+
+  // Now descend recursively in the child module tree
+  for (auto &child : *children)
+    if (is_child_module (mappings, child, possible_child))
+      return true;
+
+  return false;
+}
+
+// FIXME: This function needs a lot of refactoring
+void
+PrivacyReporter::check_for_privacy_violation (const NodeId &use_id,
+					      const Location &locus)
+{
+  NodeId ref_node_id = UNKNOWN_NODEID;
+
+  // FIXME: Don't assert here - we might be dealing with a type
+  if (!resolver.lookup_resolved_name (use_id, &ref_node_id))
+    resolver.lookup_resolved_type (use_id, &ref_node_id);
+
+  // FIXME: Assert here. For now, we return since this causes issues when
+  // checking inferred types (#1260)
+  // rust_assert (ref_node_id != UNKNOWN_NODEID);
+  if (ref_node_id == UNKNOWN_NODEID)
+    return;
+
+  ModuleVisibility vis;
+
+  // FIXME: Can we really return here if the item has no visibility?
+  if (!mappings.lookup_visibility (ref_node_id, vis))
+    return;
+
+  auto valid = true;
+
+  switch (vis.get_kind ())
+    {
+    case ModuleVisibility::Public:
+      break;
+      case ModuleVisibility::Restricted: {
+	// If we are in the crate, everything is restricted correctly, but we
+	// can't get a module for it
+	if (current_module.is_none ())
+	  return;
+
+	auto module = mappings.lookup_defid (vis.get_module_id ());
+	rust_assert (module != nullptr);
+
+	auto mod_node_id = module->get_mappings ().get_nodeid ();
+
+	// We are in the module referenced by the pub(restricted) visibility.
+	// This is valid
+	if (mod_node_id == current_module.get ())
+	  break;
+
+	// FIXME: This needs a LOT of TLC: hinting about the definition, a
+	// string to say if it's a module, function, type, etc...
+	if (!is_child_module (mappings, mod_node_id, current_module.get ()))
+	  valid = false;
+      }
+      break;
+    case ModuleVisibility::Unknown:
+      rust_unreachable ();
+      break;
+    }
+
+  if (!valid)
+    rust_error_at (locus, "definition is private in this context");
+}
+
+void
+PrivacyReporter::check_base_type_privacy (Analysis::NodeMapping &node_mappings,
+					  const TyTy::BaseType *ty,
+					  const Location &locus)
+{
+  // Avoids repeating commong argument such as `use_id` or `locus` since we're
+  // doing a lot of recursive calls here
+  auto recursive_check
+    = [this, &node_mappings, &locus] (const TyTy::BaseType *ty) {
+	return check_base_type_privacy (node_mappings, ty, locus);
+      };
+
+  switch (ty->get_kind ())
+    {
+      // These "simple" types are our stop condition
+    case TyTy::BOOL:
+    case TyTy::CHAR:
+    case TyTy::INT:
+    case TyTy::UINT:
+    case TyTy::FLOAT:
+    case TyTy::USIZE:
+    case TyTy::ISIZE:
+    case TyTy::ADT:
+      case TyTy::STR: {
+	auto ref_id = ty->get_ref ();
+	NodeId lookup_id;
+
+	bool ok = mappings.lookup_hir_to_node (ref_id, &lookup_id);
+	rust_assert (ok);
+
+	return check_for_privacy_violation (lookup_id, locus);
+      }
+    case TyTy::REF:
+      return recursive_check (
+	static_cast<const TyTy::ReferenceType *> (ty)->get_base ());
+    case TyTy::POINTER:
+      return recursive_check (
+	static_cast<const TyTy::PointerType *> (ty)->get_base ());
+    case TyTy::ARRAY:
+      return recursive_check (
+	static_cast<const TyTy::ArrayType *> (ty)->get_element_type ());
+    case TyTy::SLICE:
+      return recursive_check (
+	static_cast<const TyTy::SliceType *> (ty)->get_element_type ());
+    case TyTy::FNPTR:
+      for (auto &param : static_cast<const TyTy::FnPtr *> (ty)->get_params ())
+	recursive_check (param.get_tyty ());
+      return recursive_check (
+	static_cast<const TyTy::FnPtr *> (ty)->get_return_type ());
+    case TyTy::TUPLE:
+      for (auto &param :
+	   static_cast<const TyTy::TupleType *> (ty)->get_fields ())
+	recursive_check (param.get_tyty ());
+      return;
+    case TyTy::PLACEHOLDER:
+      return recursive_check (
+	// FIXME: Can we use `resolve` here? Is that what we should do?
+	static_cast<const TyTy::PlaceholderType *> (ty)->resolve ());
+    case TyTy::PROJECTION:
+      return recursive_check (
+	static_cast<const TyTy::ProjectionType *> (ty)->get ());
+    case TyTy::CLOSURE:
+      rust_sorry_at (locus, "privacy pass for closures is not handled yet");
+      break;
+
+      // If we're dealing with a generic param, there's nothing we should be
+      // doing here
+    case TyTy::PARAM:
+      // We are dealing with a function definition that has been assigned
+      // somewhere else. Nothing to resolve privacy-wise other than the actual
+      // function, which is resolved as an expression
+    case TyTy::FNDEF:
+      // FIXME: Can we really not resolve Dynamic types here? Shouldn't we have
+      // a look at the path and perform proper privacy analysis?
+    case TyTy::DYNAMIC:
+      // The never type is builtin and always available
+    case TyTy::NEVER:
+      // We shouldn't have inference types here, ever
+    case TyTy::INFER:
+      return;
+    case TyTy::ERROR:
+      rust_unreachable ();
+    }
+}
+
+void
+PrivacyReporter::check_type_privacy (const HIR::Type *type)
+{
+  rust_assert (type);
+
+  TyTy::BaseType *lookup = nullptr;
+  rust_assert (
+    ty_ctx.lookup_type (type->get_mappings ().get_hirid (), &lookup));
+
+  auto node_mappings = type->get_mappings ();
+  return check_base_type_privacy (node_mappings, lookup, type->get_locus ());
+}
+
+void
+PrivacyReporter::visit (HIR::PathInExpression &path)
+{
+  check_for_privacy_violation (path.get_mappings ().get_nodeid (),
+			       path.get_locus ());
+}
+
+void
+PrivacyReporter::visit (HIR::TypePathSegmentFunction &segment)
+{
+  // FIXME: Do we need to do anything for this?
+}
+
+void
+PrivacyReporter::visit (HIR::TypePath &path)
+{
+  check_for_privacy_violation (path.get_mappings ().get_nodeid (),
+			       path.get_locus ());
+}
+
+void
+PrivacyReporter::visit (HIR::QualifiedPathInExpression &path)
+{
+  check_for_privacy_violation (path.get_mappings ().get_nodeid (),
+			       path.get_locus ());
+}
+
+void
+PrivacyReporter::visit (HIR::QualifiedPathInType &path)
+{
+  check_for_privacy_violation (path.get_mappings ().get_nodeid (),
+			       path.get_locus ());
+}
+
+void
+PrivacyReporter::visit (HIR::LiteralExpr &expr)
+{
+  // Literals cannot contain any sort of privacy violation
+}
+
+void
+PrivacyReporter::visit (HIR::BorrowExpr &expr)
+{
+  expr.get_expr ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::DereferenceExpr &expr)
+{
+  expr.get_expr ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::ErrorPropagationExpr &expr)
+{
+  expr.get_expr ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::NegationExpr &expr)
+{
+  expr.get_expr ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::ArithmeticOrLogicalExpr &expr)
+{
+  expr.get_lhs ()->accept_vis (*this);
+  expr.get_rhs ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::ComparisonExpr &expr)
+{
+  expr.get_lhs ()->accept_vis (*this);
+  expr.get_rhs ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::LazyBooleanExpr &expr)
+{
+  expr.get_lhs ()->accept_vis (*this);
+  expr.get_rhs ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::TypeCastExpr &expr)
+{
+  expr.get_expr ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::AssignmentExpr &expr)
+{
+  expr.get_lhs ()->accept_vis (*this);
+  expr.get_rhs ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::CompoundAssignmentExpr &expr)
+{
+  expr.get_left_expr ()->accept_vis (*this);
+  expr.get_right_expr ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::GroupedExpr &expr)
+{
+  expr.get_expr_in_parens ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::ArrayExpr &expr)
+{
+  HIR::ArrayElems &elements = *expr.get_internal_elements ();
+  switch (elements.get_array_expr_type ())
+    {
+      case HIR::ArrayElems::ArrayExprType::VALUES: {
+	HIR::ArrayElemsValues &elems
+	  = static_cast<HIR::ArrayElemsValues &> (elements);
+	for (auto &value : elems.get_values ())
+	  value->accept_vis (*this);
+      }
+      return;
+
+    case HIR::ArrayElems::ArrayExprType::COPIED:
+      HIR::ArrayElemsCopied &elems
+	= static_cast<HIR::ArrayElemsCopied &> (elements);
+      elems.get_elem_to_copy ()->accept_vis (*this);
+    }
+}
+
+void
+PrivacyReporter::visit (HIR::ArrayIndexExpr &expr)
+{
+  expr.get_array_expr ()->accept_vis (*this);
+  expr.get_index_expr ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::TupleExpr &expr)
+{
+  for (auto &value : expr.get_tuple_elems ())
+    value->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::TupleIndexExpr &expr)
+{
+  expr.get_tuple_expr ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::StructExprStruct &expr)
+{
+  // FIXME: We need to check the visibility of the type it refers to here
+}
+
+void
+PrivacyReporter::visit (HIR::StructExprFieldIdentifier &field)
+{}
+
+void
+PrivacyReporter::visit (HIR::StructExprFieldIdentifierValue &field)
+{
+  field.get_value ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::StructExprFieldIndexValue &field)
+{
+  field.get_value ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::StructExprStructFields &expr)
+{
+  for (auto &field : expr.get_fields ())
+    field->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::CallExpr &expr)
+{
+  expr.get_fnexpr ()->accept_vis (*this);
+
+  for (auto &param : expr.get_arguments ())
+    param->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::MethodCallExpr &expr)
+{
+  expr.get_receiver ()->accept_vis (*this);
+
+  for (auto &param : expr.get_arguments ())
+    param->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::FieldAccessExpr &expr)
+{
+  expr.get_receiver_expr ()->accept_vis (*this);
+
+  // FIXME: We should also check if the field is public?
+}
+
+void
+PrivacyReporter::visit (HIR::ClosureExprInner &expr)
+{
+  // Not handled yet
+}
+
+void
+PrivacyReporter::visit (HIR::BlockExpr &expr)
+{
+  for (auto &stmt : expr.get_statements ())
+    stmt->accept_vis (*this);
+
+  auto &last_expr = expr.get_final_expr ();
+  if (last_expr)
+    last_expr->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::ClosureExprInnerTyped &expr)
+{
+  // Not handled yet
+}
+
+void
+PrivacyReporter::visit (HIR::ContinueExpr &expr)
+{}
+
+void
+PrivacyReporter::visit (HIR::BreakExpr &expr)
+{
+  auto &break_expr = expr.get_expr ();
+  if (break_expr)
+    break_expr->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::RangeFromToExpr &expr)
+{
+  expr.get_from_expr ()->accept_vis (*this);
+  expr.get_to_expr ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::RangeFromExpr &expr)
+{
+  expr.get_from_expr ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::RangeToExpr &expr)
+{
+  expr.get_to_expr ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::RangeFullExpr &expr)
+{}
+
+void
+PrivacyReporter::visit (HIR::RangeFromToInclExpr &expr)
+{
+  expr.get_from_expr ()->accept_vis (*this);
+  expr.get_to_expr ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::RangeToInclExpr &expr)
+{
+  // Not handled yet
+}
+
+void
+PrivacyReporter::visit (HIR::ReturnExpr &expr)
+{
+  auto return_expr = expr.get_expr ();
+  if (return_expr)
+    return_expr->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::UnsafeBlockExpr &expr)
+{
+  expr.get_block_expr ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::LoopExpr &expr)
+{
+  expr.get_loop_block ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::WhileLoopExpr &expr)
+{
+  expr.get_predicate_expr ()->accept_vis (*this);
+  expr.get_loop_block ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::WhileLetLoopExpr &expr)
+{
+  expr.get_cond ()->accept_vis (*this);
+  expr.get_loop_block ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::ForLoopExpr &expr)
+{
+  expr.get_iterator_expr ()->accept_vis (*this);
+  expr.get_loop_block ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::IfExpr &expr)
+{
+  expr.get_if_condition ()->accept_vis (*this);
+  expr.get_if_block ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::IfExprConseqElse &expr)
+{
+  expr.get_if_condition ()->accept_vis (*this);
+  expr.get_if_block ()->accept_vis (*this);
+  expr.get_else_block ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::IfExprConseqIf &expr)
+{
+  expr.get_if_condition ()->accept_vis (*this);
+  expr.get_if_block ()->accept_vis (*this);
+  expr.get_conseq_if_expr ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::IfExprConseqIfLet &expr)
+{
+  expr.get_if_condition ()->accept_vis (*this);
+  expr.get_if_block ()->accept_vis (*this);
+
+  // TODO: We need to visit the if_let_expr as well
+}
+
+void
+PrivacyReporter::visit (HIR::IfLetExpr &expr)
+{
+  // TODO: We need to visit the if_let_expr
+  // TODO: We need to visit the block as well
+}
+
+void
+PrivacyReporter::visit (HIR::IfLetExprConseqElse &expr)
+{
+  // TODO: We need to visit the if_let_expr
+  // TODO: We need to visit the if_block as well
+  // TODO: We need to visit the else_block as well
+}
+
+void
+PrivacyReporter::visit (HIR::IfLetExprConseqIf &expr)
+{
+  // TODO: We need to visit the if_let_expr
+  // TODO: We need to visit the if_block as well
+  // TODO: We need to visit the else_block as well
+}
+
+void
+PrivacyReporter::visit (HIR::IfLetExprConseqIfLet &expr)
+{
+  // TODO: We need to visit the if_let_expr
+  // TODO: We need to visit the if_block as well
+  // TODO: We need to visit the else_block as well
+}
+
+void
+PrivacyReporter::visit (HIR::MatchExpr &expr)
+{
+  expr.get_scrutinee_expr ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::AwaitExpr &expr)
+{
+  // Not handled yet
+}
+
+void
+PrivacyReporter::visit (HIR::AsyncBlockExpr &expr)
+{
+  // Not handled yet
+}
+
+void
+PrivacyReporter::visit (HIR::Module &module)
+{
+  // FIXME: We also need to think about module privacy
+
+  auto old_module = current_module;
+  current_module
+    = Optional<NodeId>::some (module.get_mappings ().get_nodeid ());
+
+  for (auto &item : module.get_items ())
+    item->accept_vis (*this);
+
+  current_module = old_module;
+}
+
+void
+PrivacyReporter::visit (HIR::ExternCrate &crate)
+{}
+
+void
+PrivacyReporter::visit (HIR::UseDeclaration &use_decl)
+{
+  // FIXME: Is there anything we need to do here?
+}
+
+void
+PrivacyReporter::visit (HIR::Function &function)
+{
+  for (auto &param : function.get_function_params ())
+    check_type_privacy (param.get_type ());
+
+  function.get_definition ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::TypeAlias &type_alias)
+{
+  // TODO: Check the type here
+}
+
+void
+PrivacyReporter::visit (HIR::StructStruct &struct_item)
+{
+  // TODO: Check the type of all fields
+}
+
+void
+PrivacyReporter::visit (HIR::TupleStruct &tuple_struct)
+{
+  // TODO: Check the type of all fields
+}
+
+void
+PrivacyReporter::visit (HIR::EnumItem &item)
+{
+  // TODO: Check the type of all variants
+}
+
+void
+PrivacyReporter::visit (HIR::EnumItemTuple &item)
+{
+  // TODO: Check the type
+}
+
+void
+PrivacyReporter::visit (HIR::EnumItemStruct &item)
+{
+  // TODO: Check the type
+}
+
+void
+PrivacyReporter::visit (HIR::EnumItemDiscriminant &item)
+{}
+
+void
+PrivacyReporter::visit (HIR::Enum &enum_item)
+{}
+
+void
+PrivacyReporter::visit (HIR::Union &union_item)
+{
+  // TODO: Check the type
+}
+
+void
+PrivacyReporter::visit (HIR::ConstantItem &const_item)
+{
+  // TODO: We need to visit the type
+  const_item.get_expr ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::StaticItem &static_item)
+{
+  // TODO: We need to visit the type
+  static_item.get_expr ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::Trait &trait)
+{
+  // FIXME: We need to be an ItemVisitor as well
+  // for (auto &item : trait.get_trait_items ())
+  //   item->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::ImplBlock &impl)
+{
+  for (auto &item : impl.get_impl_items ())
+    item->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::ExternBlock &block)
+{
+  // FIXME: We need to be an ItemVisitor as well
+  // for (auto &item : block.get_extern_items ())
+  //   item->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::EmptyStmt &stmt)
+{}
+
+void
+PrivacyReporter::visit (HIR::LetStmt &stmt)
+{
+  auto type = stmt.get_type ();
+  if (type)
+    check_type_privacy (type);
+
+  auto init_expr = stmt.get_init_expr ();
+  if (init_expr)
+    init_expr->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::ExprStmtWithoutBlock &stmt)
+{
+  stmt.get_expr ()->accept_vis (*this);
+}
+
+void
+PrivacyReporter::visit (HIR::ExprStmtWithBlock &stmt)
+{
+  stmt.get_expr ()->accept_vis (*this);
+}
+
+} // namespace Privacy
+} // namespace Rust
diff --git a/gcc/rust/checks/errors/privacy/rust-privacy-reporter.h b/gcc/rust/checks/errors/privacy/rust-privacy-reporter.h
new file mode 100644
index 00000000000..546b108f36d
--- /dev/null
+++ b/gcc/rust/checks/errors/privacy/rust-privacy-reporter.h
@@ -0,0 +1,173 @@
+// 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_PRIVACY_REPORTER_H
+#define RUST_PRIVACY_REPORTER_H
+
+#include "rust-hir-map.h"
+#include "rust-hir-visitor.h"
+#include "rust-mapping-common.h"
+#include "rust-name-resolver.h"
+
+namespace Rust {
+namespace Privacy {
+
+/**
+ * This visitor visits all items and expressions of a crate and reports privacy
+ * violations. It should be started after using the `VisibilityResolver` visitor
+ * which resolves the visibilities of all items of a crate.
+ */
+class PrivacyReporter : public HIR::HIRExpressionVisitor,
+			public HIR::HIRStmtVisitor
+{
+public:
+  PrivacyReporter (Analysis::Mappings &mappings,
+		   Rust::Resolver::Resolver &resolver,
+		   const Rust::Resolver::TypeCheckContext &ty_ctx);
+
+  /**
+   * Perform privacy error reporting on an entire crate
+   */
+  void go (HIR::Crate &crate);
+
+private:
+  /**
+   * Check if a given item's visibility is accessible from the current module.
+   *
+   * This function reports the errors it finds.
+   *
+   * @param use_id NodeId of the expression/statement referencing an item with
+   * 		a visibility
+   * @param locus Location of said expression/statement
+   */
+  void check_for_privacy_violation (const NodeId &use_id,
+				    const Location &locus);
+
+  /**
+   * Internal function used by `check_type_privacy` when dealing with complex
+types
+   * such as references or arrays
+   */
+  void check_base_type_privacy (Analysis::NodeMapping &node_mappings,
+				const TyTy::BaseType *ty,
+				const Location &locus);
+
+  /**
+   * Check the privacy of an explicit type.
+   *
+   * This function reports the errors it finds.
+   *
+   * @param type Reference to an explicit type used in a statement, expression
+   * 		or parameter
+   */
+  void check_type_privacy (const HIR::Type *type);
+
+  virtual void visit (HIR::StructExprFieldIdentifier &field);
+  virtual void visit (HIR::StructExprFieldIdentifierValue &field);
+  virtual void visit (HIR::StructExprFieldIndexValue &field);
+
+  virtual void visit (HIR::QualifiedPathInExpression &expr);
+  virtual void visit (HIR::PathInExpression &expr);
+  virtual void visit (HIR::ClosureExprInnerTyped &);
+  virtual void visit (HIR::ClosureExprInner &expr);
+  virtual void visit (HIR::StructExprStructFields &);
+  virtual void visit (HIR::StructExprStruct &);
+  virtual void visit (HIR::LiteralExpr &expr);
+  virtual void visit (HIR::BorrowExpr &expr);
+  virtual void visit (HIR::DereferenceExpr &expr);
+  virtual void visit (HIR::ErrorPropagationExpr &expr);
+  virtual void visit (HIR::NegationExpr &expr);
+  virtual void visit (HIR::ArithmeticOrLogicalExpr &expr);
+  virtual void visit (HIR::ComparisonExpr &expr);
+  virtual void visit (HIR::LazyBooleanExpr &expr);
+  virtual void visit (HIR::TypeCastExpr &expr);
+  virtual void visit (HIR::AssignmentExpr &expr);
+  virtual void visit (HIR::CompoundAssignmentExpr &expr);
+  virtual void visit (HIR::GroupedExpr &expr);
+  virtual void visit (HIR::ArrayExpr &expr);
+  virtual void visit (HIR::ArrayIndexExpr &expr);
+  virtual void visit (HIR::TupleExpr &expr);
+  virtual void visit (HIR::TupleIndexExpr &expr);
+  virtual void visit (HIR::CallExpr &expr);
+  virtual void visit (HIR::MethodCallExpr &expr);
+  virtual void visit (HIR::FieldAccessExpr &expr);
+  virtual void visit (HIR::BlockExpr &expr);
+  virtual void visit (HIR::ContinueExpr &expr);
+  virtual void visit (HIR::BreakExpr &expr);
+  virtual void visit (HIR::RangeFromToExpr &expr);
+  virtual void visit (HIR::RangeFromExpr &expr);
+  virtual void visit (HIR::RangeToExpr &expr);
+  virtual void visit (HIR::RangeFullExpr &expr);
+  virtual void visit (HIR::RangeFromToInclExpr &expr);
+  virtual void visit (HIR::RangeToInclExpr &expr);
+  virtual void visit (HIR::ReturnExpr &expr);
+  virtual void visit (HIR::UnsafeBlockExpr &expr);
+  virtual void visit (HIR::LoopExpr &expr);
+  virtual void visit (HIR::WhileLoopExpr &expr);
+  virtual void visit (HIR::WhileLetLoopExpr &expr);
+  virtual void visit (HIR::ForLoopExpr &expr);
+  virtual void visit (HIR::IfExpr &expr);
+  virtual void visit (HIR::IfExprConseqElse &expr);
+  virtual void visit (HIR::IfExprConseqIf &expr);
+  virtual void visit (HIR::IfExprConseqIfLet &expr);
+  virtual void visit (HIR::IfLetExpr &expr);
+  virtual void visit (HIR::IfLetExprConseqElse &expr);
+  virtual void visit (HIR::IfLetExprConseqIf &expr);
+  virtual void visit (HIR::IfLetExprConseqIfLet &expr);
+  virtual void visit (HIR::MatchExpr &expr);
+  virtual void visit (HIR::AwaitExpr &expr);
+  virtual void visit (HIR::AsyncBlockExpr &expr);
+
+  virtual void visit (HIR::EnumItemTuple &);
+  virtual void visit (HIR::EnumItemStruct &);
+  virtual void visit (HIR::EnumItem &item);
+  virtual void visit (HIR::TupleStruct &tuple_struct);
+  virtual void visit (HIR::EnumItemDiscriminant &);
+  virtual void visit (HIR::TypePathSegmentFunction &segment);
+  virtual void visit (HIR::TypePath &path);
+  virtual void visit (HIR::QualifiedPathInType &path);
+  virtual void visit (HIR::Module &module);
+  virtual void visit (HIR::ExternCrate &crate);
+  virtual void visit (HIR::UseDeclaration &use_decl);
+  virtual void visit (HIR::Function &function);
+  virtual void visit (HIR::TypeAlias &type_alias);
+  virtual void visit (HIR::StructStruct &struct_item);
+  virtual void visit (HIR::Enum &enum_item);
+  virtual void visit (HIR::Union &union_item);
+  virtual void visit (HIR::ConstantItem &const_item);
+  virtual void visit (HIR::StaticItem &static_item);
+  virtual void visit (HIR::Trait &trait);
+  virtual void visit (HIR::ImplBlock &impl);
+  virtual void visit (HIR::ExternBlock &block);
+  virtual void visit (HIR::EmptyStmt &stmt);
+  virtual void visit (HIR::LetStmt &stmt);
+  virtual void visit (HIR::ExprStmtWithoutBlock &stmt);
+  virtual void visit (HIR::ExprStmtWithBlock &stmt);
+
+  Analysis::Mappings &mappings;
+  Rust::Resolver::Resolver &resolver;
+  const Rust::Resolver::TypeCheckContext &ty_ctx;
+
+  // `None` means we're in the root module - the crate
+  Optional<NodeId> current_module;
+};
+
+} // namespace Privacy
+} // namespace Rust
+
+#endif // !RUST_PRIVACY_REPORTER_H
diff --git a/gcc/rust/checks/errors/privacy/rust-pub-restricted-visitor.cc b/gcc/rust/checks/errors/privacy/rust-pub-restricted-visitor.cc
new file mode 100644
index 00000000000..e391653ea26
--- /dev/null
+++ b/gcc/rust/checks/errors/privacy/rust-pub-restricted-visitor.cc
@@ -0,0 +1,182 @@
+// 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/>.
+
+#include "rust-pub-restricted-visitor.h"
+#include "rust-hir.h"
+#include "rust-hir-item.h"
+
+namespace Rust {
+namespace Privacy {
+
+bool
+PubRestrictedVisitor::is_restriction_valid (NodeId item_id,
+					    const Location &locus)
+{
+  ModuleVisibility visibility;
+
+  // If there is no visibility in the mappings, then the item is private and
+  // does not contain any restriction
+  // FIXME: Is that correct?
+  if (!mappings.lookup_visibility (item_id, visibility))
+    return true;
+
+  for (auto mod = module_stack.rbegin (); mod != module_stack.rend (); mod++)
+    if (*mod == visibility.get_module_id ())
+      return true;
+
+  rust_error_at (locus, "restricted path is not an ancestor of the "
+			"current module");
+  return false;
+}
+
+PubRestrictedVisitor::PubRestrictedVisitor (Analysis::Mappings &mappings)
+  : mappings (mappings)
+{}
+
+void
+PubRestrictedVisitor::go (HIR::Crate &crate)
+{
+  // The `crate` module will always be present
+  module_stack.emplace_back (crate.get_mappings ().get_defid ());
+
+  // FIXME: When do we insert `super`? `self`?
+  // We need wrapper function for these
+
+  for (auto &item : crate.items)
+    {
+      if (item->get_hir_kind () == HIR::Node::VIS_ITEM)
+	{
+	  auto vis_item = static_cast<HIR::VisItem *> (item.get ());
+	  vis_item->accept_vis (*this);
+	}
+    }
+}
+
+void
+PubRestrictedVisitor::visit (HIR::Module &mod)
+{
+  // FIXME: We need to update `super` and `self` here
+  module_stack.push_back (mod.get_mappings ().get_defid ());
+
+  is_restriction_valid (mod.get_mappings ().get_nodeid (), mod.get_locus ());
+
+  for (auto &item : mod.get_items ())
+    {
+      if (item->get_hir_kind () == HIR::Node::VIS_ITEM)
+	{
+	  auto vis_item = static_cast<HIR::VisItem *> (item.get ());
+	  vis_item->accept_vis (*this);
+	}
+    }
+
+  module_stack.pop_back ();
+}
+
+void
+PubRestrictedVisitor::visit (HIR::ExternCrate &crate)
+{
+  is_restriction_valid (crate.get_mappings ().get_nodeid (),
+			crate.get_locus ());
+}
+
+void
+PubRestrictedVisitor::visit (HIR::UseDeclaration &use_decl)
+{
+  is_restriction_valid (use_decl.get_mappings ().get_nodeid (),
+			use_decl.get_locus ());
+}
+
+void
+PubRestrictedVisitor::visit (HIR::Function &func)
+{
+  is_restriction_valid (func.get_mappings ().get_nodeid (), func.get_locus ());
+}
+
+void
+PubRestrictedVisitor::visit (HIR::TypeAlias &type_alias)
+{
+  is_restriction_valid (type_alias.get_mappings ().get_nodeid (),
+			type_alias.get_locus ());
+}
+
+void
+PubRestrictedVisitor::visit (HIR::StructStruct &struct_item)
+{
+  is_restriction_valid (struct_item.get_mappings ().get_nodeid (),
+			struct_item.get_locus ());
+  // FIXME: Check fields here as well
+}
+
+void
+PubRestrictedVisitor::visit (HIR::TupleStruct &tuple_struct)
+{
+  is_restriction_valid (tuple_struct.get_mappings ().get_nodeid (),
+			tuple_struct.get_locus ());
+  // FIXME: Check fields here as well
+}
+
+void
+PubRestrictedVisitor::visit (HIR::Enum &enum_item)
+{
+  is_restriction_valid (enum_item.get_mappings ().get_nodeid (),
+			enum_item.get_locus ());
+}
+
+void
+PubRestrictedVisitor::visit (HIR::Union &union_item)
+{
+  is_restriction_valid (union_item.get_mappings ().get_nodeid (),
+			union_item.get_locus ());
+}
+
+void
+PubRestrictedVisitor::visit (HIR::ConstantItem &const_item)
+{
+  is_restriction_valid (const_item.get_mappings ().get_nodeid (),
+			const_item.get_locus ());
+}
+
+void
+PubRestrictedVisitor::visit (HIR::StaticItem &static_item)
+{
+  is_restriction_valid (static_item.get_mappings ().get_nodeid (),
+			static_item.get_locus ());
+}
+
+void
+PubRestrictedVisitor::visit (HIR::Trait &trait)
+{
+  is_restriction_valid (trait.get_mappings ().get_nodeid (),
+			trait.get_locus ());
+}
+
+void
+PubRestrictedVisitor::visit (HIR::ImplBlock &impl)
+{
+  is_restriction_valid (impl.get_mappings ().get_nodeid (), impl.get_locus ());
+}
+
+void
+PubRestrictedVisitor::visit (HIR::ExternBlock &block)
+{
+  is_restriction_valid (block.get_mappings ().get_nodeid (),
+			block.get_locus ());
+}
+
+} // namespace Privacy
+} // namespace Rust
diff --git a/gcc/rust/checks/errors/privacy/rust-pub-restricted-visitor.h b/gcc/rust/checks/errors/privacy/rust-pub-restricted-visitor.h
new file mode 100644
index 00000000000..2685f3d1488
--- /dev/null
+++ b/gcc/rust/checks/errors/privacy/rust-pub-restricted-visitor.h
@@ -0,0 +1,120 @@
+// 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_PUB_RESTRICTED_VISITOR_H
+#define RUST_PUB_RESTRICTED_VISITOR_H
+
+#include "rust-hir-visitor.h"
+#include "rust-hir.h"
+#include "rust-hir-expr.h"
+#include "rust-hir-stmt.h"
+#include "rust-hir-item.h"
+#include "rust-hir-map.h"
+
+namespace Rust {
+namespace Privacy {
+
+/**
+ * This visitor takes care of reporting `pub(restricted)` violations:
+ * A `pub(restricted)` violation is defined as the usage of a path restriction
+ * on an item which does not restrict the item's visibility to one of its parent
+ * modules. What this means is that an user is allowed to specify that an item
+ * should be public for any of its parent modules, going all the way to the
+ * `crate` module, but not for any of its children module.
+ *
+ * ```rust
+ * mod a {
+ * 	mod b {
+ * 		pub (in a) struct A0;
+ *
+ * 		mod c {
+ * 			mod d {
+ * 				pub (in a) struct A1;
+ * 			}
+ * 		}
+ *
+ * 		pub (in c::d) struct A2;
+ * 	}
+ * }
+ * ```
+ *
+ * The above `A0`'s visibility is valid: It is restricted to a path, `a`,
+ * which is a parent of the current module, `b`.
+ * Likewise, `A1` is also defined properly: `a` is a parent of `d`, albeit
+ * a great-great-great-grandparant of it.
+ *
+ * `A2` visibility, however, is invalid: Where the struct is defined, the
+ * current module is `b`. `c::d` (which refers to the `d` module) is a child of
+ * `b`, and not one of its ancestors.
+ *
+ * Note that these resolution rules are also the ones of the 2015 rust edition:
+ * All the `pub(restricted)` visibilities above would be invalid in the 2018
+ * edition, as the paths there must be absolute and not relative (`c::d` would
+ * become `crate::a::b::c::d` etc). Nonetheless, the logic stays the same.
+ */
+class PubRestrictedVisitor : public HIR::HIRVisItemVisitor
+{
+public:
+  PubRestrictedVisitor (Analysis::Mappings &mappings);
+
+  void go (HIR::Crate &crate);
+
+  /**
+   * Check if an item's restricted visibility (`pub (crate)`, `pub (self)`,
+   * `pub(super)`, `pub (in <path>)`) is valid in the current context.
+   * `pub restricted` visibilities are only allowed when the restriction path
+   * is a parent module of the item being visited.
+   *
+   * In case of error, this function will emit the errors and return.
+   *
+   * @param item_id NodeId of the item to check the restriction of
+   * @param locus Location of the item being checked
+   *
+   * @return true if the visibility restriction is valid, false otherwise.
+   */
+  bool is_restriction_valid (NodeId item_id, const Location &locus);
+
+  virtual void visit (HIR::Module &mod);
+  virtual void visit (HIR::ExternCrate &crate);
+  virtual void visit (HIR::UseDeclaration &use_decl);
+  virtual void visit (HIR::Function &func);
+  virtual void visit (HIR::TypeAlias &type_alias);
+  virtual void visit (HIR::StructStruct &struct_item);
+  virtual void visit (HIR::TupleStruct &tuple_struct);
+  virtual void visit (HIR::Enum &enum_item);
+  virtual void visit (HIR::Union &union_item);
+  virtual void visit (HIR::ConstantItem &const_item);
+  virtual void visit (HIR::StaticItem &static_item);
+  virtual void visit (HIR::Trait &trait);
+  virtual void visit (HIR::ImplBlock &impl);
+  virtual void visit (HIR::ExternBlock &block);
+
+private:
+  /* Stack of ancestor modules visited by this visitor */
+  std::vector<DefId> module_stack;
+
+  // FIXME: Do we have to handle `self` and `super` as part of the name
+  // resolution pass?
+
+  Analysis::Mappings &mappings;
+};
+
+} // namespace Privacy
+} // namespace Rust
+
+#endif // !RUST_PUB_RESTRICTED_VISITOR_H
diff --git a/gcc/rust/checks/errors/privacy/rust-reachability.cc b/gcc/rust/checks/errors/privacy/rust-reachability.cc
new file mode 100644
index 00000000000..b322e29bfc3
--- /dev/null
+++ b/gcc/rust/checks/errors/privacy/rust-reachability.cc
@@ -0,0 +1,236 @@
+// 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/>.
+
+#include "rust-reachability.h"
+#include "rust-tyty.h"
+
+namespace Rust {
+namespace Privacy {
+
+static HIR::VisItem *
+maybe_get_vis_item (std::unique_ptr<HIR::Item> &item)
+{
+  if (item->get_hir_kind () != HIR::Node::VIS_ITEM)
+    return nullptr;
+
+  return static_cast<HIR::VisItem *> (item.get ());
+}
+
+ReachLevel
+ReachabilityVisitor::get_reachability_level (
+  const HIR::Visibility &item_visibility)
+{
+  return item_visibility.is_public () ? current_level : ReachLevel::Unreachable;
+}
+
+void
+ReachabilityVisitor::visit_generic_predicates (
+  const std::vector<std::unique_ptr<HIR::GenericParam>> &generics,
+  ReachLevel item_reach)
+{
+  if (item_reach == ReachLevel::Unreachable)
+    return;
+
+  for (const auto &generic : generics)
+    {
+      if (generic->get_kind () == HIR::GenericParam::GenericKind::TYPE)
+	{
+	  TyTy::BaseType *generic_ty = nullptr;
+	  auto ok = ty_ctx.lookup_type (generic->get_mappings ().get_hirid (),
+					&generic_ty);
+	  rust_assert (ok);
+	  rust_assert (generic_ty->get_kind () == TyTy::PARAM);
+
+	  auto generic_param = static_cast<TyTy::ParamType *> (generic_ty);
+	  for (const auto &bound : generic_param->get_specified_bounds ())
+	    {
+	      const auto trait = bound.get ()->get_hir_trait_ref ();
+	      ctx.update_reachability (trait->get_mappings (), item_reach);
+	    }
+	}
+    }
+}
+
+void
+ReachabilityVisitor::visit (HIR::Module &mod)
+{
+  auto reach = get_reachability_level (mod.get_visibility ());
+  reach = ctx.update_reachability (mod.get_mappings (), reach);
+
+  for (auto &item : mod.get_items ())
+    {
+      // FIXME: Is that what we want to do? Yes? Only visit the items with
+      // visibility?
+      //
+      // Imagine if we had `maybe_get_vis_item(item)?->accept_vis(*this)` ;)
+      auto vis_item = maybe_get_vis_item (item);
+      if (vis_item)
+	vis_item->accept_vis (*this);
+    }
+}
+
+void
+ReachabilityVisitor::visit (HIR::ExternCrate &crate)
+{
+  auto reach = get_reachability_level (crate.get_visibility ());
+  reach = ctx.update_reachability (crate.get_mappings (), reach);
+}
+
+void
+ReachabilityVisitor::visit (HIR::UseDeclaration &use_decl)
+{
+  auto reach = get_reachability_level (use_decl.get_visibility ());
+  reach = ctx.update_reachability (use_decl.get_mappings (), reach);
+}
+
+void
+ReachabilityVisitor::visit (HIR::Function &func)
+{
+  auto fn_reach = get_reachability_level (func.get_visibility ());
+
+  fn_reach = ctx.update_reachability (func.get_mappings (), fn_reach);
+  visit_generic_predicates (func.get_generic_params (), fn_reach);
+}
+
+void
+ReachabilityVisitor::visit (HIR::TypeAlias &type_alias)
+{
+  auto type_reach = get_reachability_level (type_alias.get_visibility ());
+
+  visit_generic_predicates (type_alias.get_generic_params (), type_reach);
+}
+
+void
+ReachabilityVisitor::visit (HIR::StructStruct &struct_item)
+{
+  auto struct_reach = get_reachability_level (struct_item.get_visibility ());
+
+  struct_reach
+    = ctx.update_reachability (struct_item.get_mappings (), struct_reach);
+
+  auto old_level = current_level;
+  current_level = struct_reach;
+
+  visit_generic_predicates (struct_item.get_generic_params (), struct_reach);
+
+  if (struct_reach != ReachLevel::Unreachable)
+    {
+      for (auto &field : struct_item.get_fields ())
+	if (field.get_visibility ().is_public ())
+	  ctx.update_reachability (field.get_field_type ()->get_mappings (),
+				   struct_reach);
+    }
+
+  current_level = old_level;
+}
+
+void
+ReachabilityVisitor::visit (HIR::TupleStruct &tuple_struct)
+{}
+
+void
+ReachabilityVisitor::visit (HIR::Enum &enum_item)
+{
+  auto enum_reach = get_reachability_level (enum_item.get_visibility ());
+
+  enum_reach = ctx.update_reachability (enum_item.get_mappings (), enum_reach);
+  visit_generic_predicates (enum_item.get_generic_params (), enum_reach);
+
+  for (const auto &variant : enum_item.get_variants ())
+    {
+      auto variant_reach
+	= ctx.update_reachability (variant->get_mappings (), enum_reach);
+
+      switch (variant->get_enum_item_kind ())
+	{
+	  case HIR::EnumItem::Tuple: {
+	    // Should we update the fields only if they are public? Similarly to
+	    // what we do in the ReachabilityVisitor for HIR::TupleStruct?
+	    auto tuple_variant
+	      = static_cast<HIR::EnumItemTuple *> (variant.get ());
+	    for (const auto &field : tuple_variant->get_tuple_fields ())
+	      ctx.update_reachability (field.get_mappings (), variant_reach);
+	    break;
+	  }
+	  case HIR::EnumItem::Struct: {
+	    // Should we update the fields only if they are public? Similarly to
+	    // what we do in the ReachabilityVisitor for HIR::StructStruct?
+	    auto struct_variant
+	      = static_cast<HIR::EnumItemStruct *> (variant.get ());
+	    for (const auto &field : struct_variant->get_struct_fields ())
+	      ctx.update_reachability (field.get_mappings (), variant_reach);
+	    break;
+	  }
+	// Nothing nested to visit in that case
+	case HIR::EnumItem::Named:
+	case HIR::EnumItem::Discriminant:
+	  break;
+	}
+    }
+}
+
+void
+ReachabilityVisitor::visit (HIR::Union &union_item)
+{
+  auto union_reach = get_reachability_level (union_item.get_visibility ());
+
+  union_reach
+    = ctx.update_reachability (union_item.get_mappings (), union_reach);
+  visit_generic_predicates (union_item.get_generic_params (), union_reach);
+}
+
+void
+ReachabilityVisitor::visit (HIR::ConstantItem &const_item)
+{
+  auto reach = get_reachability_level (const_item.get_visibility ());
+  reach = ctx.update_reachability (const_item.get_mappings (), reach);
+}
+
+void
+ReachabilityVisitor::visit (HIR::StaticItem &static_item)
+{
+  auto reach = get_reachability_level (static_item.get_visibility ());
+  reach = ctx.update_reachability (static_item.get_mappings (), reach);
+}
+
+void
+ReachabilityVisitor::visit (HIR::Trait &trait)
+{
+  auto trait_reach = get_reachability_level (trait.get_visibility ());
+
+  trait_reach = ctx.update_reachability (trait.get_mappings (), trait_reach);
+  visit_generic_predicates (trait.get_generic_params (), trait_reach);
+}
+
+void
+ReachabilityVisitor::visit (HIR::ImplBlock &impl)
+{
+  auto impl_reach = get_reachability_level (impl.get_visibility ());
+
+  impl_reach = ctx.update_reachability (impl.get_mappings (), impl_reach);
+  visit_generic_predicates (impl.get_generic_params (), impl_reach);
+}
+
+void
+ReachabilityVisitor::visit (HIR::ExternBlock &block)
+{}
+
+// FIXME: How can we visit Blocks in the current configuration? Have a full
+// visitor?
+} // namespace Privacy
+} // namespace Rust
diff --git a/gcc/rust/checks/errors/privacy/rust-reachability.h b/gcc/rust/checks/errors/privacy/rust-reachability.h
new file mode 100644
index 00000000000..e0bc4f5f0b8
--- /dev/null
+++ b/gcc/rust/checks/errors/privacy/rust-reachability.h
@@ -0,0 +1,87 @@
+// 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_REACHABILITY_H
+#define RUST_REACHABILITY_H
+
+#include "rust-privacy-ctx.h"
+#include "rust-hir-visitor.h"
+#include "rust-hir.h"
+#include "rust-hir-expr.h"
+#include "rust-hir-stmt.h"
+#include "rust-hir-item.h"
+#include "rust-hir-type-check.h"
+
+namespace Rust {
+namespace Privacy {
+
+// FIXME: The EmbargoVisitor from rustc is a fixed-point visitor which tries
+// to reach more and more nodes until nothing has changed anymore.
+// Do we need to reproduce this behavior? How long does it take to do this?
+
+/**
+ * The ReachabilityVisitor tries to reach all items possible in the crate,
+ * according to their privacy level.
+ */
+class ReachabilityVisitor : public HIR::HIRVisItemVisitor
+{
+public:
+  ReachabilityVisitor (PrivacyContext &ctx,
+		       const ::Rust::Resolver::TypeCheckContext &ty_ctx)
+    : current_level (ReachLevel::Reachable), ctx (ctx), ty_ctx (ty_ctx)
+  {}
+
+  // FIXME: Add `go` method which takes an `HIR::Crate &` as argument
+
+  /**
+   * Visit all the predicates of all the generic types of a given item, marking
+   * them as reachable or not.
+   */
+  void visit_generic_predicates (
+    const std::vector<std::unique_ptr<HIR::GenericParam>> &generics,
+    ReachLevel item_reach);
+
+  /**
+   * Get the initial reach level for an item based on its visibility.
+   */
+  ReachLevel get_reachability_level (const HIR::Visibility &item_visibility);
+
+  virtual void visit (HIR::Module &mod);
+  virtual void visit (HIR::ExternCrate &crate);
+  virtual void visit (HIR::UseDeclaration &use_decl);
+  virtual void visit (HIR::Function &func);
+  virtual void visit (HIR::TypeAlias &type_alias);
+  virtual void visit (HIR::StructStruct &struct_item);
+  virtual void visit (HIR::TupleStruct &tuple_struct);
+  virtual void visit (HIR::Enum &enum_item);
+  virtual void visit (HIR::Union &union_item);
+  virtual void visit (HIR::ConstantItem &const_item);
+  virtual void visit (HIR::StaticItem &static_item);
+  virtual void visit (HIR::Trait &trait);
+  virtual void visit (HIR::ImplBlock &impl);
+  virtual void visit (HIR::ExternBlock &block);
+
+private:
+  ReachLevel current_level;
+  PrivacyContext &ctx;
+  const ::Rust::Resolver::TypeCheckContext &ty_ctx;
+};
+} // namespace Privacy
+} // namespace Rust
+
+#endif // !RUST_REACHABILITY_H
diff --git a/gcc/rust/checks/errors/privacy/rust-visibility-resolver.cc b/gcc/rust/checks/errors/privacy/rust-visibility-resolver.cc
new file mode 100644
index 00000000000..301182754a4
--- /dev/null
+++ b/gcc/rust/checks/errors/privacy/rust-visibility-resolver.cc
@@ -0,0 +1,245 @@
+// 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/>.
+
+#include "rust-visibility-resolver.h"
+#include "rust-ast.h"
+#include "rust-hir.h"
+#include "rust-hir-item.h"
+
+namespace Rust {
+namespace Privacy {
+
+VisibilityResolver::VisibilityResolver (Analysis::Mappings &mappings,
+					Resolver::Resolver &resolver)
+  : mappings (mappings), resolver (resolver)
+{}
+
+void
+VisibilityResolver::go (HIR::Crate &crate)
+{
+  mappings.insert_visibility (crate.get_mappings ().get_nodeid (),
+			      ModuleVisibility::create_public ());
+
+  current_module = crate.get_mappings ().get_defid ();
+
+  for (auto &item : crate.items)
+    {
+      if (item->get_hir_kind () == HIR::Node::VIS_ITEM)
+	{
+	  auto vis_item = static_cast<HIR::VisItem *> (item.get ());
+	  vis_item->accept_vis (*this);
+	}
+    }
+}
+
+bool
+VisibilityResolver::resolve_module_path (const HIR::SimplePath &restriction,
+					 DefId &id)
+{
+  // We need, from the restriction, to figure out the actual Module it
+  // belongs to.
+
+  NodeId ast_node_id = restriction.get_mappings ().get_nodeid ();
+
+  auto invalid_path
+    = Error (restriction.get_locus (),
+	     "cannot use non-module path as privacy restrictor");
+
+  NodeId ref_node_id = UNKNOWN_NODEID;
+  if (!resolver.lookup_resolved_name (ast_node_id, &ref_node_id))
+    {
+      invalid_path.emit_error ();
+      return false;
+    }
+  // FIXME: Add a hint here if we can find the path in another scope, such as
+  // a type or something else
+  // TODO: For the hint, can we point to the original item's definition if
+  // present?
+
+  HirId ref;
+  rust_assert (mappings.lookup_node_to_hir (ref_node_id, &ref));
+
+  auto module = mappings.lookup_module (ref);
+  if (!module)
+    {
+      invalid_path.emit_error ();
+      return false;
+    }
+
+  // Fill in the resolved `DefId`
+  id = module->get_mappings ().get_defid ();
+
+  return true;
+}
+
+bool
+VisibilityResolver::resolve_visibility (const HIR::Visibility &visibility,
+					ModuleVisibility &to_resolve)
+{
+  switch (visibility.get_vis_type ())
+    {
+    case HIR::Visibility::PRIVATE:
+      to_resolve = ModuleVisibility::create_restricted (current_module);
+      return true;
+    case HIR::Visibility::PUBLIC:
+      to_resolve = ModuleVisibility::create_public ();
+      return true;
+      case HIR::Visibility::RESTRICTED: {
+	// FIXME: We also need to handle 2015 vs 2018 edition conflicts
+	auto id = UNKNOWN_DEFID;
+	auto result = resolve_module_path (visibility.get_path (), id);
+	to_resolve = ModuleVisibility::create_restricted (id);
+	return result;
+      }
+    default:
+      gcc_unreachable ();
+      return false;
+    }
+}
+
+void
+VisibilityResolver::resolve_and_update (const HIR::VisItem *item)
+{
+  ModuleVisibility module_vis;
+  if (!resolve_visibility (item->get_visibility (), module_vis))
+    return; // we will already have emitted errors
+
+  mappings.insert_visibility (item->get_mappings ().get_nodeid (), module_vis);
+}
+
+void
+VisibilityResolver::visit (HIR::Module &mod)
+{
+  auto old_module = current_module;
+  current_module = mod.get_mappings ().get_defid ();
+
+  for (auto &item : mod.get_items ())
+    {
+      if (item->get_hir_kind () == HIR::Node::VIS_ITEM)
+	{
+	  auto vis_item = static_cast<HIR::VisItem *> (item.get ());
+	  vis_item->accept_vis (*this);
+	}
+    }
+
+  current_module = old_module;
+}
+
+void
+VisibilityResolver::visit (HIR::ExternCrate &crate)
+{}
+
+void
+VisibilityResolver::visit (HIR::UseDeclaration &use_decl)
+{}
+
+void
+VisibilityResolver::visit (HIR::Function &func)
+{
+  resolve_and_update (&func);
+}
+
+void
+VisibilityResolver::visit (HIR::TypeAlias &type_alias)
+{
+  resolve_and_update (&type_alias);
+}
+
+void
+VisibilityResolver::visit (HIR::StructStruct &struct_item)
+{
+  resolve_and_update (&struct_item);
+}
+
+void
+VisibilityResolver::visit (HIR::TupleStruct &tuple_struct)
+{
+  resolve_and_update (&tuple_struct);
+}
+
+void
+VisibilityResolver::visit (HIR::Enum &enum_item)
+{
+  ModuleVisibility vis;
+  if (!resolve_visibility (enum_item.get_visibility (), vis))
+    return;
+
+  mappings.insert_visibility (enum_item.get_mappings ().get_nodeid (), vis);
+  for (auto &variant : enum_item.get_variants ())
+    mappings.insert_visibility (variant->get_mappings ().get_nodeid (), vis);
+}
+
+void
+VisibilityResolver::visit (HIR::Union &union_item)
+{}
+
+void
+VisibilityResolver::visit (HIR::ConstantItem &const_item)
+{
+  resolve_and_update (&const_item);
+}
+
+void
+VisibilityResolver::visit (HIR::StaticItem &static_item)
+{
+  resolve_and_update (&static_item);
+}
+
+void
+VisibilityResolver::visit (HIR::Trait &trait)
+{
+  ModuleVisibility vis;
+  if (!resolve_visibility (trait.get_visibility (), vis))
+    return;
+
+  mappings.insert_visibility (trait.get_mappings ().get_nodeid (), vis);
+  for (auto &item : trait.get_trait_items ())
+    mappings.insert_visibility (item->get_mappings ().get_nodeid (), vis);
+}
+
+void
+VisibilityResolver::visit (HIR::ImplBlock &impl)
+{
+  for (auto &item : impl.get_impl_items ())
+    {
+      HIR::VisItem *vis_item;
+      switch (item->get_impl_item_type ())
+	{
+	case HIR::ImplItem::FUNCTION:
+	  vis_item = static_cast<HIR::Function *> (item.get ());
+	  break;
+	case HIR::ImplItem::TYPE_ALIAS:
+	  vis_item = static_cast<HIR::TypeAlias *> (item.get ());
+	  break;
+	case HIR::ImplItem::CONSTANT:
+	  vis_item = static_cast<HIR::ConstantItem *> (item.get ());
+	  break;
+	default:
+	  gcc_unreachable ();
+	  return;
+	}
+      vis_item->accept_vis (*this);
+    }
+}
+
+void
+VisibilityResolver::visit (HIR::ExternBlock &block)
+{}
+
+} // namespace Privacy
+} // namespace Rust
diff --git a/gcc/rust/checks/errors/privacy/rust-visibility-resolver.h b/gcc/rust/checks/errors/privacy/rust-visibility-resolver.h
new file mode 100644
index 00000000000..20a581c16d4
--- /dev/null
+++ b/gcc/rust/checks/errors/privacy/rust-visibility-resolver.h
@@ -0,0 +1,103 @@
+// 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_VISIBILITY_H
+#define RUST_VISIBILITY_H
+
+#include "rust-hir.h"
+#include "rust-hir-expr.h"
+#include "rust-hir-stmt.h"
+#include "rust-hir-item.h"
+#include "rust-hir-map.h"
+#include "rust-name-resolver.h"
+#include "rust-hir-visitor.h"
+
+namespace Rust {
+namespace Privacy {
+
+class VisibilityResolver : public HIR::HIRVisItemVisitor
+{
+public:
+  VisibilityResolver (Analysis::Mappings &mappings,
+		      Rust::Resolver::Resolver &resolver);
+
+  /**
+   * Perform visibility resolving on an entire crate
+   */
+  void go (HIR::Crate &crate);
+
+  /**
+   * Resolve a path to the module it refers
+   */
+  bool resolve_module_path (const HIR::SimplePath &restriction,
+			    DefId &to_resolve);
+
+  /**
+   * Resolve the visibility of an item to its ModuleVisibility. This function
+   * emits errors if necessary. The contents of the to_resolve parameter will be
+   * overwritten on success.
+   *
+   * @param visibility Visibility of the item to resolve
+   * @param to_resolve ModuleVisibility reference to fill on success.
+   *
+   * @return false on error, true if the resolving was successful.
+   */
+  bool resolve_visibility (const HIR::Visibility &visibility,
+			   ModuleVisibility &to_resolve);
+
+  /**
+   * Resolve the visibility of an item and updates it. This is useful for
+   * vis-items who need to be resolved but do not care about their module
+   * visibility - const items, static items, etc. For items with an impact on
+   * their children (enums, traits), this cannot be used
+   */
+  void resolve_and_update (const HIR::VisItem *item);
+
+  /**
+   * Get the DefId of the parent module we are currently visiting.
+   *
+   * @return UNKNOWN_DEFID if the module stack is empty, a valid `DefId`
+   * otherwise
+   */
+  DefId peek_module ();
+
+  virtual void visit (HIR::Module &mod);
+  virtual void visit (HIR::ExternCrate &crate);
+  virtual void visit (HIR::UseDeclaration &use_decl);
+  virtual void visit (HIR::Function &func);
+  virtual void visit (HIR::TypeAlias &type_alias);
+  virtual void visit (HIR::StructStruct &struct_item);
+  virtual void visit (HIR::TupleStruct &tuple_struct);
+  virtual void visit (HIR::Enum &enum_item);
+  virtual void visit (HIR::Union &union_item);
+  virtual void visit (HIR::ConstantItem &const_item);
+  virtual void visit (HIR::StaticItem &static_item);
+  virtual void visit (HIR::Trait &trait);
+  virtual void visit (HIR::ImplBlock &impl);
+  virtual void visit (HIR::ExternBlock &block);
+
+private:
+  Analysis::Mappings &mappings;
+  Rust::Resolver::Resolver &resolver;
+  DefId current_module;
+};
+
+} // namespace Privacy
+} // namespace Rust
+
+#endif // !RUST_VISIBILITY_H
-- 
2.25.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [PATCH Rust front-end v2 26/37] gccrs: Add dead code scan on HIR
  2022-08-24 11:59 Rust frontend patches v2 herron.philip
                   ` (24 preceding siblings ...)
  2022-08-24 11:59 ` [PATCH Rust front-end v2 25/37] gccrs: Add privacy checks herron.philip
@ 2022-08-24 11:59 ` herron.philip
  2022-08-24 11:59 ` [PATCH Rust front-end v2 27/37] gccrs: Add unused variable scan herron.philip
                   ` (11 subsequent siblings)
  37 siblings, 0 replies; 59+ messages in thread
From: herron.philip @ 2022-08-24 11:59 UTC (permalink / raw)
  To: gcc-patches; +Cc: gcc-rust, Thomas Young

From: Thomas Young <wenzhang5800@gmail.com>

In order to find dead code we use a depth first search and keep liveness
variables, after type resolution. In this case, if a function is unused
and it calls another function the 2nd function is now unused since the
caller is not used etc. The algorithm is a depth first search.
---
 .../checks/lints/rust-lint-marklive-base.h    |  45 +++
 gcc/rust/checks/lints/rust-lint-marklive.cc   | 282 ++++++++++++++++
 gcc/rust/checks/lints/rust-lint-marklive.h    | 308 ++++++++++++++++++
 .../checks/lints/rust-lint-scan-deadcode.h    | 154 +++++++++
 4 files changed, 789 insertions(+)
 create mode 100644 gcc/rust/checks/lints/rust-lint-marklive-base.h
 create mode 100644 gcc/rust/checks/lints/rust-lint-marklive.cc
 create mode 100644 gcc/rust/checks/lints/rust-lint-marklive.h
 create mode 100644 gcc/rust/checks/lints/rust-lint-scan-deadcode.h

diff --git a/gcc/rust/checks/lints/rust-lint-marklive-base.h b/gcc/rust/checks/lints/rust-lint-marklive-base.h
new file mode 100644
index 00000000000..97c068188b1
--- /dev/null
+++ b/gcc/rust/checks/lints/rust-lint-marklive-base.h
@@ -0,0 +1,45 @@
+// Copyright (C) 2021-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_HIR_LIVENESS_BASE
+#define RUST_HIR_LIVENESS_BASE
+
+#include "rust-diagnostics.h"
+#include "rust-lint-marklive.h"
+#include "rust-lint-marklive-base.h"
+#include "rust-hir-visitor.h"
+#include "rust-hir-map.h"
+
+namespace Rust {
+namespace Analysis {
+
+class MarkLiveBase : public HIR::HIRFullVisitorBase
+{
+public:
+  virtual ~MarkLiveBase () {}
+
+protected:
+  MarkLiveBase () : mappings (Analysis::Mappings::get ()) {}
+
+  Analysis::Mappings *mappings;
+};
+
+} // namespace Analysis
+} // namespace Rust
+
+#endif
diff --git a/gcc/rust/checks/lints/rust-lint-marklive.cc b/gcc/rust/checks/lints/rust-lint-marklive.cc
new file mode 100644
index 00000000000..245632b4b4c
--- /dev/null
+++ b/gcc/rust/checks/lints/rust-lint-marklive.cc
@@ -0,0 +1,282 @@
+// Copyright (C) 2021-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/>.
+
+// The idea is that all reachable symbols are live, codes called
+// from live codes are live, and everything else is dead.
+
+#include "rust-lint-marklive.h"
+#include "rust-hir-full.h"
+#include "rust-name-resolver.h"
+
+namespace Rust {
+namespace Analysis {
+
+// This class trys to find the live symbols which can be used as
+// seeds in MarkLive
+//
+// 1. TODO: explicit live
+//    - Attribute like #[allow(dead_code)]
+//    - Attribute like #[lang=".."], it's not a intra-crate item.
+// 2. TODO: foreign item
+class FindEntryPoint : public MarkLiveBase
+{
+  using Rust::Analysis::MarkLiveBase::visit;
+
+public:
+  static std::vector<HirId> find (HIR::Crate &crate)
+  {
+    FindEntryPoint findEntryPoint;
+    for (auto it = crate.items.begin (); it != crate.items.end (); it++)
+      {
+	it->get ()->accept_vis (findEntryPoint);
+      }
+    return findEntryPoint.getEntryPoint ();
+  }
+
+  // TODO not only fn main can be a entry point.
+  void visit (HIR::Function &function) override
+  {
+    if (function.get_function_name () == "main")
+      {
+	entryPoints.push_back (function.get_mappings ().get_hirid ());
+      }
+  }
+
+private:
+  FindEntryPoint () : MarkLiveBase () {}
+  std::vector<HirId> entryPoints;
+  std::vector<HirId> getEntryPoint () { return entryPoints; }
+};
+
+std::set<HirId>
+MarkLive::Analysis (HIR::Crate &crate)
+{
+  MarkLive marklive (FindEntryPoint::find (crate));
+  marklive.go (crate);
+
+  return marklive.liveSymbols;
+}
+
+// pop a live symbol from worklist every iteration,
+// if it's a function then walk the function body, and
+// 1. save all the live symbols in worklist which is
+//    visited first time
+// 2. save all the live symbols in liveSymbols
+void
+MarkLive::go (HIR::Crate &crate)
+{
+  while (!worklist.empty ())
+    {
+      HirId hirId = worklist.back ();
+      worklist.pop_back ();
+      scannedSymbols.emplace (hirId);
+      HIR::Item *item = mappings->lookup_hir_item (hirId);
+      liveSymbols.emplace (hirId);
+      if (item != nullptr)
+	{
+	  item->accept_vis (*this);
+	}
+      else
+	{ // the item maybe inside a trait impl
+	  HirId parent_impl_id = UNKNOWN_HIRID;
+	  HIR::ImplItem *implItem
+	    = mappings->lookup_hir_implitem (hirId, &parent_impl_id);
+	  if (implItem != nullptr)
+	    implItem->accept_vis (*this);
+	}
+    }
+}
+
+void
+MarkLive::visit (HIR::PathInExpression &expr)
+{
+  // We should iterate every path segment in order to mark the struct which
+  // is used in expression like Foo::bar(), we should mark the Foo alive.
+  expr.iterate_path_segments ([&] (HIR::PathExprSegment &seg) -> bool {
+    return visit_path_segment (seg);
+  });
+
+  // after iterate the path segments, we should mark functions and associated
+  // functions alive.
+  NodeId ast_node_id = expr.get_mappings ().get_nodeid ();
+  NodeId ref_node_id = UNKNOWN_NODEID;
+  find_ref_node_id (ast_node_id, ref_node_id);
+
+  // node back to HIR
+  HirId ref;
+  bool ok = mappings->lookup_node_to_hir (ref_node_id, &ref);
+  rust_assert (ok);
+
+  // it must resolve to some kind of HIR::Item or HIR::InheritImplItem
+  HIR::Item *resolved_item = mappings->lookup_hir_item (ref);
+  if (resolved_item != nullptr)
+    {
+      mark_hir_id (resolved_item->get_mappings ().get_hirid ());
+    }
+  else
+    {
+      HirId parent_impl_id = UNKNOWN_HIRID;
+      HIR::ImplItem *resolved_item
+	= mappings->lookup_hir_implitem (ref, &parent_impl_id);
+      if (resolved_item != nullptr)
+	{
+	  mark_hir_id (resolved_item->get_impl_mappings ().get_hirid ());
+	}
+    }
+}
+
+void
+MarkLive::visit (HIR::MethodCallExpr &expr)
+{
+  expr.get_receiver ()->accept_vis (*this);
+  visit_path_segment (expr.get_method_name ());
+  for (auto &argument : expr.get_arguments ())
+    argument->accept_vis (*this);
+
+  // Trying to find the method definition and mark it alive.
+  NodeId ast_node_id = expr.get_mappings ().get_nodeid ();
+  NodeId ref_node_id = UNKNOWN_NODEID;
+  find_ref_node_id (ast_node_id, ref_node_id);
+
+  // node back to HIR
+  HirId ref;
+  bool ok = mappings->lookup_node_to_hir (ref_node_id, &ref);
+  rust_assert (ok);
+  mark_hir_id (ref);
+}
+
+bool
+MarkLive::visit_path_segment (HIR::PathExprSegment seg)
+{
+  NodeId ast_node_id = seg.get_mappings ().get_nodeid ();
+  NodeId ref_node_id = UNKNOWN_NODEID;
+
+  // There are two different kinds of segment for us.
+  // 1. function segment
+  //      like the symbol "foo" in expression `foo()`.
+  // 2. type segment
+  //      like the symbol "Foo" in expression `Foo{a: 1, b: 2}`
+  //
+  // We should mark them alive all and ignoring other kind of segments.
+  // If the segment we dont care then just return false is fine
+  if (!resolver->lookup_resolved_name (ast_node_id, &ref_node_id))
+    {
+      if (!resolver->lookup_resolved_type (ast_node_id, &ref_node_id))
+	return false;
+    }
+  HirId ref;
+  bool ok = mappings->lookup_node_to_hir (ref_node_id, &ref);
+  rust_assert (ok);
+  mark_hir_id (ref);
+  return true;
+}
+
+void
+MarkLive::visit (HIR::FieldAccessExpr &expr)
+{
+  // visit receiver at first
+  expr.get_receiver_expr ()->accept_vis (*this);
+
+  // resolve the receiver back to ADT type
+  TyTy::BaseType *receiver = nullptr;
+  if (!tyctx->lookup_type (
+	expr.get_receiver_expr ()->get_mappings ().get_hirid (), &receiver))
+    {
+      rust_error_at (expr.get_receiver_expr ()->get_locus (),
+		     "unresolved type for receiver");
+    }
+
+  TyTy::ADTType *adt = nullptr;
+  if (receiver->get_kind () == TyTy::TypeKind::ADT)
+    {
+      adt = static_cast<TyTy::ADTType *> (receiver);
+    }
+  else if (receiver->get_kind () == TyTy::TypeKind::REF)
+    {
+      TyTy::ReferenceType *r = static_cast<TyTy::ReferenceType *> (receiver);
+      TyTy::BaseType *b = r->get_base ();
+      rust_assert (b->get_kind () == TyTy::TypeKind::ADT);
+
+      adt = static_cast<TyTy::ADTType *> (b);
+    }
+
+  rust_assert (adt != nullptr);
+  rust_assert (!adt->is_enum ());
+  rust_assert (adt->number_of_variants () == 1);
+
+  TyTy::VariantDef *variant = adt->get_variants ().at (0);
+
+  // get the field index
+  size_t index;
+  TyTy::StructFieldType *field;
+  bool ok = variant->lookup_field (expr.get_field_name (), &field, &index);
+  rust_assert (ok);
+  if (index >= variant->num_fields ())
+    {
+      rust_error_at (expr.get_receiver_expr ()->get_locus (),
+		     "cannot access struct %s by index: %lu",
+		     adt->get_name ().c_str (), (unsigned long) index);
+      return;
+    }
+
+  // get the field hir id
+  HirId field_id = field->get_ref ();
+  mark_hir_id (field_id);
+}
+
+void
+MarkLive::visit (HIR::TupleIndexExpr &expr)
+{
+  // TODO: unused tuple field detection
+  expr.get_tuple_expr ()->accept_vis (*this);
+}
+
+void
+MarkLive::visit (HIR::TypeAlias &alias)
+{
+  NodeId ast_node_id;
+  resolver->lookup_resolved_type (
+    alias.get_type_aliased ()->get_mappings ().get_nodeid (), &ast_node_id);
+  HirId hir_id;
+  bool ok = mappings->lookup_node_to_hir (ast_node_id, &hir_id);
+  rust_assert (ok);
+  mark_hir_id (hir_id);
+}
+
+void
+MarkLive::mark_hir_id (HirId id)
+{
+  if (scannedSymbols.find (id) == scannedSymbols.end ())
+    {
+      worklist.push_back (id);
+    }
+  liveSymbols.emplace (id);
+}
+
+void
+MarkLive::find_ref_node_id (NodeId ast_node_id, NodeId &ref_node_id)
+{
+  if (!resolver->lookup_resolved_name (ast_node_id, &ref_node_id))
+    {
+      bool ok = resolver->lookup_resolved_type (ast_node_id, &ref_node_id);
+      rust_assert (ok);
+    }
+}
+
+} // namespace Analysis
+} // namespace Rust
diff --git a/gcc/rust/checks/lints/rust-lint-marklive.h b/gcc/rust/checks/lints/rust-lint-marklive.h
new file mode 100644
index 00000000000..119af8b8c95
--- /dev/null
+++ b/gcc/rust/checks/lints/rust-lint-marklive.h
@@ -0,0 +1,308 @@
+// Copyright (C) 2021-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_HIR_LIVENESS
+#define RUST_HIR_LIVENESS
+
+#include "rust-hir-full-decls.h"
+#include "rust-hir-map.h"
+#include "rust-lint-marklive-base.h"
+#include "rust-name-resolver.h"
+
+namespace Rust {
+namespace Analysis {
+
+class MarkLive : public MarkLiveBase
+{
+  using Rust::Analysis::MarkLiveBase::visit;
+
+public:
+  static std::set<HirId> Analysis (HIR::Crate &crate);
+  void go (HIR::Crate &crate);
+
+  void visit (HIR::PathInExpression &expr) override;
+  void visit (HIR::FieldAccessExpr &expr) override;
+  void visit (HIR::TupleIndexExpr &expr) override;
+  void visit (HIR::MethodCallExpr &expr) override;
+  void visit (HIR::TypeAlias &alias) override;
+
+  void visit (HIR::BorrowExpr &expr) override
+  {
+    expr.get_expr ()->accept_vis (*this);
+  }
+
+  void visit (HIR::DereferenceExpr &expr) override
+  {
+    expr.get_expr ()->accept_vis (*this);
+  }
+
+  void visit (HIR::NegationExpr &expr) override
+  {
+    expr.get_expr ()->accept_vis (*this);
+  }
+
+  void visit (HIR::LazyBooleanExpr &expr) override
+  {
+    expr.get_lhs ()->accept_vis (*this);
+    expr.get_rhs ()->accept_vis (*this);
+  }
+
+  void visit (HIR::TypeCastExpr &expr) override
+  {
+    expr.get_expr ()->accept_vis (*this);
+  }
+
+  void visit (HIR::GroupedExpr &expr) override
+  {
+    expr.get_expr_in_parens ()->accept_vis (*this);
+  }
+
+  void visit (HIR::ArrayExpr &expr) override
+  {
+    expr.get_internal_elements ()->accept_vis (*this);
+  }
+
+  void visit (HIR::ArrayIndexExpr &expr) override
+  {
+    expr.get_array_expr ()->accept_vis (*this);
+    expr.get_index_expr ()->accept_vis (*this);
+  }
+
+  void visit (HIR::ArrayElemsValues &expr) override
+  {
+    for (auto &elem : expr.get_values ())
+      {
+	elem->accept_vis (*this);
+      }
+  }
+
+  void visit (HIR::TupleExpr &expr) override
+  {
+    for (auto &elem : expr.get_tuple_elems ())
+      {
+	elem->accept_vis (*this);
+      }
+  }
+
+  void visit (HIR::BlockExpr &expr) override
+  {
+    for (auto &s : expr.get_statements ())
+      {
+	s->accept_vis (*this);
+      }
+    if (expr.has_expr ())
+      {
+	expr.get_final_expr ()->accept_vis (*this);
+      }
+  }
+
+  void visit (HIR::UnsafeBlockExpr &expr) override
+  {
+    expr.get_block_expr ()->accept_vis (*this);
+  }
+
+  void visit (HIR::LoopExpr &expr) override
+  {
+    expr.get_loop_block ()->accept_vis (*this);
+  }
+
+  void visit (HIR::BreakExpr &expr) override
+  {
+    if (expr.has_break_expr ())
+      expr.get_expr ()->accept_vis (*this);
+  }
+
+  void visit (HIR::WhileLoopExpr &expr) override
+  {
+    expr.get_loop_block ()->accept_vis (*this);
+    expr.get_predicate_expr ()->accept_vis (*this);
+  }
+
+  void visit (HIR::Function &function) override
+  {
+    function.get_definition ()->accept_vis (*this);
+  }
+
+  void visit (HIR::ReturnExpr &expr) override
+  {
+    if (expr.has_return_expr ())
+      expr.get_expr ()->accept_vis (*this);
+  }
+
+  void visit (HIR::WhileLetLoopExpr &expr) override
+  {
+    expr.get_loop_block ()->accept_vis (*this);
+    expr.get_cond ()->accept_vis (*this);
+  }
+
+  void visit (HIR::ForLoopExpr &expr) override
+  {
+    expr.get_loop_block ()->accept_vis (*this);
+    expr.get_iterator_expr ()->accept_vis (*this);
+  }
+
+  void visit (HIR::ExprStmtWithoutBlock &stmt) override
+  {
+    stmt.get_expr ()->accept_vis (*this);
+  }
+
+  void visit (HIR::ExprStmtWithBlock &stmt) override
+  {
+    stmt.get_expr ()->accept_vis (*this);
+  }
+
+  void visit (HIR::CallExpr &expr) override
+  {
+    expr.get_fnexpr ()->accept_vis (*this);
+    for (auto &argument : expr.get_arguments ())
+      argument->accept_vis (*this);
+  }
+
+  void visit (HIR::ArithmeticOrLogicalExpr &expr) override
+  {
+    expr.visit_lhs (*this);
+    expr.visit_rhs (*this);
+  }
+  void visit (HIR::ComparisonExpr &expr) override
+  {
+    expr.get_lhs ()->accept_vis (*this);
+    expr.get_rhs ()->accept_vis (*this);
+  }
+
+  void visit (HIR::AssignmentExpr &expr) override
+  {
+    expr.visit_lhs (*this);
+    expr.visit_rhs (*this);
+  }
+
+  void visit (HIR::CompoundAssignmentExpr &expr) override
+  {
+    expr.visit_lhs (*this);
+    expr.visit_rhs (*this);
+  }
+
+  void visit (HIR::IfExpr &expr) override
+  {
+    expr.get_if_condition ()->accept_vis (*this);
+    expr.get_if_block ()->accept_vis (*this);
+  }
+
+  void visit (HIR::IfExprConseqElse &expr) override
+  {
+    expr.get_if_condition ()->accept_vis (*this);
+    expr.get_if_block ()->accept_vis (*this);
+    expr.get_else_block ()->accept_vis (*this);
+  }
+
+  void visit (HIR::MatchExpr &expr) override
+  {
+    expr.get_scrutinee_expr ()->accept_vis (*this);
+    std::vector<HIR::MatchCase> &cases = expr.get_match_cases ();
+    for (auto &&caz : cases)
+      {
+	auto case_arm = caz.get_arm ();
+	if (case_arm.has_match_arm_guard ())
+	  case_arm.get_guard_expr ()->accept_vis (*this);
+	caz.get_expr ()->accept_vis (*this);
+      }
+  }
+
+  void visit (HIR::IfExprConseqIf &expr) override
+  {
+    expr.get_if_condition ()->accept_vis (*this);
+    expr.get_if_block ()->accept_vis (*this);
+    expr.get_conseq_if_expr ()->accept_vis (*this);
+  }
+
+  void visit (HIR::TraitItemFunc &item) override
+  {
+    item.get_block_expr ()->accept_vis (*this);
+  }
+
+  void visit (HIR::ImplBlock &impl) override
+  {
+    for (auto &&item : impl.get_impl_items ())
+      {
+	item->accept_vis (*this);
+      }
+  }
+
+  void visit (HIR::LetStmt &stmt) override
+  {
+    if (stmt.has_init_expr ())
+      {
+	stmt.get_init_expr ()->accept_vis (*this);
+      }
+  }
+
+  void visit (HIR::StructExprStruct &stct) override
+  {
+    stct.get_struct_name ().accept_vis (*this);
+  }
+
+  void visit (HIR::StructExprStructFields &stct) override
+  {
+    for (auto &field : stct.get_fields ())
+      {
+	field->accept_vis (*this);
+      }
+
+    stct.get_struct_name ().accept_vis (*this);
+    if (stct.has_struct_base ())
+      {
+	stct.struct_base->base_struct->accept_vis (*this);
+      }
+  }
+
+  virtual void visit (HIR::StructExprFieldIdentifierValue &field) override
+  {
+    field.get_value ()->accept_vis (*this);
+  }
+
+  void visit (HIR::StructExprStructBase &stct) override
+  {
+    stct.get_struct_base ()->base_struct->accept_vis (*this);
+  }
+
+  void visit (HIR::Module &module) override
+  {
+    for (auto &item : module.get_items ())
+      item->accept_vis (*this);
+  }
+
+private:
+  std::vector<HirId> worklist;
+  std::set<HirId> liveSymbols;
+  std::set<HirId> scannedSymbols;
+  Analysis::Mappings *mappings;
+  Resolver::Resolver *resolver;
+  Resolver::TypeCheckContext *tyctx;
+  MarkLive (std::vector<HirId> worklist)
+    : worklist (worklist), mappings (Analysis::Mappings::get ()),
+      resolver (Resolver::Resolver::get ()),
+      tyctx (Resolver::TypeCheckContext::get ()){};
+
+  void mark_hir_id (HirId);
+  bool visit_path_segment (HIR::PathExprSegment);
+  void find_ref_node_id (NodeId ast_node_id, NodeId &ref_node_id);
+};
+
+} // namespace Analysis
+} // namespace Rust
+
+#endif
diff --git a/gcc/rust/checks/lints/rust-lint-scan-deadcode.h b/gcc/rust/checks/lints/rust-lint-scan-deadcode.h
new file mode 100644
index 00000000000..591cb30bc24
--- /dev/null
+++ b/gcc/rust/checks/lints/rust-lint-scan-deadcode.h
@@ -0,0 +1,154 @@
+// Copyright (C) 2021-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_HIR_SCAN_DEADCODE
+#define RUST_HIR_SCAN_DEADCODE
+
+#include "rust-hir-full-decls.h"
+#include "rust-hir-map.h"
+#include "rust-lint-marklive.h"
+#include "rust-name-resolver.h"
+#include "rust-diagnostics.h"
+
+namespace Rust {
+namespace Analysis {
+
+// Scan item symbols and warn the symbol if it is not in the live_symbols set.
+// There are three kinds of item we should handle in this pass.
+// 1. Function item
+// 2. The function item in the impl block without trait
+// 3. StructStruct, e.g., `Struct Foo{one: 1, two: 2}`. Furthermore, the unused
+//    struct fields will be warned too.
+// 4. TupleStruct, e.g., `Struct Foo(i32, i32)`
+class ScanDeadcode : public MarkLiveBase
+{
+  using Rust::Analysis::MarkLiveBase::visit;
+
+public:
+  static void Scan (HIR::Crate &crate)
+  {
+    std::set<HirId> live_symbols = Analysis::MarkLive::Analysis (crate);
+    ScanDeadcode sdc (live_symbols);
+    for (auto it = crate.items.begin (); it != crate.items.end (); it++)
+      {
+	it->get ()->accept_vis (sdc);
+      }
+  };
+
+  void visit (HIR::Function &function) override
+  {
+    HirId hirId = function.get_mappings ().get_hirid ();
+    if (should_warn (hirId))
+      {
+	if (mappings->is_impl_item (hirId))
+	  {
+	    HIR::ImplBlock *implBlock
+	      = mappings->lookup_associated_impl (hirId);
+	    if (!implBlock->has_trait_ref ())
+	      {
+		rust_warning_at (function.get_locus (), 0,
+				 "associated function is never used: %<%s%>",
+				 function.get_function_name ().c_str ());
+	      }
+	  }
+	else
+	  {
+	    rust_warning_at (function.get_locus (), 0,
+			     "function is never used: %<%s%>",
+			     function.get_function_name ().c_str ());
+	  }
+      }
+  }
+
+  void visit (HIR::StructStruct &stct) override
+  {
+    HirId hirId = stct.get_mappings ().get_hirid ();
+    if (should_warn (hirId))
+      {
+	bool name_starts_underscore = stct.get_identifier ().at (0) == '_';
+	if (!name_starts_underscore)
+	  rust_warning_at (stct.get_locus (), 0,
+			   "struct is never constructed: %<%s%>",
+			   stct.get_identifier ().c_str ());
+      }
+    else
+      {
+	// only warn the unused fields when in unwarned struct.
+	for (auto &field : stct.get_fields ())
+	  {
+	    HirId field_hir_id = field.get_mappings ().get_hirid ();
+	    if (should_warn (field_hir_id))
+	      {
+		rust_warning_at (field.get_locus (), 0,
+				 "field is never read: %<%s%>",
+				 field.get_field_name ().c_str ());
+	      }
+	  }
+      }
+  }
+
+  void visit (HIR::TupleStruct &stct) override
+  {
+    // only warn tuple struct unconstructed, and ignoring unused field
+    HirId hirId = stct.get_mappings ().get_hirid ();
+    if (should_warn (hirId))
+      {
+	rust_warning_at (stct.get_locus (), 0,
+			 "struct is never constructed: %<%s%>",
+			 stct.get_identifier ().c_str ());
+      }
+  }
+
+  void visit (HIR::ImplBlock &blc) override
+  {
+    if (blc.has_impl_items ())
+      {
+	for (auto &implItem : blc.get_impl_items ())
+	  {
+	    implItem->accept_vis (*this);
+	  }
+      }
+  }
+
+  void visit (HIR::Module &mod) override
+  {
+    for (auto &item : mod.get_items ())
+      item->accept_vis (*this);
+  }
+
+private:
+  std::set<HirId> live_symbols;
+  Resolver::Resolver *resolver;
+  Analysis::Mappings *mappings;
+
+  ScanDeadcode (std::set<HirId> &live_symbols)
+    : live_symbols (live_symbols), resolver (Resolver::Resolver::get ()),
+      mappings (Analysis::Mappings::get ()){};
+
+  bool should_warn (HirId hirId)
+  {
+    // TODO: There are more condition to check if should warn, i.e visibility,
+    // attributes.
+    return live_symbols.find (hirId) == live_symbols.end ();
+  }
+};
+
+} // namespace Analysis
+} // namespace Rust
+
+#endif
-- 
2.25.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [PATCH Rust front-end v2 27/37] gccrs: Add unused variable scan
  2022-08-24 11:59 Rust frontend patches v2 herron.philip
                   ` (25 preceding siblings ...)
  2022-08-24 11:59 ` [PATCH Rust front-end v2 26/37] gccrs: Add dead code scan on HIR herron.philip
@ 2022-08-24 11:59 ` herron.philip
  2022-08-24 11:59 ` [PATCH Rust front-end v2 28/37] gccrs: Add metadata ouptput pass herron.philip
                   ` (10 subsequent siblings)
  37 siblings, 0 replies; 59+ messages in thread
From: herron.philip @ 2022-08-24 11:59 UTC (permalink / raw)
  To: gcc-patches; +Cc: gcc-rust, Philip Herron

From: Philip Herron <philip.herron@embecosm.com>

This is a simple walk_tree which acts on the monomorphized code. By walking
the compiled translation unit of functions.
---
 gcc/rust/checks/lints/rust-lint-unused-var.cc | 98 +++++++++++++++++++
 gcc/rust/checks/lints/rust-lint-unused-var.h  | 36 +++++++
 2 files changed, 134 insertions(+)
 create mode 100644 gcc/rust/checks/lints/rust-lint-unused-var.cc
 create mode 100644 gcc/rust/checks/lints/rust-lint-unused-var.h

diff --git a/gcc/rust/checks/lints/rust-lint-unused-var.cc b/gcc/rust/checks/lints/rust-lint-unused-var.cc
new file mode 100644
index 00000000000..d4317e53280
--- /dev/null
+++ b/gcc/rust/checks/lints/rust-lint-unused-var.cc
@@ -0,0 +1,98 @@
+// Copyright (C) 2021-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/>.
+
+#include "rust-lint-unused-var.h"
+#include "print-tree.h"
+
+namespace Rust {
+namespace Analysis {
+
+static void
+check_decl (tree *t)
+{
+  rust_assert (TREE_CODE (*t) == VAR_DECL || TREE_CODE (*t) == PARM_DECL
+	       || TREE_CODE (*t) == CONST_DECL);
+
+  tree var_name = DECL_NAME (*t);
+  const char *var_name_ptr = IDENTIFIER_POINTER (var_name);
+  bool starts_with_under_score = strncmp (var_name_ptr, "_", 1) == 0;
+
+  bool is_constant = TREE_CODE (*t) == CONST_DECL;
+  // if (!is_constant)
+  //   {
+  //     debug_tree (*t);
+  //     rust_debug ("found var-decl: used %s artifical %s underscore %s name
+  //     %s",
+  //       	  TREE_USED (*t) ? "true" : "false",
+  //       	  DECL_ARTIFICIAL (*t) ? "true" : "false",
+  //       	  starts_with_under_score ? "true" : "false", var_name_ptr);
+  //   }
+
+  if (!TREE_USED (*t) && !DECL_ARTIFICIAL (*t) && !starts_with_under_score)
+    {
+      warning_at (DECL_SOURCE_LOCATION (*t),
+		  is_constant ? OPT_Wunused_const_variable_
+			      : OPT_Wunused_variable,
+		  "unused name %qE", *t);
+    }
+}
+
+static tree
+unused_var_walk_fn (tree *t, int *walk_subtrees, void *closure)
+{
+  switch (TREE_CODE (*t))
+    {
+    case VAR_DECL:
+    case CONST_DECL:
+      check_decl (t);
+      break;
+
+    default:
+      break;
+    }
+  return NULL_TREE;
+}
+
+void
+UnusedVariables::Lint (Compile::Context &ctx)
+{
+  for (auto &fndecl : ctx.get_func_decls ())
+    {
+      for (tree p = DECL_ARGUMENTS (fndecl); p != NULL_TREE; p = DECL_CHAIN (p))
+	{
+	  check_decl (&p);
+	}
+
+      walk_tree_without_duplicates (&DECL_SAVED_TREE (fndecl),
+				    &unused_var_walk_fn, &ctx);
+    }
+
+  for (auto &var : ctx.get_var_decls ())
+    {
+      tree t = ctx.get_backend ()->var_expression (var, Location ());
+      check_decl (&t);
+    }
+
+  for (auto &const_decl : ctx.get_const_decls ())
+    {
+      check_decl (&const_decl);
+    }
+}
+
+} // namespace Analysis
+} // namespace Rust
diff --git a/gcc/rust/checks/lints/rust-lint-unused-var.h b/gcc/rust/checks/lints/rust-lint-unused-var.h
new file mode 100644
index 00000000000..6fabfeff01b
--- /dev/null
+++ b/gcc/rust/checks/lints/rust-lint-unused-var.h
@@ -0,0 +1,36 @@
+// Copyright (C) 2021-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_LINT_UNUSED_VAR
+#define RUST_LINT_UNUSED_VAR
+
+#include "rust-compile-context.h"
+
+namespace Rust {
+namespace Analysis {
+
+class UnusedVariables
+{
+public:
+  static void Lint (Compile::Context &ctx);
+};
+
+} // namespace Analysis
+} // namespace Rust
+
+#endif // RUST_LINT_UNUSED_VAR
-- 
2.25.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [PATCH Rust front-end v2 28/37] gccrs: Add metadata ouptput pass
  2022-08-24 11:59 Rust frontend patches v2 herron.philip
                   ` (26 preceding siblings ...)
  2022-08-24 11:59 ` [PATCH Rust front-end v2 27/37] gccrs: Add unused variable scan herron.philip
@ 2022-08-24 11:59 ` herron.philip
  2022-08-24 11:59 ` [PATCH Rust front-end v2 29/37] gccrs: HIR to GCC GENERIC lowering herron.philip
                   ` (9 subsequent siblings)
  37 siblings, 0 replies; 59+ messages in thread
From: herron.philip @ 2022-08-24 11:59 UTC (permalink / raw)
  To: gcc-patches; +Cc: gcc-rust, Philip Herron

From: Philip Herron <philip.herron@embecosm.com>

Extern crates statements to tell the front-end to look for another library.
The mechanism here is heavily inspired from gccgo, so when we compile a
library for example we invoke:

  gccrs -g -O2 -frust-crate=mylib -c src/lib.rs -o src/mylib.o

All going well this object file will now contain extra data inside
.rust-export section inside the object file which will be preserved inside
archives and shared objects. When we have another application which uses
this library 'mylib'.

  extern crate mylib;
  use mylib::foo;

  fn main() {
    foo();
  }

We compile using:

  gcc -g -O2 -frust-crate=test -c src/main.rs -o src/main.o

When the extern crate line is hit the front-end will look for mylib.o,
libmylib.a, mylib.rox. If it finds a raw object file it will read the
.rust-export section directly from the object for the public metadata
such as public functions, types constants etc. If it fails to find an
object it might find .rox which is the objdump of the .rust-export to a
raw file, it might even find libmylib.a and read the export directly out
of the archive file reusing code from gccgo to do so.

The full compiler pipeline is reused here, so the metatadata is actually
just real rust code. The benifit here is that Rust supports exporting,
macros and generics so this requires the name-resolution and type info
all to be generated and inserted into the apropriate context classes. Since
the metadata is real rust code it means we can reuse the full pipeline to
generate the code as nessecary. So for the simple case of a public struct
we simply emit the AST dump of this struct directly into the metadata. If
its a non-generic public function we emit and extern rust abi block for
that function. If its a trait we can simply emit the trait with the public
memebers. Generics are more complicated since we need to emit the function
fully for it to be compiled correctly this still needs tests to be added.
The hardest part is non generic impl blocks which is still a WIP.

To finally link the two crates together you run:

  gcc -g -O2 -o rust-program.exe src/main.o src/mylib.o
---
 gcc/rust/metadata/rust-export-metadata.cc | 385 ++++++++++
 gcc/rust/metadata/rust-export-metadata.h  |  85 +++
 gcc/rust/metadata/rust-extern-crate.cc    | 173 +++++
 gcc/rust/metadata/rust-extern-crate.h     |  55 ++
 gcc/rust/metadata/rust-import-archive.cc  | 885 ++++++++++++++++++++++
 gcc/rust/metadata/rust-imports.cc         | 441 +++++++++++
 gcc/rust/metadata/rust-imports.h          | 257 +++++++
 gcc/rust/rust-object-export.cc            | 177 +++++
 gcc/rust/rust-object-export.h             |  33 +
 9 files changed, 2491 insertions(+)
 create mode 100644 gcc/rust/metadata/rust-export-metadata.cc
 create mode 100644 gcc/rust/metadata/rust-export-metadata.h
 create mode 100644 gcc/rust/metadata/rust-extern-crate.cc
 create mode 100644 gcc/rust/metadata/rust-extern-crate.h
 create mode 100644 gcc/rust/metadata/rust-import-archive.cc
 create mode 100644 gcc/rust/metadata/rust-imports.cc
 create mode 100644 gcc/rust/metadata/rust-imports.h
 create mode 100644 gcc/rust/rust-object-export.cc
 create mode 100644 gcc/rust/rust-object-export.h

diff --git a/gcc/rust/metadata/rust-export-metadata.cc b/gcc/rust/metadata/rust-export-metadata.cc
new file mode 100644
index 00000000000..4856bc26149
--- /dev/null
+++ b/gcc/rust/metadata/rust-export-metadata.cc
@@ -0,0 +1,385 @@
+// 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/>.
+
+#include "rust-export-metadata.h"
+#include "rust-hir-visitor.h"
+#include "rust-hir-full.h"
+#include "rust-hir-map.h"
+#include "rust-ast-dump.h"
+#include "rust-abi.h"
+#include "rust-object-export.h"
+
+#include "md5.h"
+
+namespace Rust {
+namespace Metadata {
+
+static const std::string extension_path = ".rox";
+
+ExportContext::ExportContext () : mappings (Analysis::Mappings::get ()) {}
+
+ExportContext::~ExportContext () {}
+
+void
+ExportContext::push_module_scope (const HIR::Module &module)
+{
+  module_stack.push_back (module);
+}
+
+const HIR::Module &
+ExportContext::pop_module_scope ()
+{
+  rust_assert (!module_stack.empty ());
+  const HIR::Module &poped = module_stack.back ();
+  module_stack.pop_back ();
+  return poped;
+}
+
+void
+ExportContext::emit_trait (const HIR::Trait &trait)
+{
+  // lookup the AST node for this
+  AST::Item *item = nullptr;
+  bool ok
+    = mappings->lookup_ast_item (trait.get_mappings ().get_nodeid (), &item);
+  rust_assert (ok);
+
+  std::stringstream oss;
+  AST::Dump dumper (oss);
+  dumper.go (*item);
+
+  public_interface_buffer += oss.str ();
+}
+
+void
+ExportContext::emit_function (const HIR::Function &fn)
+{
+  // lookup the AST node for this
+  AST::Item *item = nullptr;
+  bool ok = mappings->lookup_ast_item (fn.get_mappings ().get_nodeid (), &item);
+  rust_assert (ok);
+
+  // is this a CFG macro or not
+  if (item->is_marked_for_strip ())
+    return;
+
+  // FIXME add assertion that item must be a vis_item;
+  AST::VisItem &vis_item = static_cast<AST::VisItem &> (*item);
+
+  // if its a generic function we need to output the full declaration
+  // otherwise we can let people link against this
+
+  std::stringstream oss;
+  AST::Dump dumper (oss);
+  if (!fn.has_generics ())
+    {
+      // FIXME assert that this is actually an AST::Function
+      AST::Function &function = static_cast<AST::Function &> (vis_item);
+
+      // we can emit an extern block with abi of "rust"
+      Identifier item_name = function.get_function_name ();
+
+      // always empty for extern linkage
+      AST::WhereClause where_clause = AST::WhereClause::create_empty ();
+      std::vector<std::unique_ptr<AST::GenericParam>> generic_params;
+
+      AST::Visibility vis = function.get_visibility ();
+      std::unique_ptr<AST::Type> return_type
+	= std::unique_ptr<AST::Type> (nullptr);
+      if (function.has_return_type ())
+	{
+	  return_type = function.get_return_type ()->clone_type ();
+	}
+
+      std::vector<AST::NamedFunctionParam> function_params;
+      for (AST::FunctionParam &param : function.get_function_params ())
+	{
+	  std::string name = param.get_pattern ()->as_string ();
+	  std::unique_ptr<AST::Type> param_type
+	    = param.get_type ()->clone_type ();
+
+	  AST::NamedFunctionParam p (name, std::move (param_type), {},
+				     param.get_locus ());
+	  function_params.push_back (std::move (p));
+	}
+
+      AST::ExternalItem *external_item = new AST::ExternalFunctionItem (
+	item_name, {} /* generic_params */, std::move (return_type),
+	where_clause, std::move (function_params), false /* has_variadics */,
+	{} /* variadic_outer_attrs */, vis, function.get_outer_attrs (),
+	function.get_locus ());
+
+      std::vector<std::unique_ptr<AST::ExternalItem>> external_items;
+      external_items.push_back (
+	std::unique_ptr<AST::ExternalItem> (external_item));
+
+      AST::ExternBlock extern_block (get_string_from_abi (Rust::ABI::RUST),
+				     std::move (external_items),
+				     vis_item.get_visibility (), {}, {},
+				     fn.get_locus ());
+
+      dumper.go (extern_block);
+    }
+  else
+    {
+      dumper.go (*item);
+    }
+
+  // store the dump
+  public_interface_buffer += oss.str ();
+}
+
+const std::string &
+ExportContext::get_interface_buffer () const
+{
+  return public_interface_buffer;
+}
+
+// implicitly by using HIR nodes we know that these have passed CFG expansion
+// and they exist in the compilation unit
+class ExportVisItems : public HIR::HIRVisItemVisitor
+{
+public:
+  ExportVisItems (ExportContext &context) : ctx (context) {}
+
+  void visit (HIR::Module &module) override {}
+  void visit (HIR::ExternCrate &crate) override {}
+  void visit (HIR::UseDeclaration &use_decl) override {}
+  void visit (HIR::TypeAlias &type_alias) override {}
+  void visit (HIR::StructStruct &struct_item) override {}
+  void visit (HIR::TupleStruct &tuple_struct) override {}
+  void visit (HIR::Enum &enum_item) override {}
+  void visit (HIR::Union &union_item) override {}
+  void visit (HIR::ConstantItem &const_item) override {}
+  void visit (HIR::StaticItem &static_item) override {}
+  void visit (HIR::ImplBlock &impl) override {}
+  void visit (HIR::ExternBlock &block) override {}
+
+  void visit (HIR::Trait &trait) override { ctx.emit_trait (trait); }
+
+  void visit (HIR::Function &function) override
+  {
+    ctx.emit_function (function);
+  }
+
+private:
+  ExportContext &ctx;
+};
+
+PublicInterface::PublicInterface (HIR::Crate &crate)
+  : crate (crate), mappings (*Analysis::Mappings::get ()), context ()
+{}
+
+void
+PublicInterface::Export (HIR::Crate &crate)
+{
+  PublicInterface interface (crate);
+  interface.gather_export_data ();
+  interface.write_to_object_file ();
+}
+
+void
+PublicInterface::ExportTo (HIR::Crate &crate, const std::string &output_path)
+{
+  PublicInterface interface (crate);
+  interface.gather_export_data ();
+  interface.write_to_path (output_path);
+}
+
+void
+PublicInterface::gather_export_data ()
+{
+  ExportVisItems visitor (context);
+  for (auto &item : crate.items)
+    {
+      bool is_vis_item = item->get_hir_kind () == HIR::Node::BaseKind::VIS_ITEM;
+      if (!is_vis_item)
+	continue;
+
+      HIR::VisItem &vis_item = static_cast<HIR::VisItem &> (*item.get ());
+      if (is_crate_public (vis_item))
+	vis_item.accept_vis (visitor);
+    }
+}
+
+void
+PublicInterface::write_to_object_file () const
+{
+  // done
+  const auto &buf = context.get_interface_buffer ();
+  std::string size_buffer = std::to_string (buf.size ());
+
+  // md5 this
+  struct md5_ctx chksm;
+  unsigned char checksum[16];
+
+  md5_init_ctx (&chksm);
+  md5_process_bytes (buf.c_str (), buf.size (), &chksm);
+  md5_finish_ctx (&chksm, checksum);
+
+  // MAGIC MD5 DLIM  DLIM buffer-size DELIM contents
+  const std::string current_crate_name = mappings.get_current_crate_name ();
+
+  // extern void
+  rust_write_export_data (kMagicHeader, sizeof (kMagicHeader));
+  rust_write_export_data ((const char *) checksum, sizeof (checksum));
+  rust_write_export_data (kSzDelim, sizeof (kSzDelim));
+  rust_write_export_data (current_crate_name.c_str (),
+			  current_crate_name.size ());
+  rust_write_export_data (kSzDelim, sizeof (kSzDelim));
+  rust_write_export_data (size_buffer.c_str (), size_buffer.size ());
+  rust_write_export_data (kSzDelim, sizeof (kSzDelim));
+  rust_write_export_data (buf.c_str (), buf.size ());
+}
+
+void
+PublicInterface::write_to_path (const std::string &path) const
+{
+  // validate path contains correct extension
+  const std::string expected_file_name = expected_metadata_filename ();
+  const char *path_base_name = basename (path.c_str ());
+  if (strcmp (path_base_name, expected_file_name.c_str ()) != 0)
+    {
+      rust_error_at (Location (),
+		     "expected metadata-output path to have base file name of: "
+		     "%<%s%> got %<%s%>",
+		     expected_file_name.c_str (), path_base_name);
+      return;
+    }
+
+  // done
+  const auto &buf = context.get_interface_buffer ();
+  std::string size_buffer = std::to_string (buf.size ());
+
+  // md5 this
+  struct md5_ctx chksm;
+  unsigned char checksum[16];
+
+  md5_init_ctx (&chksm);
+  md5_process_bytes (buf.c_str (), buf.size (), &chksm);
+  md5_finish_ctx (&chksm, checksum);
+
+  // MAGIC MD5 DLIM  DLIM buffer-size DELIM contents
+  const std::string current_crate_name = mappings.get_current_crate_name ();
+
+  // write to path
+  FILE *nfd = fopen (path.c_str (), "wb");
+  if (nfd == NULL)
+    {
+      rust_error_at (Location (), "failed to open file %<%s%> for writing: %s",
+		     path.c_str (), xstrerror (errno));
+      return;
+    }
+
+  // write data
+  if (fwrite (kMagicHeader, sizeof (kMagicHeader), 1, nfd) < 1)
+    {
+      rust_error_at (Location (), "failed to write to file %<%s%>: %s",
+		     path.c_str (), xstrerror (errno));
+      fclose (nfd);
+      return;
+    }
+
+  if (fwrite (checksum, sizeof (checksum), 1, nfd) < 1)
+    {
+      rust_error_at (Location (), "failed to write to file %<%s%>: %s",
+		     path.c_str (), xstrerror (errno));
+      fclose (nfd);
+      return;
+    }
+
+  if (fwrite (kSzDelim, sizeof (kSzDelim), 1, nfd) < 1)
+    {
+      rust_error_at (Location (), "failed to write to file %<%s%>: %s",
+		     path.c_str (), xstrerror (errno));
+      fclose (nfd);
+      return;
+    }
+
+  if (fwrite (current_crate_name.c_str (), current_crate_name.size (), 1, nfd)
+      < 1)
+    {
+      rust_error_at (Location (), "failed to write to file %<%s%>: %s",
+		     path.c_str (), xstrerror (errno));
+      fclose (nfd);
+      return;
+    }
+
+  if (fwrite (kSzDelim, sizeof (kSzDelim), 1, nfd) < 1)
+    {
+      rust_error_at (Location (), "failed to write to file %<%s%>: %s",
+		     path.c_str (), xstrerror (errno));
+      fclose (nfd);
+      return;
+    }
+
+  if (fwrite (size_buffer.c_str (), size_buffer.size (), 1, nfd) < 1)
+    {
+      rust_error_at (Location (), "failed to write to file %<%s%>: %s",
+		     path.c_str (), xstrerror (errno));
+      fclose (nfd);
+      return;
+    }
+
+  if (fwrite (kSzDelim, sizeof (kSzDelim), 1, nfd) < 1)
+    {
+      rust_error_at (Location (), "failed to write to file %<%s%>: %s",
+		     path.c_str (), xstrerror (errno));
+      fclose (nfd);
+      return;
+    }
+
+  if (!buf.empty ())
+    if (fwrite (buf.c_str (), buf.size (), 1, nfd) < 1)
+      {
+	rust_error_at (Location (), "failed to write to file %<%s%>: %s",
+		       path.c_str (), xstrerror (errno));
+	fclose (nfd);
+	return;
+      }
+
+  // done
+  fclose (nfd);
+}
+
+bool
+PublicInterface::is_crate_public (const HIR::VisItem &item)
+{
+  const HIR::Visibility &visibility = item.get_visibility ();
+
+  bool is_public
+    = visibility.get_vis_type () == HIR::Visibility::VisType::PUBLIC;
+  bool has_path = !visibility.get_path ().is_error ();
+
+  // FIXME this might be pub(crate)
+  // Arthur magic required here
+
+  return is_public && !has_path;
+}
+
+std::string
+PublicInterface::expected_metadata_filename ()
+{
+  auto mappings = Analysis::Mappings::get ();
+
+  const std::string current_crate_name = mappings->get_current_crate_name ();
+  return current_crate_name + extension_path;
+}
+
+} // namespace Metadata
+} // namespace Rust
diff --git a/gcc/rust/metadata/rust-export-metadata.h b/gcc/rust/metadata/rust-export-metadata.h
new file mode 100644
index 00000000000..cbb6ecd65a6
--- /dev/null
+++ b/gcc/rust/metadata/rust-export-metadata.h
@@ -0,0 +1,85 @@
+// 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_EXPORT_METADATA_H
+#define RUST_EXPORT_METADATA_H
+
+#include "rust-system.h"
+#include "rust-hir-full-decls.h"
+#include "rust-hir-map.h"
+
+namespace Rust {
+namespace Metadata {
+
+static const char kMagicHeader[4] = {'G', 'R', 'S', 'T'};
+static const char kSzDelim[1] = {'$'};
+
+class ExportContext
+{
+public:
+  ExportContext ();
+
+  ~ExportContext ();
+
+  void push_module_scope (const HIR::Module &module);
+
+  const HIR::Module &pop_module_scope ();
+
+  void emit_trait (const HIR::Trait &trait);
+
+  void emit_function (const HIR::Function &fn);
+
+  const std::string &get_interface_buffer () const;
+
+private:
+  Analysis::Mappings *mappings;
+
+  std::vector<std::reference_wrapper<const HIR::Module>> module_stack;
+  std::string public_interface_buffer;
+};
+
+class PublicInterface
+{
+public:
+  static void Export (HIR::Crate &crate);
+
+  static void ExportTo (HIR::Crate &crate, const std::string &output_path);
+
+  static bool is_crate_public (const HIR::VisItem &item);
+
+  static std::string expected_metadata_filename ();
+
+protected:
+  void gather_export_data ();
+
+  void write_to_object_file () const;
+
+  void write_to_path (const std::string &path) const;
+
+private:
+  PublicInterface (HIR::Crate &crate);
+
+  HIR::Crate &crate;
+  Analysis::Mappings &mappings;
+  ExportContext context;
+};
+
+} // namespace Metadata
+} // namespace Rust
+
+#endif // RUST_EXPORT_METADATA_H
diff --git a/gcc/rust/metadata/rust-extern-crate.cc b/gcc/rust/metadata/rust-extern-crate.cc
new file mode 100644
index 00000000000..614a6d91729
--- /dev/null
+++ b/gcc/rust/metadata/rust-extern-crate.cc
@@ -0,0 +1,173 @@
+// 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/>.
+
+#include "rust-extern-crate.h"
+#include "rust-diagnostics.h"
+#include "rust-export-metadata.h"
+
+#include "md5.h"
+
+namespace Rust {
+namespace Imports {
+
+ExternCrate::ExternCrate (Import::Stream &stream) : import_stream (stream) {}
+
+ExternCrate::~ExternCrate () {}
+
+bool
+ExternCrate::ok () const
+{
+  return !import_stream.saw_error ();
+}
+
+bool
+ExternCrate::load (Location locus)
+{
+  // match header
+  import_stream.require_bytes (locus, Metadata::kMagicHeader,
+			       sizeof (Metadata::kMagicHeader));
+  if (import_stream.saw_error ())
+    return false;
+
+  // parse 16 bytes md5
+  unsigned char checksum[16];
+  bool ok
+    = import_stream.do_peek (sizeof (checksum), (const char **) &checksum);
+  if (!ok)
+    return false;
+
+  import_stream.advance (sizeof (checksum));
+
+  // parse delim
+  import_stream.require_bytes (locus, Metadata::kSzDelim,
+			       sizeof (Metadata::kSzDelim));
+  if (import_stream.saw_error ())
+    return false;
+
+  // parse crate name
+  bool saw_delim = false;
+  while (!import_stream.saw_error () && !import_stream.at_eof ())
+    {
+      unsigned char byte = import_stream.get_char ();
+      saw_delim
+	= memcmp (&byte, Metadata::kSzDelim, sizeof (Metadata::kSzDelim)) == 0;
+      if (saw_delim)
+	break;
+
+      crate_name += byte;
+    }
+  if (!saw_delim || crate_name.empty ())
+    {
+      import_stream.set_saw_error ();
+      rust_error_at (locus, "failed to read crate name field");
+
+      return false;
+    }
+
+  // read until delim which is the size of the meta data
+  std::string metadata_length_buffer;
+  saw_delim = false;
+  while (!import_stream.saw_error () && !import_stream.at_eof ())
+    {
+      unsigned char byte = import_stream.get_char ();
+      saw_delim
+	= memcmp (&byte, Metadata::kSzDelim, sizeof (Metadata::kSzDelim)) == 0;
+      if (saw_delim)
+	break;
+
+      metadata_length_buffer += byte;
+    }
+  if (!saw_delim || metadata_length_buffer.empty ())
+    {
+      import_stream.set_saw_error ();
+      rust_error_at (locus, "failed to read metatadata size");
+
+      return false;
+    }
+
+  // interpret the string size
+  int expected_buffer_length = -1;
+  ok = ExternCrate::string_to_int (locus, metadata_length_buffer, false,
+				   &expected_buffer_length);
+  if (!ok)
+    return false;
+
+  // read the parsed size and it should be eof
+  metadata_buffer.reserve (expected_buffer_length);
+  for (int i = 0; i < expected_buffer_length && !import_stream.saw_error ()
+		  && !import_stream.at_eof ();
+       i++)
+    {
+      metadata_buffer += import_stream.get_char ();
+    }
+
+  // compute the md5
+  struct md5_ctx chksm;
+  unsigned char computed_checksum[16];
+
+  md5_init_ctx (&chksm);
+  md5_process_bytes (metadata_buffer.c_str (), metadata_buffer.size (), &chksm);
+  md5_finish_ctx (&chksm, computed_checksum);
+
+  // FIXME i think the encoding and decoding of md5 is going wrong or else we
+  // are not computing it correctly
+  //
+  // compare the checksums
+  // if (memcmp(computed_checksum, checksum, sizeof (checksum)) != 0)
+  //   {
+  //     rust_error_at (locus,
+  //       	     "checksum mismatch in metadata: %<%.*s%> vs %<%.*s%>",
+  //       	     sizeof (computed_checksum), computed_checksum,
+  //       	     sizeof (checksum), checksum);
+  //     return false;
+  //   }
+
+  // all good
+  return true;
+}
+
+const std::string &
+ExternCrate::get_crate_name () const
+{
+  return crate_name;
+}
+
+const std::string &
+ExternCrate::get_metadata () const
+{
+  return metadata_buffer;
+}
+
+// Turn a string into a integer with appropriate error handling.
+bool
+ExternCrate::string_to_int (Location locus, const std::string &s,
+			    bool is_neg_ok, int *ret)
+{
+  char *end;
+  long prio = strtol (s.c_str (), &end, 10);
+  if (*end != '\0' || prio > 0x7fffffff || (prio < 0 && !is_neg_ok))
+    {
+      rust_error_at (locus, "invalid integer in import data");
+      return false;
+    }
+  *ret = prio;
+  return true;
+}
+
+} // namespace Imports
+} // namespace Rust
diff --git a/gcc/rust/metadata/rust-extern-crate.h b/gcc/rust/metadata/rust-extern-crate.h
new file mode 100644
index 00000000000..66da83894c1
--- /dev/null
+++ b/gcc/rust/metadata/rust-extern-crate.h
@@ -0,0 +1,55 @@
+// 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_EXTERN_CRATE_H
+#define RUST_EXTERN_CRATE_H
+
+#include "rust-system.h"
+#include "rust-imports.h"
+
+namespace Rust {
+namespace Imports {
+
+class ExternCrate
+{
+public:
+  ExternCrate (Import::Stream &stream);
+  ~ExternCrate ();
+
+  bool ok () const;
+
+  bool load (Location locus);
+
+  const std::string &get_crate_name () const;
+
+  const std::string &get_metadata () const;
+
+  static bool string_to_int (Location locus, const std::string &s,
+			     bool is_neg_ok, int *ret);
+
+private:
+  Import::Stream &import_stream;
+
+  std::string crate_name;
+  std::string metadata_buffer;
+};
+
+} // namespace Imports
+} // namespace Rust
+
+#endif // RUST_EXTERN_CRATE_H
diff --git a/gcc/rust/metadata/rust-import-archive.cc b/gcc/rust/metadata/rust-import-archive.cc
new file mode 100644
index 00000000000..5678d486f17
--- /dev/null
+++ b/gcc/rust/metadata/rust-import-archive.cc
@@ -0,0 +1,885 @@
+// import-archive.cc -- Go frontend read import data from an archive file.
+
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "rust-system.h"
+#include "rust-diagnostics.h"
+#include "rust-imports.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+// Archive magic numbers.
+
+static const char armag[] = {'!', '<', 'a', 'r', 'c', 'h', '>', '\n'};
+static const char armagt[] = {'!', '<', 't', 'h', 'i', 'n', '>', '\n'};
+static const char armagb[] = {'<', 'b', 'i', 'g', 'a', 'f', '>', '\n'};
+static const char arfmag[2] = {'`', '\n'};
+
+namespace Rust {
+
+// Archive fixed length header for AIX big format.
+
+struct Archive_fl_header
+{
+  // Archive magic string.
+  char fl_magic[8];
+  // Offset to member table.
+  char fl_memoff[20];
+  // Offset to global symbol table.
+  char fl_gstoff[20];
+  // Offset to global symbol table for 64-bit objects.
+  char fl_gst64off[20];
+  // Offset to first archive member.
+  char fl_fstmoff[20];
+  // Offset to last archive member.
+  char fl_lstmoff[20];
+  // Offset to first member on free list.
+  char fl_freeoff[20];
+};
+
+// The header of an entry in an archive.  This is all readable text,
+// padded with spaces where necesary.
+
+struct Archive_header
+{
+  // The entry name.
+  char ar_name[16];
+  // The file modification time.
+  char ar_date[12];
+  // The user's UID in decimal.
+  char ar_uid[6];
+  // The user's GID in decimal.
+  char ar_gid[6];
+  // The file mode in octal.
+  char ar_mode[8];
+  // The file size in decimal.
+  char ar_size[10];
+  // The final magic code.
+  char ar_fmag[2];
+};
+
+// The header of an entry in an AIX big archive.
+// This is followed by ar_namlen bytes + 2 bytes for arfmag.
+
+struct Archive_big_header
+{
+  // The file size in decimal.
+  char ar_size[20];
+  // The next member offset in decimal.
+  char ar_nxtmem[20];
+  // The previous member offset in decimal.
+  char ar_prvmem[20];
+  // The file modification time in decimal.
+  char ar_date[12];
+  // The user's UID in decimal.
+  char ar_uid[12];
+  // The user's GID in decimal.
+  char ar_gid[12];
+  // The file mode in octal.
+  char ar_mode[12];
+  // The file name length in decimal.
+  char ar_namlen[4];
+};
+
+// Return true if BYTES, which are from the start of the file, are an
+// archive magic number.
+
+bool
+Import::is_archive_magic (const char *bytes)
+{
+  const int archive_magic_len = 8;
+  return (memcmp (bytes, armag, archive_magic_len) == 0
+	  || memcmp (bytes, armagt, archive_magic_len) == 0
+	  || memcmp (bytes, armagb, archive_magic_len) == 0);
+}
+
+// An object used to read an archive file.
+
+class Archive_file
+{
+public:
+  Archive_file (const std::string &filename, int fd, Location location)
+    : filename_ (filename), fd_ (fd), filesize_ (-1), first_member_offset_ (0),
+      extended_names_ (), is_thin_archive_ (false), is_big_archive_ (false),
+      location_ (location), nested_archives_ ()
+  {}
+
+  // Initialize.
+  bool initialize ();
+
+  // Return the file name.
+  const std::string &filename () const { return this->filename_; }
+
+  // Get the file size.
+  off_t filesize () const { return this->filesize_; }
+
+  // Return the offset of the first member.
+  off_t first_member_offset () const { return this->first_member_offset_; }
+
+  // Return whether this is a thin archive.
+  bool is_thin_archive () const { return this->is_thin_archive_; }
+
+  // Return whether this is a big archive.
+  bool is_big_archive () const { return this->is_big_archive_; }
+
+  // Return the location of the import statement.
+  Location location () const { return this->location_; }
+
+  // Read bytes.
+  bool read (off_t offset, off_t size, char *);
+
+  // Parse a decimal in readable text.
+  bool parse_decimal (const char *str, off_t size, long *res) const;
+
+  // Read the archive header at OFF, setting *PNAME, *SIZE,
+  // *NESTED_OFF and *NEXT_OFF.
+  bool read_header (off_t off, std::string *pname, off_t *size,
+		    off_t *nested_off, off_t *next_off);
+
+  // Interpret the header of HDR, the header of the archive member at
+  // file offset OFF.  Return whether it succeeded.  Set *SIZE to the
+  // size of the member.  Set *PNAME to the name of the member.  Set
+  // *NESTED_OFF to the offset in a nested archive.
+  bool interpret_header (const Archive_header *hdr, off_t off,
+			 std::string *pname, off_t *size,
+			 off_t *nested_off) const;
+
+  // Get the file and offset for an archive member.
+  bool get_file_and_offset (off_t off, const std::string &hdrname,
+			    off_t nested_off, int *memfd, off_t *memoff,
+			    std::string *memname);
+
+private:
+  // Initialize a big archive (AIX)
+  bool initialize_big_archive ();
+
+  // Initialize a normal archive
+  bool initialize_archive ();
+
+  // Read the big archive header at OFF, setting *PNAME, *SIZE and *NEXT_OFF.
+  bool read_big_archive_header (off_t off, std::string *pname, off_t *size,
+				off_t *next_off);
+
+  // Read the normal archive header at OFF, setting *PNAME, *SIZE,
+  // *NESTED_OFF and *NEXT_OFF.
+  bool read_archive_header (off_t off, std::string *pname, off_t *size,
+			    off_t *nested_off, off_t *next_off);
+
+  // For keeping track of open nested archives in a thin archive file.
+  typedef std::map<std::string, Archive_file *> Nested_archive_table;
+
+  // The name of the file.
+  std::string filename_;
+  // The file descriptor.
+  int fd_;
+  // The file size;
+  off_t filesize_;
+  // The first member offset;
+  off_t first_member_offset_;
+  // The extended name table.
+  std::string extended_names_;
+  // Whether this is a thin archive.
+  bool is_thin_archive_;
+  // Whether this is a big archive.
+  bool is_big_archive_;
+  // The location of the import statements.
+  Location location_;
+  // Table of nested archives.
+  Nested_archive_table nested_archives_;
+};
+
+bool
+Archive_file::initialize ()
+{
+  struct stat st;
+  if (fstat (this->fd_, &st) < 0)
+    {
+      rust_error_at (this->location_, "%s: %m", this->filename_.c_str ());
+      return false;
+    }
+  this->filesize_ = st.st_size;
+
+  char buf[sizeof (armagt)];
+  if (::lseek (this->fd_, 0, SEEK_SET) < 0
+      || ::read (this->fd_, buf, sizeof (armagt)) != sizeof (armagt))
+    {
+      rust_error_at (this->location_, "%s: %m", this->filename_.c_str ());
+      return false;
+    }
+  if (memcmp (buf, armagt, sizeof (armagt)) == 0)
+    this->is_thin_archive_ = true;
+  else if (memcmp (buf, armagb, sizeof (armagb)) == 0)
+    this->is_big_archive_ = true;
+
+  if (this->is_big_archive_)
+    return this->initialize_big_archive ();
+  else
+    return this->initialize_archive ();
+}
+
+// Initialize a big archive (AIX).
+
+bool
+Archive_file::initialize_big_archive ()
+{
+  Archive_fl_header flhdr;
+
+  // Read the fixed length header.
+  if (::lseek (this->fd_, 0, SEEK_SET) < 0
+      || ::read (this->fd_, &flhdr, sizeof (flhdr)) != sizeof (flhdr))
+    {
+      rust_error_at (this->location_, "%s: could not read archive header",
+		     this->filename_.c_str ());
+      return false;
+    }
+
+  // Parse offset of the first member.
+  long off;
+  if (!this->parse_decimal (flhdr.fl_fstmoff, sizeof (flhdr.fl_fstmoff), &off))
+    {
+      char *buf = new char[sizeof (flhdr.fl_fstmoff) + 1];
+      memcpy (buf, flhdr.fl_fstmoff, sizeof (flhdr.fl_fstmoff));
+      rust_error_at (this->location_,
+		     ("%s: malformed first member offset in archive header"
+		      " (expected decimal, got %s)"),
+		     this->filename_.c_str (), buf);
+      delete[] buf;
+      return false;
+    }
+  if (off == 0) // Empty archive.
+    this->first_member_offset_ = this->filesize_;
+  else
+    this->first_member_offset_ = off;
+  return true;
+}
+
+// Initialize a normal archive.
+
+bool
+Archive_file::initialize_archive ()
+{
+  this->first_member_offset_ = sizeof (armag);
+  if (this->first_member_offset_ == this->filesize_)
+    {
+      // Empty archive.
+      return true;
+    }
+
+  // Look for the extended name table.
+  std::string filename;
+  off_t size;
+  off_t next_off;
+  if (!this->read_header (this->first_member_offset_, &filename, &size, NULL,
+			  &next_off))
+    return false;
+  if (filename.empty ())
+    {
+      // We found the symbol table.
+      if (!this->read_header (next_off, &filename, &size, NULL, NULL))
+	filename.clear ();
+    }
+  if (filename == "/")
+    {
+      char *rdbuf = new char[size];
+      if (::read (this->fd_, rdbuf, size) != size)
+	{
+	  rust_error_at (this->location_, "%s: could not read extended names",
+			 filename.c_str ());
+	  delete[] rdbuf;
+	  return false;
+	}
+      this->extended_names_.assign (rdbuf, size);
+      delete[] rdbuf;
+    }
+
+  return true;
+}
+
+// Read bytes from the file.
+
+bool
+Archive_file::read (off_t offset, off_t size, char *buf)
+{
+  if (::lseek (this->fd_, offset, SEEK_SET) < 0
+      || ::read (this->fd_, buf, size) != size)
+    {
+      rust_error_at (this->location_, "%s: %m", this->filename_.c_str ());
+      return false;
+    }
+  return true;
+}
+
+// Parse a decimal in readable text.
+
+bool
+Archive_file::parse_decimal (const char *str, off_t size, long *res) const
+{
+  char *buf = new char[size + 1];
+  memcpy (buf, str, size);
+  char *ps = buf + size;
+  while (ps > buf && ps[-1] == ' ')
+    --ps;
+  *ps = '\0';
+
+  errno = 0;
+  char *end;
+  *res = strtol (buf, &end, 10);
+  if (*end != '\0' || *res < 0 || (*res == LONG_MAX && errno == ERANGE))
+    {
+      delete[] buf;
+      return false;
+    }
+  delete[] buf;
+  return true;
+}
+
+// Read the header at OFF.  Set *PNAME to the name, *SIZE to the size,
+// *NESTED_OFF to the nested offset, and *NEXT_OFF to the next member offset.
+
+bool
+Archive_file::read_header (off_t off, std::string *pname, off_t *size,
+			   off_t *nested_off, off_t *next_off)
+{
+  if (::lseek (this->fd_, off, SEEK_SET) < 0)
+    {
+      rust_error_at (this->location_, "%s: %m", this->filename_.c_str ());
+      return false;
+    }
+  if (this->is_big_archive_)
+    return this->read_big_archive_header (off, pname, size, next_off);
+  else
+    return this->read_archive_header (off, pname, size, nested_off, next_off);
+}
+
+// Read the big archive header at OFF, setting *PNAME, *SIZE and *NEXT_OFF.
+
+bool
+Archive_file::read_big_archive_header (off_t off, std::string *pname,
+				       off_t *size, off_t *next_off)
+{
+  Archive_big_header hdr;
+  ssize_t got;
+
+  got = ::read (this->fd_, &hdr, sizeof hdr);
+  if (got != sizeof hdr)
+    {
+      if (got < 0)
+	rust_error_at (this->location_, "%s: %m", this->filename_.c_str ());
+      else if (got > 0)
+	rust_error_at (this->location_, "%s: short entry header at %ld",
+		       this->filename_.c_str (), static_cast<long> (off));
+      else
+	rust_error_at (this->location_, "%s: unexpected EOF at %ld",
+		       this->filename_.c_str (), static_cast<long> (off));
+    }
+
+  long local_size;
+  if (!this->parse_decimal (hdr.ar_size, sizeof (hdr.ar_size), &local_size))
+    {
+      char *buf = new char[sizeof (hdr.ar_size) + 1];
+      memcpy (buf, hdr.ar_size, sizeof (hdr.ar_size));
+      rust_error_at (this->location_,
+		     ("%s: malformed size in entry header at %ld"
+		      " (expected decimal, got %s)"),
+		     this->filename_.c_str (), static_cast<long> (off), buf);
+      delete[] buf;
+      return false;
+    }
+  *size = local_size;
+
+  long namlen;
+  if (!this->parse_decimal (hdr.ar_namlen, sizeof (hdr.ar_namlen), &namlen))
+    {
+      char *buf = new char[sizeof (hdr.ar_namlen) + 1];
+      memcpy (buf, hdr.ar_namlen, sizeof (hdr.ar_namlen));
+      rust_error_at (this->location_,
+		     ("%s: malformed name length in entry header at %ld"
+		      " (expected decimal, got %s)"),
+		     this->filename_.c_str (), static_cast<long> (off), buf);
+      delete[] buf;
+      return false;
+    }
+  // Read member name following member header.
+  char *rdbuf = new char[namlen];
+  got = ::read (this->fd_, rdbuf, namlen);
+  if (got != namlen)
+    {
+      rust_error_at (this->location_,
+		     "%s: malformed member name in entry header at %ld",
+		     this->filename_.c_str (), static_cast<long> (off));
+      delete[] rdbuf;
+      return false;
+    }
+  pname->assign (rdbuf, namlen);
+  delete[] rdbuf;
+
+  long local_next_off;
+  if (!this->parse_decimal (hdr.ar_nxtmem, sizeof (hdr.ar_nxtmem),
+			    &local_next_off))
+    {
+      char *buf = new char[sizeof (hdr.ar_nxtmem) + 1];
+      memcpy (buf, hdr.ar_nxtmem, sizeof (hdr.ar_nxtmem));
+      rust_error_at (this->location_,
+		     ("%s: malformed next member offset in entry header at %ld"
+		      " (expected decimal, got %s)"),
+		     this->filename_.c_str (), static_cast<long> (off), buf);
+      delete[] buf;
+      return false;
+    }
+  if (next_off != NULL)
+    {
+      if (local_next_off == 0) // Last member.
+	*next_off = this->filesize_;
+      else
+	*next_off = local_next_off;
+    }
+  return true;
+}
+
+// Read the normal archive header at OFF, setting *PNAME, *SIZE,
+// *NESTED_OFF and *NEXT_OFF.
+
+bool
+Archive_file::read_archive_header (off_t off, std::string *pname, off_t *size,
+				   off_t *nested_off, off_t *next_off)
+{
+  Archive_header hdr;
+  ssize_t got = ::read (this->fd_, &hdr, sizeof hdr);
+  if (got != sizeof hdr)
+    {
+      if (got < 0)
+	rust_error_at (this->location_, "%s: %m", this->filename_.c_str ());
+      else if (got > 0)
+	rust_error_at (this->location_, "%s: short archive header at %ld",
+		       this->filename_.c_str (), static_cast<long> (off));
+      else
+	rust_error_at (this->location_, "%s: unexpected EOF at %ld",
+		       this->filename_.c_str (), static_cast<long> (off));
+    }
+  off_t local_nested_off;
+  if (!this->interpret_header (&hdr, off, pname, size, &local_nested_off))
+    return false;
+  if (nested_off != NULL)
+    *nested_off = local_nested_off;
+
+  off_t local_next_off;
+  local_next_off = off + sizeof (Archive_header);
+  if (!this->is_thin_archive_ || pname->empty () || *pname == "/")
+    local_next_off += *size;
+  if ((local_next_off & 1) != 0)
+    ++local_next_off;
+  if (local_next_off > this->filesize_) // Last member.
+    local_next_off = this->filesize_;
+  if (next_off != NULL)
+    *next_off = local_next_off;
+  return true;
+}
+
+// Interpret the header of HDR, the header of the archive member at
+// file offset OFF.
+
+bool
+Archive_file::interpret_header (const Archive_header *hdr, off_t off,
+				std::string *pname, off_t *size,
+				off_t *nested_off) const
+{
+  if (memcmp (hdr->ar_fmag, arfmag, sizeof arfmag) != 0)
+    {
+      rust_error_at (this->location_, "%s: malformed archive header at %lu",
+		     this->filename_.c_str (),
+		     static_cast<unsigned long> (off));
+      return false;
+    }
+
+  long local_size;
+  if (!this->parse_decimal (hdr->ar_size, sizeof hdr->ar_size, &local_size))
+    {
+      rust_error_at (this->location_,
+		     "%s: malformed archive header size at %lu",
+		     this->filename_.c_str (),
+		     static_cast<unsigned long> (off));
+      return false;
+    }
+  *size = local_size;
+
+  *nested_off = 0;
+  if (hdr->ar_name[0] != '/')
+    {
+      const char *name_end = strchr (hdr->ar_name, '/');
+      if (name_end == NULL
+	  || name_end - hdr->ar_name >= static_cast<int> (sizeof hdr->ar_name))
+	{
+	  rust_error_at (this->location_,
+			 "%s: malformed archive header name at %lu",
+			 this->filename_.c_str (),
+			 static_cast<unsigned long> (off));
+	  return false;
+	}
+      pname->assign (hdr->ar_name, name_end - hdr->ar_name);
+    }
+  else if (hdr->ar_name[1] == ' ')
+    {
+      // This is the symbol table.
+      pname->clear ();
+    }
+  else if (hdr->ar_name[1] == 'S' && hdr->ar_name[2] == 'Y'
+	   && hdr->ar_name[3] == 'M' && hdr->ar_name[4] == '6'
+	   && hdr->ar_name[5] == '4' && hdr->ar_name[6] == '/'
+	   && hdr->ar_name[7] == ' ')
+    {
+      // 64-bit symbol table.
+      pname->clear ();
+    }
+  else if (hdr->ar_name[1] == '/')
+    {
+      // This is the extended name table.
+      pname->assign (1, '/');
+    }
+  else
+    {
+      char *end;
+      errno = 0;
+      long x = strtol (hdr->ar_name + 1, &end, 10);
+      long y = 0;
+      if (*end == ':')
+	y = strtol (end + 1, &end, 10);
+      if (*end != ' ' || x < 0 || (x == LONG_MAX && errno == ERANGE)
+	  || static_cast<size_t> (x) >= this->extended_names_.size ())
+	{
+	  rust_error_at (this->location_, "%s: bad extended name index at %lu",
+			 this->filename_.c_str (),
+			 static_cast<unsigned long> (off));
+	  return false;
+	}
+
+      const char *name = this->extended_names_.data () + x;
+      const char *name_end = strchr (name, '\n');
+      if (static_cast<size_t> (name_end - name) > this->extended_names_.size ()
+	  || name_end[-1] != '/')
+	{
+	  rust_error_at (this->location_,
+			 "%s: bad extended name entry at header %lu",
+			 this->filename_.c_str (),
+			 static_cast<unsigned long> (off));
+	  return false;
+	}
+      pname->assign (name, name_end - 1 - name);
+      *nested_off = y;
+    }
+
+  return true;
+}
+
+// Get the file and offset for an archive member.
+
+bool
+Archive_file::get_file_and_offset (off_t off, const std::string &hdrname,
+				   off_t nested_off, int *memfd, off_t *memoff,
+				   std::string *memname)
+{
+  if (this->is_big_archive_)
+    {
+      *memfd = this->fd_;
+      *memoff = (off + sizeof (Archive_big_header) + hdrname.length ()
+		 + sizeof (arfmag));
+      if ((*memoff & 1) != 0)
+	++*memoff;
+      *memname = this->filename_ + '(' + hdrname + ')';
+      return true;
+    }
+  else if (!this->is_thin_archive_)
+    {
+      *memfd = this->fd_;
+      *memoff = off + sizeof (Archive_header);
+      *memname = this->filename_ + '(' + hdrname + ')';
+      return true;
+    }
+
+  std::string filename = hdrname;
+  if (!IS_ABSOLUTE_PATH (filename.c_str ()))
+    {
+      const char *archive_path = this->filename_.c_str ();
+      const char *basename = lbasename (archive_path);
+      if (basename > archive_path)
+	filename.replace (0, 0,
+			  this->filename_.substr (0, basename - archive_path));
+    }
+
+  if (nested_off > 0)
+    {
+      // This is a member of a nested archive.
+      Archive_file *nfile;
+      Nested_archive_table::const_iterator p
+	= this->nested_archives_.find (filename);
+      if (p != this->nested_archives_.end ())
+	nfile = p->second;
+      else
+	{
+	  int nfd = open (filename.c_str (), O_RDONLY | O_BINARY);
+	  if (nfd < 0)
+	    {
+	      rust_error_at (this->location_,
+			     "%s: cannot open nested archive %s",
+			     this->filename_.c_str (), filename.c_str ());
+	      return false;
+	    }
+	  nfile = new Archive_file (filename, nfd, this->location_);
+	  if (!nfile->initialize ())
+	    {
+	      delete nfile;
+	      return false;
+	    }
+	  this->nested_archives_[filename] = nfile;
+	}
+
+      std::string nname;
+      off_t nsize;
+      off_t nnested_off;
+      if (!nfile->read_header (nested_off, &nname, &nsize, &nnested_off, NULL))
+	return false;
+      return nfile->get_file_and_offset (nested_off, nname, nnested_off, memfd,
+					 memoff, memname);
+    }
+
+  // An external member of a thin archive.
+  *memfd = open (filename.c_str (), O_RDONLY | O_BINARY);
+  if (*memfd < 0)
+    {
+      rust_error_at (this->location_, "%s: %m", filename.c_str ());
+      return false;
+    }
+  *memoff = 0;
+  *memname = filename;
+  return true;
+}
+
+// An archive member iterator.  This is more-or-less copied from gold.
+
+class Archive_iterator
+{
+public:
+  // The header of an archive member.  This is what this iterator
+  // points to.
+  struct Header
+  {
+    // The name of the member.
+    std::string name;
+    // The file offset of the member.
+    off_t off;
+    // The file offset of a nested archive member.
+    off_t nested_off;
+    // The size of the member.
+    off_t size;
+  };
+
+  Archive_iterator (Archive_file *afile, off_t off) : afile_ (afile), off_ (off)
+  {
+    this->read_next_header ();
+  }
+
+  const Header &operator* () const { return this->header_; }
+
+  const Header *operator-> () const { return &this->header_; }
+
+  Archive_iterator &operator++ ()
+  {
+    if (this->off_ == this->afile_->filesize ())
+      return *this;
+    this->off_ = this->next_off_;
+    this->read_next_header ();
+    return *this;
+  }
+
+  Archive_iterator operator++ (int)
+  {
+    Archive_iterator ret = *this;
+    ++*this;
+    return ret;
+  }
+
+  bool operator== (const Archive_iterator &p) const
+  {
+    return this->off_ == p->off;
+  }
+
+  bool operator!= (const Archive_iterator &p) const
+  {
+    return this->off_ != p->off;
+  }
+
+private:
+  void read_next_header ();
+
+  // The underlying archive file.
+  Archive_file *afile_;
+  // The current offset in the file.
+  off_t off_;
+  // The offset of the next member.
+  off_t next_off_;
+  // The current archive header.
+  Header header_;
+};
+
+// Read the next archive header.
+
+void
+Archive_iterator::read_next_header ()
+{
+  off_t filesize = this->afile_->filesize ();
+  while (true)
+    {
+      if (this->off_ == filesize)
+	{
+	  this->header_.off = filesize;
+	  return;
+	}
+
+      if (!this->afile_->read_header (this->off_, &this->header_.name,
+				      &this->header_.size,
+				      &this->header_.nested_off,
+				      &this->next_off_))
+	{
+	  this->header_.off = filesize;
+	  this->off_ = filesize;
+	  return;
+	}
+      this->header_.off = this->off_;
+
+      // Skip special members.
+      if (!this->header_.name.empty () && this->header_.name != "/")
+	return;
+
+      this->off_ = this->next_off_;
+    }
+}
+
+// Initial iterator.
+
+Archive_iterator
+archive_begin (Archive_file *afile)
+{
+  return Archive_iterator (afile, afile->first_member_offset ());
+}
+
+// Final iterator.
+
+Archive_iterator
+archive_end (Archive_file *afile)
+{
+  return Archive_iterator (afile, afile->filesize ());
+}
+
+// A type of Import_stream which concatenates other Import_streams
+// together.
+
+class Stream_concatenate : public Import::Stream
+{
+public:
+  Stream_concatenate () : inputs_ () {}
+
+  // Add a new stream.
+  void add (Import::Stream *is) { this->inputs_.push_back (is); }
+
+protected:
+  bool do_peek (size_t, const char **);
+
+  void do_advance (size_t);
+
+private:
+  std::list<Import::Stream *> inputs_;
+};
+
+// Peek ahead.
+
+bool
+Stream_concatenate::do_peek (size_t length, const char **bytes)
+{
+  while (true)
+    {
+      if (this->inputs_.empty ())
+	return false;
+      if (this->inputs_.front ()->peek (length, bytes))
+	return true;
+      delete this->inputs_.front ();
+      this->inputs_.pop_front ();
+    }
+}
+
+// Advance.
+
+void
+Stream_concatenate::do_advance (size_t skip)
+{
+  while (true)
+    {
+      if (this->inputs_.empty ())
+	return;
+      if (!this->inputs_.front ()->at_eof ())
+	{
+	  // We just assume that this will do the right thing.  It
+	  // should be OK since we should never want to skip past
+	  // multiple streams.
+	  this->inputs_.front ()->advance (skip);
+	  return;
+	}
+      delete this->inputs_.front ();
+      this->inputs_.pop_front ();
+    }
+}
+
+// Import data from an archive.  We walk through the archive and
+// import data from each member.
+
+Import::Stream *
+Import::find_archive_export_data (const std::string &filename, int fd,
+				  Location location)
+{
+  Archive_file afile (filename, fd, location);
+  if (!afile.initialize ())
+    return NULL;
+
+  Stream_concatenate *ret = new Stream_concatenate;
+
+  bool any_data = false;
+  bool any_members = false;
+  Archive_iterator pend = archive_end (&afile);
+  for (Archive_iterator p = archive_begin (&afile); p != pend; p++)
+    {
+      any_members = true;
+      int member_fd;
+      off_t member_off;
+      std::string member_name;
+      if (!afile.get_file_and_offset (p->off, p->name, p->nested_off,
+				      &member_fd, &member_off, &member_name))
+	return NULL;
+
+      Import::Stream *is
+	= Import::find_object_export_data (member_name, member_fd, member_off,
+					   location);
+      if (is != NULL)
+	{
+	  ret->add (is);
+	  any_data = true;
+	}
+    }
+
+  if (!any_members)
+    {
+      // It's normal to have an empty archive file when using gobuild.
+      return new Stream_from_string ("");
+    }
+
+  if (!any_data)
+    {
+      delete ret;
+      return NULL;
+    }
+
+  return ret;
+}
+
+} // namespace Rust
diff --git a/gcc/rust/metadata/rust-imports.cc b/gcc/rust/metadata/rust-imports.cc
new file mode 100644
index 00000000000..b44165b1fa2
--- /dev/null
+++ b/gcc/rust/metadata/rust-imports.cc
@@ -0,0 +1,441 @@
+// 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/>.
+
+#include "rust-system.h"
+#include "rust-diagnostics.h"
+#include "rust-imports.h"
+#include "rust-object-export.h"
+#include "rust-export-metadata.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+namespace Rust {
+
+// The list of paths we search for import files.
+static std::vector<std::string> search_path;
+
+// Add a directory to the search path.  This is called from the option
+// handling language hook.
+void
+add_search_path (const std::string &path)
+{
+  search_path.push_back (path);
+}
+
+// Find import data.  This searches the file system for FILENAME and
+// returns a pointer to a Stream object to read the data that it
+// exports.  If the file is not found, it returns NULL.
+
+// When FILENAME is not an absolute path and does not start with ./ or
+// ../, we use the search path provided by -I and -L options.
+
+// When FILENAME does start with ./ or ../, we use
+// RELATIVE_IMPORT_PATH as a prefix.
+
+// When FILENAME does not exist, we try modifying FILENAME to find the
+// file.  We use the first of these which exists:
+//   * We append ".gox".
+//   * We turn the base of FILENAME into libFILENAME.so.
+//   * We turn the base of FILENAME into libFILENAME.a.
+//   * We append ".o".
+
+// When using a search path, we apply each of these transformations at
+// each entry on the search path before moving on to the next entry.
+// If the file exists, but does not contain any Go export data, we
+// stop; we do not keep looking for another file with the same name
+// later in the search path.
+
+Import::Stream *
+Import::open_package (const std::string &filename, Location location,
+		      const std::string &relative_import_path)
+{
+  bool is_local;
+  if (IS_ABSOLUTE_PATH (filename))
+    is_local = true;
+  else if (filename[0] == '.'
+	   && (filename[1] == '\0' || IS_DIR_SEPARATOR (filename[1])))
+    is_local = true;
+  else if (filename[0] == '.' && filename[1] == '.'
+	   && (filename[2] == '\0' || IS_DIR_SEPARATOR (filename[2])))
+    is_local = true;
+  else
+    is_local = false;
+
+  std::string fn = filename;
+  if (is_local && !IS_ABSOLUTE_PATH (filename)
+      && !relative_import_path.empty ())
+    {
+      if (fn == ".")
+	{
+	  // A special case.
+	  fn = relative_import_path;
+	}
+      else if (fn[0] == '.' && fn[1] == '.'
+	       && (fn[2] == '\0' || IS_DIR_SEPARATOR (fn[2])))
+	{
+	  // We are going to join relative_import_path and fn, and it
+	  // will look like DIR/../PATH.  But DIR does not necessarily
+	  // exist in this case, and if it doesn't the use of .. will
+	  // fail although it shouldn't.  The gc compiler uses
+	  // path.Join here, which cleans up the .., so we need to do
+	  // the same.
+	  size_t index;
+	  for (index = relative_import_path.length () - 1;
+	       index > 0 && !IS_DIR_SEPARATOR (relative_import_path[index]);
+	       index--)
+	    ;
+	  if (index > 0)
+	    fn = relative_import_path.substr (0, index) + fn.substr (2);
+	  else
+	    fn = relative_import_path + '/' + fn;
+	}
+      else
+	fn = relative_import_path + '/' + fn;
+      is_local = false;
+    }
+
+  if (!is_local)
+    {
+      for (std::vector<std::string>::const_iterator p = search_path.begin ();
+	   p != search_path.end (); ++p)
+	{
+	  std::string indir = *p;
+	  if (!indir.empty () && indir[indir.size () - 1] != '/')
+	    indir += '/';
+	  indir += fn;
+	  Stream *s = Import::try_package_in_directory (indir, location);
+	  if (s != NULL)
+	    return s;
+	}
+    }
+
+  Stream *s = Import::try_package_in_directory (fn, location);
+  if (s != NULL)
+    return s;
+
+  return NULL;
+}
+
+// Try to find the export data for FILENAME.
+
+Import::Stream *
+Import::try_package_in_directory (const std::string &filename,
+				  Location location)
+{
+  std::string found_filename = filename;
+  int fd = open (found_filename.c_str (), O_RDONLY | O_BINARY);
+
+  if (fd >= 0)
+    {
+      struct stat s;
+      if (fstat (fd, &s) >= 0 && S_ISDIR (s.st_mode))
+	{
+	  close (fd);
+	  fd = -1;
+	  errno = EISDIR;
+	}
+    }
+
+  if (fd < 0)
+    {
+      if (errno != ENOENT && errno != EISDIR)
+	rust_warning_at (location, 0, "%s: %m", filename.c_str ());
+
+      fd = Import::try_suffixes (&found_filename);
+      if (fd < 0)
+	return NULL;
+    }
+
+  // The export data may not be in this file.
+  Stream *s = Import::find_export_data (found_filename, fd, location);
+  if (s != NULL)
+    return s;
+
+  close (fd);
+
+  rust_error_at (location, "%s exists but does not contain any Go export data",
+		 found_filename.c_str ());
+
+  return NULL;
+}
+
+// Given import "*PFILENAME", where *PFILENAME does not exist, try
+// various suffixes.  If we find one, set *PFILENAME to the one we
+// found.  Return the open file descriptor.
+
+int
+Import::try_suffixes (std::string *pfilename)
+{
+  std::string filename = *pfilename + ".rox";
+  int fd = open (filename.c_str (), O_RDONLY | O_BINARY);
+  if (fd >= 0)
+    {
+      *pfilename = filename;
+      return fd;
+    }
+
+  const char *basename = lbasename (pfilename->c_str ());
+  size_t basename_pos = basename - pfilename->c_str ();
+  filename = pfilename->substr (0, basename_pos) + "lib" + basename + ".so";
+  fd = open (filename.c_str (), O_RDONLY | O_BINARY);
+  if (fd >= 0)
+    {
+      *pfilename = filename;
+      return fd;
+    }
+
+  filename = pfilename->substr (0, basename_pos) + "lib" + basename + ".a";
+  fd = open (filename.c_str (), O_RDONLY | O_BINARY);
+  if (fd >= 0)
+    {
+      *pfilename = filename;
+      return fd;
+    }
+
+  filename = *pfilename + ".o";
+  fd = open (filename.c_str (), O_RDONLY | O_BINARY);
+  if (fd >= 0)
+    {
+      *pfilename = filename;
+      return fd;
+    }
+
+  return -1;
+}
+
+// Look for export data in the file descriptor FD.
+
+Import::Stream *
+Import::find_export_data (const std::string &filename, int fd,
+			  Location location)
+{
+  // See if we can read this as an object file.
+  Import::Stream *stream
+    = Import::find_object_export_data (filename, fd, 0, location);
+  if (stream != NULL)
+    return stream;
+
+  const int len = sizeof (Metadata::kMagicHeader);
+  if (lseek (fd, 0, SEEK_SET) < 0)
+    {
+      rust_error_at (location, "lseek %s failed: %m", filename.c_str ());
+      return NULL;
+    }
+
+  char buf[len];
+  ssize_t c = ::read (fd, buf, len);
+  if (c < len)
+    return NULL;
+
+  // Check for a file containing nothing but Go export data.
+  // if (memcmp (buf, Export::cur_magic, Export::magic_len) == 0
+  //     || memcmp (buf, Export::v1_magic, Export::magic_len) == 0
+  //     || memcmp (buf, Export::v2_magic, Export::magic_len) == 0)
+  //
+  // FIXME we need to work out a better header
+  //
+  if (memcmp (buf, Metadata::kMagicHeader, sizeof (Metadata::kMagicHeader))
+      == 0)
+    return new Stream_from_file (fd);
+
+  // See if we can read this as an archive.
+  if (Import::is_archive_magic (buf))
+    return Import::find_archive_export_data (filename, fd, location);
+
+  return NULL;
+}
+
+// Look for export data in an object file.
+
+Import::Stream *
+Import::find_object_export_data (const std::string &filename, int fd,
+				 off_t offset, Location location)
+{
+  char *buf;
+  size_t len;
+  int err;
+  const char *errmsg = rust_read_export_data (fd, offset, &buf, &len, &err);
+  if (errmsg != NULL)
+    {
+      if (err == 0)
+	rust_error_at (location, "%s: %s", filename.c_str (), errmsg);
+      else
+	rust_error_at (location, "%s: %s: %s", filename.c_str (), errmsg,
+		       xstrerror (err));
+      return NULL;
+    }
+
+  if (buf == NULL)
+    return NULL;
+
+  return new Stream_from_buffer (buf, len);
+}
+
+// Class Import.
+
+// Construct an Import object.  We make the builtin_types_ vector
+// large enough to hold all the builtin types.
+
+Import::Import (Stream *stream, Location location)
+  : stream_ (stream), location_ (location)
+{}
+
+// Import the data in the associated stream.
+
+// Read LENGTH bytes from the stream.
+
+void
+Import::read (size_t length, std::string *out)
+{
+  const char *data;
+  if (!this->stream_->peek (length, &data))
+    {
+      if (!this->stream_->saw_error ())
+	rust_error_at (this->location_, "import error at %d: expected %d bytes",
+		       this->stream_->pos (), static_cast<int> (length));
+      this->stream_->set_saw_error ();
+      *out = std::string ("");
+      return;
+    }
+  *out = std::string (data, length);
+  this->advance (length);
+}
+
+// Class Import::Stream.
+
+Import::Stream::Stream () : pos_ (0), saw_error_ (false) {}
+
+Import::Stream::~Stream () {}
+
+// Return the next character to come from the stream.
+
+int
+Import::Stream::peek_char ()
+{
+  const char *read;
+  if (!this->do_peek (1, &read))
+    return -1;
+  // Make sure we return an unsigned char, so that we don't get
+  // confused by \xff.
+  unsigned char ret = *read;
+  return ret;
+}
+
+// Return true if the next LENGTH characters from the stream match
+// BYTES
+
+bool
+Import::Stream::match_bytes (const char *bytes, size_t length)
+{
+  const char *read;
+  if (!this->do_peek (length, &read))
+    return false;
+  return memcmp (bytes, read, length) == 0;
+}
+
+// Require that the next LENGTH bytes from the stream match BYTES.
+
+void
+Import::Stream::require_bytes (Location location, const char *bytes,
+			       size_t length)
+{
+  const char *read;
+  if (!this->do_peek (length, &read) || memcmp (bytes, read, length) != 0)
+    {
+      if (!this->saw_error_)
+	rust_error_at (location, "import error at %d: expected %<%.*s%>",
+		       this->pos (), static_cast<int> (length), bytes);
+      this->saw_error_ = true;
+      return;
+    }
+  this->advance (length);
+}
+
+// Class Stream_from_file.
+
+Stream_from_file::Stream_from_file (int fd) : fd_ (fd), data_ ()
+{
+  if (lseek (fd, 0, SEEK_SET) != 0)
+    {
+      rust_fatal_error (Linemap::unknown_location (), "lseek failed: %m");
+      this->set_saw_error ();
+    }
+}
+
+Stream_from_file::~Stream_from_file () { close (this->fd_); }
+
+// Read next bytes.
+
+bool
+Stream_from_file::do_peek (size_t length, const char **bytes)
+{
+  if (this->data_.length () >= length)
+    {
+      *bytes = this->data_.data ();
+      return true;
+    }
+
+  this->data_.resize (length);
+  ssize_t got = ::read (this->fd_, &this->data_[0], length);
+
+  if (got < 0)
+    {
+      if (!this->saw_error ())
+	rust_fatal_error (Linemap::unknown_location (), "read failed: %m");
+      this->set_saw_error ();
+      return false;
+    }
+
+  if (lseek (this->fd_, -got, SEEK_CUR) < 0)
+    {
+      if (!this->saw_error ())
+	rust_fatal_error (Linemap::unknown_location (), "lseek failed: %m");
+      this->set_saw_error ();
+      return false;
+    }
+
+  if (static_cast<size_t> (got) < length)
+    return false;
+
+  *bytes = this->data_.data ();
+  return true;
+}
+
+// Advance.
+
+void
+Stream_from_file::do_advance (size_t skip)
+{
+  if (lseek (this->fd_, skip, SEEK_CUR) < 0)
+    {
+      if (!this->saw_error ())
+	rust_fatal_error (Linemap::unknown_location (), "lseek failed: %m");
+      this->set_saw_error ();
+    }
+  if (!this->data_.empty ())
+    {
+      if (this->data_.length () > skip)
+	this->data_.erase (0, skip);
+      else
+	this->data_.clear ();
+    }
+}
+
+} // namespace Rust
diff --git a/gcc/rust/metadata/rust-imports.h b/gcc/rust/metadata/rust-imports.h
new file mode 100644
index 00000000000..51cc4fc7613
--- /dev/null
+++ b/gcc/rust/metadata/rust-imports.h
@@ -0,0 +1,257 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#ifndef RUST_IMPORTS_H
+#define RUST_IMPORTS_H
+
+#include "rust-system.h"
+#include "rust-location.h"
+
+namespace Rust {
+
+extern void
+add_search_path (const std::string &path);
+
+class Import
+{
+public:
+  // The Stream class is an interface used to read the data.  The
+  // caller should instantiate a child of this class.
+  class Stream
+  {
+  public:
+    Stream ();
+    virtual ~Stream ();
+
+    // Set the position, for error messages.
+    void set_pos (int pos) { this->pos_ = pos; }
+
+    // Return whether we have seen an error.
+    bool saw_error () const { return this->saw_error_; }
+
+    // Record that we've seen an error.
+    void set_saw_error () { this->saw_error_ = true; }
+
+    // Return the next character (a value from 0 to 0xff) without
+    // advancing.  Returns -1 at end of stream.
+    int peek_char ();
+
+    // Look for LENGTH characters, setting *BYTES to point to them.
+    // Returns false if the bytes are not available.  Does not
+    // advance.
+    bool peek (size_t length, const char **bytes)
+    {
+      return this->do_peek (length, bytes);
+    }
+
+    // Return the next character (a value from 0 to 0xff) and advance
+    // the read position by 1.  Returns -1 at end of stream.
+    int get_char ()
+    {
+      int c = this->peek_char ();
+      this->advance (1);
+      return c;
+    }
+
+    // Return true if at the end of the stream.
+    bool at_eof () { return this->peek_char () == -1; }
+
+    // Return true if the next bytes match STR.
+    bool match_c_string (const char *str)
+    {
+      return this->match_bytes (str, strlen (str));
+    }
+
+    // Return true if the next LENGTH bytes match BYTES.
+    bool match_bytes (const char *bytes, size_t length);
+
+    // Give an error if the next bytes do not match STR.  Advance the
+    // read position by the length of STR.
+    void require_c_string (Location location, const char *str)
+    {
+      this->require_bytes (location, str, strlen (str));
+    }
+
+    // Given an error if the next LENGTH bytes do not match BYTES.
+    // Advance the read position by LENGTH.
+    void require_bytes (Location, const char *bytes, size_t length);
+
+    // Advance the read position by SKIP bytes.
+    void advance (size_t skip)
+    {
+      this->do_advance (skip);
+      this->pos_ += skip;
+    }
+
+    // Return the current read position.  This returns int because it
+    // is more convenient in error reporting.  FIXME.
+    int pos () { return static_cast<int> (this->pos_); }
+
+    // This function should set *BYTES to point to a buffer holding
+    // the LENGTH bytes at the current read position.  It should
+    // return false if the bytes are not available.  This should not
+    // change the current read position.
+    virtual bool do_peek (size_t length, const char **bytes) = 0;
+
+    // This function should advance the current read position LENGTH
+    // bytes.
+    virtual void do_advance (size_t skip) = 0;
+
+  private:
+    // The current read position.
+    size_t pos_;
+    // True if we've seen an error reading from this stream.
+    bool saw_error_;
+  };
+
+  // Find import data.  This searches the file system for FILENAME and
+  // returns a pointer to a Stream object to read the data that it
+  // exports.  LOCATION is the location of the import statement.
+  // RELATIVE_IMPORT_PATH is used as a prefix for a relative import.
+  static Stream *open_package (const std::string &filename, Location location,
+			       const std::string &relative_import_path);
+
+  // Constructor.
+  Import (Stream *, Location);
+
+  // The location of the import statement.
+  Location location () const { return this->location_; }
+
+  // Return the next character.
+  int peek_char () { return this->stream_->peek_char (); }
+
+  // Return the next character and advance.
+  int get_char () { return this->stream_->get_char (); }
+
+  // Read LENGTH characters into *OUT and advance past them.  On
+  // EOF reports an error and sets *OUT to an empty string.
+  void read (size_t length, std::string *out);
+
+  // Return true at the end of the stream.
+  bool at_eof () { return this->stream_->at_eof (); }
+
+  // Return whether the next bytes match STR.
+  bool match_c_string (const char *str)
+  {
+    return this->stream_->match_c_string (str);
+  }
+
+  // Require that the next bytes match STR.
+  void require_c_string (const char *str)
+  {
+    this->stream_->require_c_string (this->location_, str);
+  }
+
+  // Advance the stream SKIP bytes.
+  void advance (size_t skip) { this->stream_->advance (skip); }
+
+  // Stream position, for error reporting.
+  int pos () { return this->stream_->pos (); }
+
+  // Clear the stream when it is no longer accessible.
+  void clear_stream () { this->stream_ = NULL; }
+
+private:
+  static Stream *try_package_in_directory (const std::string &, Location);
+
+  static int try_suffixes (std::string *);
+
+  static Stream *find_export_data (const std::string &filename, int fd,
+				   Location);
+
+  static Stream *find_object_export_data (const std::string &filename, int fd,
+					  off_t offset, Location);
+
+  static bool is_archive_magic (const char *);
+
+  static Stream *find_archive_export_data (const std::string &filename, int fd,
+					   Location);
+
+  // The stream from which to read import data.
+  Stream *stream_;
+  // The location of the import statement we are processing.
+  Location location_;
+};
+
+// Read import data from a string.
+
+class Stream_from_string : public Import::Stream
+{
+public:
+  Stream_from_string (const std::string &str) : str_ (str), pos_ (0) {}
+
+  bool do_peek (size_t length, const char **bytes)
+  {
+    if (this->pos_ + length > this->str_.length ())
+      return false;
+    *bytes = this->str_.data () + this->pos_;
+    return true;
+  }
+
+  void do_advance (size_t len) { this->pos_ += len; }
+
+private:
+  // The string of data we are reading.
+  std::string str_;
+  // The current position within the string.
+  size_t pos_;
+};
+
+// Read import data from a buffer allocated using malloc.
+
+class Stream_from_buffer : public Import::Stream
+{
+public:
+  Stream_from_buffer (char *buf, size_t length)
+    : buf_ (buf), length_ (length), pos_ (0)
+  {}
+
+  ~Stream_from_buffer () { free (this->buf_); }
+
+  bool do_peek (size_t length, const char **bytes)
+  {
+    if (this->pos_ + length > this->length_)
+      return false;
+    *bytes = this->buf_ + this->pos_;
+    return true;
+  }
+
+  void do_advance (size_t len) { this->pos_ += len; }
+
+private:
+  // The data we are reading.
+  char *buf_;
+  // The length of the buffer.
+  size_t length_;
+  // The current position within the buffer.
+  size_t pos_;
+};
+
+// Read import data from an open file descriptor.
+
+class Stream_from_file : public Import::Stream
+{
+public:
+  Stream_from_file (int fd);
+
+  ~Stream_from_file ();
+
+  bool do_peek (size_t, const char **);
+
+  void do_advance (size_t);
+
+private:
+  // No copying.
+  Stream_from_file (const Stream_from_file &);
+  Stream_from_file &operator= (const Stream_from_file &);
+
+  // The file descriptor.
+  int fd_;
+  // Data read from the file.
+  std::string data_;
+};
+
+} // namespace Rust
+
+#endif // RUST_IMPORTS_H
diff --git a/gcc/rust/rust-object-export.cc b/gcc/rust/rust-object-export.cc
new file mode 100644
index 00000000000..f3007289ead
--- /dev/null
+++ b/gcc/rust/rust-object-export.cc
@@ -0,0 +1,177 @@
+/* rust-backend.c -- Rust frontend interface to gcc backend.
+   Copyright (C) 2010-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/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "target.h"
+#include "tree.h"
+#include "memmodel.h"
+#include "tm_p.h"
+#include "diagnostic.h"
+#include "simple-object.h"
+#include "stor-layout.h"
+#include "intl.h"
+#include "output.h" /* for assemble_string */
+#include "common/common-target.h"
+
+// satisfy intellisense
+#include "options.h"
+
+/* The segment name we pass to simple_object_start_read to find Rust
+   export data.  */
+
+#ifndef RUST_EXPORT_SEGMENT_NAME
+#define RUST_EXPORT_SEGMENT_NAME "__GNU_RUST"
+#endif
+
+/* The section name we use when reading and writing export data.  */
+
+#ifndef RUST_EXPORT_SECTION_NAME
+#define RUST_EXPORT_SECTION_NAME ".rust_export"
+#endif
+
+#ifndef TARGET_AIX
+#define TARGET_AIX 0
+#endif
+
+/* Return whether or not GCC has reported any errors.  */
+
+bool
+saw_errors (void)
+{
+  return errorcount != 0 || sorrycount != 0;
+}
+
+/* Return the alignment in bytes of a struct field of type T.  */
+
+unsigned int
+rust_field_alignment (tree t)
+{
+  unsigned int v;
+
+  v = TYPE_ALIGN (t);
+
+#ifdef BIGGEST_FIELD_ALIGNMENT
+  if (v > BIGGEST_FIELD_ALIGNMENT)
+    v = BIGGEST_FIELD_ALIGNMENT;
+#endif
+
+#ifdef ADJUST_FIELD_ALIGN
+  v = ADJUST_FIELD_ALIGN (NULL_TREE, t, v);
+#endif
+
+  return v / BITS_PER_UNIT;
+}
+
+/* This is called by the Rust frontend proper to add data to the
+   section containing Rust export data.  */
+
+void
+rust_write_export_data (const char *bytes, unsigned int size)
+{
+  static section *sec;
+
+  if (sec == NULL)
+    {
+      gcc_assert (targetm_common.have_named_sections);
+      sec = get_section (RUST_EXPORT_SECTION_NAME,
+			 TARGET_AIX ? SECTION_EXCLUDE : SECTION_DEBUG, NULL);
+    }
+
+  switch_to_section (sec);
+  assemble_string (bytes, size);
+}
+
+/* The rust_read_export_data function is called by the Rust frontend
+   proper to read Rust export data from an object file.  FD is a file
+   descriptor open for reading.  OFFSET is the offset within the file
+   where the object file starts; this will be 0 except when reading an
+   archive.  On success this returns NULL and sets *PBUF to a buffer
+   allocated using malloc, of size *PLEN, holding the export data.  If
+   the data is not found, this returns NULL and sets *PBUF to NULL and
+   *PLEN to 0.  If some error occurs, this returns an error message
+   and sets *PERR to an errno value or 0 if there is no relevant
+   errno.  */
+
+const char *
+rust_read_export_data (int fd, off_t offset, char **pbuf, size_t *plen,
+		       int *perr)
+{
+  simple_object_read *sobj;
+  const char *errmsg;
+  off_t sec_offset;
+  off_t sec_length;
+  int found;
+  char *buf;
+  ssize_t c;
+
+  *pbuf = NULL;
+  *plen = 0;
+
+  sobj = simple_object_start_read (fd, offset, RUST_EXPORT_SEGMENT_NAME,
+				   &errmsg, perr);
+  if (sobj == NULL)
+    {
+      /* If we get an error here, just pretend that we didn't find any
+	 export data.  This is the right thing to do if the error is
+	 that the file was not recognized as an object file.  This
+	 will ignore file I/O errors, but it's not too big a deal
+	 because we will wind up giving some other error later.  */
+      return NULL;
+    }
+
+  found = simple_object_find_section (sobj, RUST_EXPORT_SECTION_NAME,
+				      &sec_offset, &sec_length, &errmsg, perr);
+  simple_object_release_read (sobj);
+  if (!found)
+    return errmsg;
+
+  if (lseek (fd, offset + sec_offset, SEEK_SET) < 0)
+    {
+      *perr = errno;
+      return _ ("lseek failed while reading export data");
+    }
+
+  buf = XNEWVEC (char, sec_length);
+  if (buf == NULL)
+    {
+      *perr = errno;
+      return _ ("memory allocation failed while reading export data");
+    }
+
+  c = read (fd, buf, sec_length);
+  if (c < 0)
+    {
+      *perr = errno;
+      free (buf);
+      return _ ("read failed while reading export data");
+    }
+
+  if (c < sec_length)
+    {
+      free (buf);
+      return _ ("short read while reading export data");
+    }
+
+  *pbuf = buf;
+  *plen = sec_length;
+
+  return NULL;
+}
diff --git a/gcc/rust/rust-object-export.h b/gcc/rust/rust-object-export.h
new file mode 100644
index 00000000000..fcede54ae59
--- /dev/null
+++ b/gcc/rust/rust-object-export.h
@@ -0,0 +1,33 @@
+// 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_OBJECT_EXPORT_H
+#define RUST_OBJECT_EXPORT_H
+
+#include "rust-system.h"
+
+extern unsigned int
+rust_field_alignment (tree t);
+
+extern const char *
+rust_read_export_data (int fd, off_t offset, char **pbuf, size_t *plen,
+		       int *perr);
+extern void
+rust_write_export_data (const char *bytes, unsigned int size);
+
+#endif // RUST_OBJECT_EXPORT_H
-- 
2.25.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [PATCH Rust front-end v2 29/37] gccrs: HIR to GCC GENERIC lowering
  2022-08-24 11:59 Rust frontend patches v2 herron.philip
                   ` (27 preceding siblings ...)
  2022-08-24 11:59 ` [PATCH Rust front-end v2 28/37] gccrs: Add metadata ouptput pass herron.philip
@ 2022-08-24 11:59 ` herron.philip
  2022-08-24 11:59 ` [PATCH Rust front-end v2 30/37] gccrs: These are wrappers ported from reusing gccgo herron.philip
                   ` (8 subsequent siblings)
  37 siblings, 0 replies; 59+ messages in thread
From: herron.philip @ 2022-08-24 11:59 UTC (permalink / raw)
  To: gcc-patches; +Cc: gcc-rust, Philip Herron, David Faust

From: Philip Herron <philip.herron@embecosm.com>

This pass walks the HIR crate and turns them into GCC tree's we do not have
any Rust specific tree's. We are slowly removing the backend abstraction
which was ported over from gccgo in favour of using direct tree's.

Note we are porting over the constexpr evaluation from the C++ front-end
here.

Co-authored-by: David Faust <david.faust@oracle.com>
---
 gcc/rust/backend/rust-builtins.h              |  189 ++
 gcc/rust/backend/rust-compile-base.cc         |  730 +++++
 gcc/rust/backend/rust-compile-base.h          |  146 +
 gcc/rust/backend/rust-compile-block.cc        |  158 +
 gcc/rust/backend/rust-compile-block.h         |  211 ++
 gcc/rust/backend/rust-compile-context.cc      |  146 +
 gcc/rust/backend/rust-compile-context.h       |  343 ++
 gcc/rust/backend/rust-compile-expr.cc         | 2764 +++++++++++++++++
 gcc/rust/backend/rust-compile-expr.h          |  148 +
 gcc/rust/backend/rust-compile-extern.h        |  172 +
 gcc/rust/backend/rust-compile-fnparam.cc      |  121 +
 gcc/rust/backend/rust-compile-fnparam.h       |   70 +
 gcc/rust/backend/rust-compile-implitem.cc     |  101 +
 gcc/rust/backend/rust-compile-implitem.h      |   91 +
 gcc/rust/backend/rust-compile-intrinsic.cc    |  515 +++
 gcc/rust/backend/rust-compile-intrinsic.h     |   40 +
 gcc/rust/backend/rust-compile-item.cc         |  206 ++
 gcc/rust/backend/rust-compile-item.h          |   88 +
 gcc/rust/backend/rust-compile-pattern.cc      |  333 ++
 gcc/rust/backend/rust-compile-pattern.h       |   95 +
 gcc/rust/backend/rust-compile-resolve-path.cc |  301 ++
 gcc/rust/backend/rust-compile-resolve-path.h  |   73 +
 gcc/rust/backend/rust-compile-stmt.cc         |  115 +
 gcc/rust/backend/rust-compile-stmt.h          |   69 +
 .../backend/rust-compile-struct-field-expr.cc |   81 +
 .../backend/rust-compile-struct-field-expr.h  |   46 +
 gcc/rust/backend/rust-compile-type.cc         |  713 +++++
 gcc/rust/backend/rust-compile-type.h          |   79 +
 gcc/rust/backend/rust-compile-var-decl.h      |   95 +
 gcc/rust/backend/rust-compile.cc              |  414 +++
 gcc/rust/backend/rust-compile.h               |   47 +
 gcc/rust/backend/rust-constexpr.cc            |  441 +++
 gcc/rust/backend/rust-constexpr.h             |   31 +
 gcc/rust/backend/rust-mangle.cc               |  307 ++
 gcc/rust/backend/rust-mangle.h                |   52 +
 gcc/rust/backend/rust-tree.cc                 |  958 ++++++
 gcc/rust/backend/rust-tree.h                  |  508 +++
 gcc/rust/rust-backend.h                       |  506 +++
 gcc/rust/rust-gcc.cc                          | 2717 ++++++++++++++++
 39 files changed, 14220 insertions(+)
 create mode 100644 gcc/rust/backend/rust-builtins.h
 create mode 100644 gcc/rust/backend/rust-compile-base.cc
 create mode 100644 gcc/rust/backend/rust-compile-base.h
 create mode 100644 gcc/rust/backend/rust-compile-block.cc
 create mode 100644 gcc/rust/backend/rust-compile-block.h
 create mode 100644 gcc/rust/backend/rust-compile-context.cc
 create mode 100644 gcc/rust/backend/rust-compile-context.h
 create mode 100644 gcc/rust/backend/rust-compile-expr.cc
 create mode 100644 gcc/rust/backend/rust-compile-expr.h
 create mode 100644 gcc/rust/backend/rust-compile-extern.h
 create mode 100644 gcc/rust/backend/rust-compile-fnparam.cc
 create mode 100644 gcc/rust/backend/rust-compile-fnparam.h
 create mode 100644 gcc/rust/backend/rust-compile-implitem.cc
 create mode 100644 gcc/rust/backend/rust-compile-implitem.h
 create mode 100644 gcc/rust/backend/rust-compile-intrinsic.cc
 create mode 100644 gcc/rust/backend/rust-compile-intrinsic.h
 create mode 100644 gcc/rust/backend/rust-compile-item.cc
 create mode 100644 gcc/rust/backend/rust-compile-item.h
 create mode 100644 gcc/rust/backend/rust-compile-pattern.cc
 create mode 100644 gcc/rust/backend/rust-compile-pattern.h
 create mode 100644 gcc/rust/backend/rust-compile-resolve-path.cc
 create mode 100644 gcc/rust/backend/rust-compile-resolve-path.h
 create mode 100644 gcc/rust/backend/rust-compile-stmt.cc
 create mode 100644 gcc/rust/backend/rust-compile-stmt.h
 create mode 100644 gcc/rust/backend/rust-compile-struct-field-expr.cc
 create mode 100644 gcc/rust/backend/rust-compile-struct-field-expr.h
 create mode 100644 gcc/rust/backend/rust-compile-type.cc
 create mode 100644 gcc/rust/backend/rust-compile-type.h
 create mode 100644 gcc/rust/backend/rust-compile-var-decl.h
 create mode 100644 gcc/rust/backend/rust-compile.cc
 create mode 100644 gcc/rust/backend/rust-compile.h
 create mode 100644 gcc/rust/backend/rust-constexpr.cc
 create mode 100644 gcc/rust/backend/rust-constexpr.h
 create mode 100644 gcc/rust/backend/rust-mangle.cc
 create mode 100644 gcc/rust/backend/rust-mangle.h
 create mode 100644 gcc/rust/backend/rust-tree.cc
 create mode 100644 gcc/rust/backend/rust-tree.h
 create mode 100644 gcc/rust/rust-backend.h
 create mode 100644 gcc/rust/rust-gcc.cc

diff --git a/gcc/rust/backend/rust-builtins.h b/gcc/rust/backend/rust-builtins.h
new file mode 100644
index 00000000000..2bfa6c6cdf7
--- /dev/null
+++ b/gcc/rust/backend/rust-builtins.h
@@ -0,0 +1,189 @@
+// 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_BUILTINS_H
+#define RUST_BUILTINS_H
+
+#include "rust-system.h"
+#include "tree.h"
+#include "langhooks.h"
+
+namespace Rust {
+namespace Compile {
+
+// https://github.com/rust-lang/rust/blob/master/library/core/src/intrinsics.rs
+// https://github.com/rust-lang/rust/blob/master/compiler/rustc_codegen_llvm/src/intrinsic.rs
+// https://github.com/Rust-GCC/gccrs/issues/658
+//
+//   let llvm_name = match name {
+//     sym::sqrtf32 => "llvm.sqrt.f32",
+//     sym::sqrtf64 => "llvm.sqrt.f64",
+//     sym::powif32 => "llvm.powi.f32",
+//     sym::powif64 => "llvm.powi.f64",
+//     sym::sinf32 => "llvm.sin.f32",
+//     sym::sinf64 => "llvm.sin.f64",
+//     sym::cosf32 => "llvm.cos.f32",
+//     sym::cosf64 => "llvm.cos.f64",
+//     sym::powf32 => "llvm.pow.f32",
+//     sym::powf64 => "llvm.pow.f64",
+//     sym::expf32 => "llvm.exp.f32",
+//     sym::expf64 => "llvm.exp.f64",
+//     sym::exp2f32 => "llvm.exp2.f32",
+//     sym::exp2f64 => "llvm.exp2.f64",
+//     sym::logf32 => "llvm.log.f32",
+//     sym::logf64 => "llvm.log.f64",
+//     sym::log10f32 => "llvm.log10.f32",
+//     sym::log10f64 => "llvm.log10.f64",
+//     sym::log2f32 => "llvm.log2.f32",
+//     sym::log2f64 => "llvm.log2.f64",
+//     sym::fmaf32 => "llvm.fma.f32",
+//     sym::fmaf64 => "llvm.fma.f64",
+//     sym::fabsf32 => "llvm.fabs.f32",
+//     sym::fabsf64 => "llvm.fabs.f64",
+//     sym::minnumf32 => "llvm.minnum.f32",
+//     sym::minnumf64 => "llvm.minnum.f64",
+//     sym::maxnumf32 => "llvm.maxnum.f32",
+//     sym::maxnumf64 => "llvm.maxnum.f64",
+//     sym::copysignf32 => "llvm.copysign.f32",
+//     sym::copysignf64 => "llvm.copysign.f64",
+//     sym::floorf32 => "llvm.floor.f32",
+//     sym::floorf64 => "llvm.floor.f64",
+//     sym::ceilf32 => "llvm.ceil.f32",
+//     sym::ceilf64 => "llvm.ceil.f64",
+//     sym::truncf32 => "llvm.trunc.f32",
+//     sym::truncf64 => "llvm.trunc.f64",
+//     sym::rintf32 => "llvm.rint.f32",
+//     sym::rintf64 => "llvm.rint.f64",
+//     sym::nearbyintf32 => "llvm.nearbyint.f32",
+//     sym::nearbyintf64 => "llvm.nearbyint.f64",
+//     sym::roundf32 => "llvm.round.f32",
+//     sym::roundf64 => "llvm.round.f64",
+//     _ => return None,
+// };
+// Some(cx.get_intrinsic(&llvm_name))
+class BuiltinsContext
+{
+public:
+  static BuiltinsContext &get ()
+  {
+    static BuiltinsContext instance;
+    return instance;
+  }
+
+  bool lookup_simple_builtin (const std::string &name, tree *builtin)
+  {
+    auto it = rust_intrinsic_to_gcc_builtin.find (name);
+    if (it == rust_intrinsic_to_gcc_builtin.end ())
+      return false;
+
+    return lookup_gcc_builtin (it->second, builtin);
+  }
+
+private:
+  static const int builtin_const = 1 << 0;
+  static const int builtin_noreturn = 1 << 1;
+  static const int builtin_novops = 1 << 2;
+
+  BuiltinsContext () { setup (); }
+
+  void setup ()
+  {
+    tree math_function_type_f32
+      = build_function_type_list (float_type_node, float_type_node, NULL_TREE);
+
+    define_builtin ("sinf32", BUILT_IN_SINF, "__builtin_sinf", "sinf",
+		    math_function_type_f32, builtin_const);
+
+    define_builtin ("sqrtf32", BUILT_IN_SQRTF, "__builtin_sqrtf", "sqrtf",
+		    math_function_type_f32, builtin_const);
+
+    define_builtin ("unreachable", BUILT_IN_UNREACHABLE,
+		    "__builtin_unreachable", NULL,
+		    build_function_type (void_type_node, void_list_node),
+		    builtin_const | builtin_noreturn);
+
+    define_builtin ("abort", BUILT_IN_ABORT, "__builtin_abort", "abort",
+		    build_function_type (void_type_node, void_list_node),
+		    builtin_const | builtin_noreturn);
+
+    define_builtin ("breakpoint", BUILT_IN_TRAP, "__builtin_trap", "breakpoint",
+		    build_function_type (void_type_node, void_list_node),
+		    builtin_const | builtin_noreturn);
+
+    define_builtin (
+      "memcpy", BUILT_IN_MEMCPY, "__builtin_memcpy", "memcpy",
+      build_function_type_list (build_pointer_type (void_type_node),
+				build_pointer_type (void_type_node),
+				build_pointer_type (void_type_node),
+				size_type_node, NULL_TREE),
+      0);
+  }
+
+  // Define a builtin function.  BCODE is the builtin function code
+  // defined by builtins.def.  NAME is the name of the builtin function.
+  // LIBNAME is the name of the corresponding library function, and is
+  // NULL if there isn't one.  FNTYPE is the type of the function.
+  // CONST_P is true if the function has the const attribute.
+  // NORETURN_P is true if the function has the noreturn attribute.
+  void define_builtin (const std::string rust_name, built_in_function bcode,
+		       const char *name, const char *libname, tree fntype,
+		       int flags)
+  {
+    tree decl = add_builtin_function (name, fntype, bcode, BUILT_IN_NORMAL,
+				      libname, NULL_TREE);
+    if ((flags & builtin_const) != 0)
+      TREE_READONLY (decl) = 1;
+    if ((flags & builtin_noreturn) != 0)
+      TREE_THIS_VOLATILE (decl) = 1;
+    if ((flags & builtin_novops) != 0)
+      DECL_IS_NOVOPS (decl) = 1;
+    set_builtin_decl (bcode, decl, true);
+    this->builtin_functions_[name] = decl;
+    if (libname != NULL)
+      {
+	decl = add_builtin_function (libname, fntype, bcode, BUILT_IN_NORMAL,
+				     NULL, NULL_TREE);
+	if ((flags & builtin_const) != 0)
+	  TREE_READONLY (decl) = 1;
+	if ((flags & builtin_noreturn) != 0)
+	  TREE_THIS_VOLATILE (decl) = 1;
+	if ((flags & builtin_novops) != 0)
+	  DECL_IS_NOVOPS (decl) = 1;
+	this->builtin_functions_[libname] = decl;
+      }
+
+    rust_intrinsic_to_gcc_builtin[rust_name] = name;
+  }
+
+  bool lookup_gcc_builtin (const std::string &name, tree *builtin)
+  {
+    auto it = builtin_functions_.find (name);
+    if (it == builtin_functions_.end ())
+      return false;
+
+    *builtin = it->second;
+    return true;
+  }
+
+  // A mapping of the GCC built-ins exposed to GCC Rust.
+  std::map<std::string, tree> builtin_functions_;
+  std::map<std::string, std::string> rust_intrinsic_to_gcc_builtin;
+};
+
+} // namespace Compile
+} // namespace Rust
+
+#endif // RUST_BUILTINS_H
diff --git a/gcc/rust/backend/rust-compile-base.cc b/gcc/rust/backend/rust-compile-base.cc
new file mode 100644
index 00000000000..2b5c850872f
--- /dev/null
+++ b/gcc/rust/backend/rust-compile-base.cc
@@ -0,0 +1,730 @@
+// 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/>.
+
+#include "rust-compile-base.h"
+#include "rust-abi.h"
+#include "rust-compile-item.h"
+#include "rust-compile-stmt.h"
+#include "rust-compile-expr.h"
+#include "rust-compile-fnparam.h"
+#include "rust-compile-var-decl.h"
+#include "rust-constexpr.h"
+#include "rust-diagnostics.h"
+#include "rust-expr.h"	// for AST::AttrInputLiteral
+#include "rust-macro.h" // for AST::MetaNameValueStr
+
+#include "fold-const.h"
+#include "stringpool.h"
+#include "attribs.h"
+#include "tree.h"
+
+namespace Rust {
+namespace Compile {
+
+bool inline should_mangle_item (const tree fndecl)
+{
+  return lookup_attribute ("no_mangle", DECL_ATTRIBUTES (fndecl)) == NULL_TREE;
+}
+
+void
+HIRCompileBase::setup_fndecl (tree fndecl, bool is_main_entry_point,
+			      bool is_generic_fn, HIR::Visibility &visibility,
+			      const HIR::FunctionQualifiers &qualifiers,
+			      const AST::AttrVec &attrs)
+{
+  // if its the main fn or pub visibility mark its as DECL_PUBLIC
+  // please see https://github.com/Rust-GCC/gccrs/pull/137
+  bool is_pub = visibility.get_vis_type () == HIR::Visibility::VisType::PUBLIC;
+  if (is_main_entry_point || (is_pub && !is_generic_fn))
+    {
+      TREE_PUBLIC (fndecl) = 1;
+    }
+
+  // is it a const fn
+  if (qualifiers.is_const ())
+    {
+      TREE_READONLY (fndecl) = 1;
+    }
+
+  // is it inline?
+  for (const auto &attr : attrs)
+    {
+      bool is_inline = attr.get_path ().as_string ().compare ("inline") == 0;
+      bool is_must_use
+	= attr.get_path ().as_string ().compare ("must_use") == 0;
+      bool is_cold = attr.get_path ().as_string ().compare ("cold") == 0;
+      bool is_link_section
+	= attr.get_path ().as_string ().compare ("link_section") == 0;
+      bool no_mangle = attr.get_path ().as_string ().compare ("no_mangle") == 0;
+      bool is_deprecated
+	= attr.get_path ().as_string ().compare ("deprecated") == 0;
+
+      if (is_inline)
+	{
+	  handle_inline_attribute_on_fndecl (fndecl, attr);
+	}
+      else if (is_must_use)
+	{
+	  handle_must_use_attribute_on_fndecl (fndecl, attr);
+	}
+      else if (is_cold)
+	{
+	  handle_cold_attribute_on_fndecl (fndecl, attr);
+	}
+      else if (is_link_section)
+	{
+	  handle_link_section_attribute_on_fndecl (fndecl, attr);
+	}
+      else if (is_deprecated)
+	{
+	  handle_deprecated_attribute_on_fndecl (fndecl, attr);
+	}
+      else if (no_mangle)
+	{
+	  handle_no_mangle_attribute_on_fndecl (fndecl, attr);
+	}
+    }
+}
+
+void
+HIRCompileBase::handle_cold_attribute_on_fndecl (tree fndecl,
+						 const AST::Attribute &attr)
+{
+  // simple #[cold]
+  if (!attr.has_attr_input ())
+    {
+      tree cold = get_identifier ("cold");
+      // this will get handled by the GCC backend later
+      DECL_ATTRIBUTES (fndecl)
+	= tree_cons (cold, NULL_TREE, DECL_ATTRIBUTES (fndecl));
+      return;
+    }
+
+  rust_error_at (attr.get_locus (),
+		 "attribute %<cold%> does not accept any arguments");
+}
+
+void
+HIRCompileBase::handle_link_section_attribute_on_fndecl (
+  tree fndecl, const AST::Attribute &attr)
+{
+  if (!attr.has_attr_input ())
+    {
+      rust_error_at (attr.get_locus (),
+		     "%<link_section%> expects exactly one argment");
+      return;
+    }
+
+  rust_assert (attr.get_attr_input ().get_attr_input_type ()
+	       == AST::AttrInput::AttrInputType::LITERAL);
+
+  auto &literal = static_cast<AST::AttrInputLiteral &> (attr.get_attr_input ());
+  const auto &msg_str = literal.get_literal ().as_string ();
+
+  if (decl_section_name (fndecl))
+    {
+      rust_warning_at (attr.get_locus (), 0, "section name redefined");
+    }
+
+  set_decl_section_name (fndecl, msg_str.c_str ());
+}
+
+void
+HIRCompileBase::handle_no_mangle_attribute_on_fndecl (
+  tree fndecl, const AST::Attribute &attr)
+{
+  if (attr.has_attr_input ())
+    {
+      rust_error_at (attr.get_locus (),
+		     "attribute %<no_mangle%> does not accept any arguments");
+      return;
+    }
+
+  DECL_ATTRIBUTES (fndecl) = tree_cons (get_identifier ("no_mangle"), NULL_TREE,
+					DECL_ATTRIBUTES (fndecl));
+}
+
+void
+HIRCompileBase::handle_deprecated_attribute_on_fndecl (
+  tree fndecl, const AST::Attribute &attr)
+{
+  tree value = NULL_TREE;
+  TREE_DEPRECATED (fndecl) = 1;
+
+  // simple #[deprecated]
+  if (!attr.has_attr_input ())
+    return;
+
+  const AST::AttrInput &input = attr.get_attr_input ();
+  auto input_type = input.get_attr_input_type ();
+
+  if (input_type == AST::AttrInput::AttrInputType::LITERAL)
+    {
+      // handle #[deprecated = "message"]
+      auto &literal
+	= static_cast<AST::AttrInputLiteral &> (attr.get_attr_input ());
+      const auto &msg_str = literal.get_literal ().as_string ();
+      value = build_string (msg_str.size (), msg_str.c_str ());
+    }
+  else if (input_type == AST::AttrInput::AttrInputType::TOKEN_TREE)
+    {
+      // handle #[deprecated(since = "...", note = "...")]
+      const auto &option = static_cast<const AST::DelimTokenTree &> (input);
+      AST::AttrInputMetaItemContainer *meta_item = option.parse_to_meta_item ();
+      for (const auto &item : meta_item->get_items ())
+	{
+	  auto converted_item = item->to_meta_name_value_str ();
+	  if (!converted_item)
+	    continue;
+	  auto key_value = converted_item->get_name_value_pair ();
+	  if (key_value.first.compare ("since") == 0)
+	    {
+	      // valid, but this is handled by Cargo and some third-party audit
+	      // tools
+	      continue;
+	    }
+	  else if (key_value.first.compare ("note") == 0)
+	    {
+	      const auto &msg_str = key_value.second;
+	      if (value)
+		rust_error_at (attr.get_locus (), "multiple %<note%> items");
+	      value = build_string (msg_str.size (), msg_str.c_str ());
+	    }
+	  else
+	    {
+	      rust_error_at (attr.get_locus (), "unknown meta item %qs",
+			     key_value.first.c_str ());
+	    }
+	}
+    }
+
+  if (value)
+    {
+      tree attr_list = build_tree_list (NULL_TREE, value);
+      DECL_ATTRIBUTES (fndecl)
+	= tree_cons (get_identifier ("deprecated"), attr_list,
+		     DECL_ATTRIBUTES (fndecl));
+    }
+}
+
+void
+HIRCompileBase::handle_inline_attribute_on_fndecl (tree fndecl,
+						   const AST::Attribute &attr)
+{
+  // simple #[inline]
+  if (!attr.has_attr_input ())
+    {
+      DECL_DECLARED_INLINE_P (fndecl) = 1;
+      return;
+    }
+
+  const AST::AttrInput &input = attr.get_attr_input ();
+  bool is_token_tree
+    = input.get_attr_input_type () == AST::AttrInput::AttrInputType::TOKEN_TREE;
+  rust_assert (is_token_tree);
+  const auto &option = static_cast<const AST::DelimTokenTree &> (input);
+  AST::AttrInputMetaItemContainer *meta_item = option.parse_to_meta_item ();
+  if (meta_item->get_items ().size () != 1)
+    {
+      rust_error_at (attr.get_locus (), "invalid number of arguments");
+      return;
+    }
+
+  const std::string inline_option
+    = meta_item->get_items ().at (0)->as_string ();
+
+  // we only care about NEVER and ALWAYS else its an error
+  bool is_always = inline_option.compare ("always") == 0;
+  bool is_never = inline_option.compare ("never") == 0;
+
+  // #[inline(never)]
+  if (is_never)
+    {
+      DECL_UNINLINABLE (fndecl) = 1;
+    }
+  // #[inline(always)]
+  else if (is_always)
+    {
+      DECL_DECLARED_INLINE_P (fndecl) = 1;
+      DECL_ATTRIBUTES (fndecl) = tree_cons (get_identifier ("always_inline"),
+					    NULL, DECL_ATTRIBUTES (fndecl));
+    }
+  else
+    {
+      rust_error_at (attr.get_locus (), "unknown inline option");
+    }
+}
+
+void
+HIRCompileBase::handle_must_use_attribute_on_fndecl (tree fndecl,
+						     const AST::Attribute &attr)
+{
+  tree nodiscard = get_identifier ("nodiscard");
+  tree value = NULL_TREE;
+
+  if (attr.has_attr_input ())
+    {
+      rust_assert (attr.get_attr_input ().get_attr_input_type ()
+		   == AST::AttrInput::AttrInputType::LITERAL);
+
+      auto &literal
+	= static_cast<AST::AttrInputLiteral &> (attr.get_attr_input ());
+      const auto &msg_str = literal.get_literal ().as_string ();
+      tree message = build_string (msg_str.size (), msg_str.c_str ());
+
+      value = tree_cons (nodiscard, message, NULL_TREE);
+    }
+
+  DECL_ATTRIBUTES (fndecl)
+    = tree_cons (nodiscard, value, DECL_ATTRIBUTES (fndecl));
+}
+
+void
+HIRCompileBase::setup_abi_options (tree fndecl, ABI abi)
+{
+  tree abi_tree = NULL_TREE;
+
+  switch (abi)
+    {
+    case Rust::ABI::RUST:
+    case Rust::ABI::INTRINSIC:
+    case Rust::ABI::C:
+    case Rust::ABI::CDECL:
+      // `decl_attributes` function (not the macro) has the side-effect of
+      // actually switching the codegen backend to use the ABI we annotated.
+      // However, since `cdecl` is the default ABI GCC will be using, explicitly
+      // specifying that ABI will cause GCC to emit a warning saying the
+      // attribute is useless (which is confusing to the user as the attribute
+      // is added by us).
+      DECL_ATTRIBUTES (fndecl)
+	= tree_cons (get_identifier ("cdecl"), NULL, DECL_ATTRIBUTES (fndecl));
+
+      return;
+
+    case Rust::ABI::STDCALL:
+      abi_tree = get_identifier ("stdcall");
+
+      break;
+
+    case Rust::ABI::FASTCALL:
+      abi_tree = get_identifier ("fastcall");
+
+      break;
+
+    case Rust::ABI::SYSV64:
+      abi_tree = get_identifier ("sysv_abi");
+
+      break;
+
+    case Rust::ABI::WIN64:
+      abi_tree = get_identifier ("ms_abi");
+
+      break;
+
+    default:
+      break;
+    }
+
+  decl_attributes (&fndecl, build_tree_list (abi_tree, NULL_TREE), 0);
+}
+
+// ported from gcc/c/c-typecheck.c
+//
+// Mark EXP saying that we need to be able to take the
+// address of it; it should not be allocated in a register.
+// Returns true if successful.  ARRAY_REF_P is true if this
+// is for ARRAY_REF construction - in that case we don't want
+// to look through VIEW_CONVERT_EXPR from VECTOR_TYPE to ARRAY_TYPE,
+// it is fine to use ARRAY_REFs for vector subscripts on vector
+// register variables.
+bool
+HIRCompileBase::mark_addressable (tree exp, Location locus)
+{
+  tree x = exp;
+
+  while (1)
+    switch (TREE_CODE (x))
+      {
+      case VIEW_CONVERT_EXPR:
+	if (TREE_CODE (TREE_TYPE (x)) == ARRAY_TYPE
+	    && VECTOR_TYPE_P (TREE_TYPE (TREE_OPERAND (x, 0))))
+	  return true;
+	x = TREE_OPERAND (x, 0);
+	break;
+
+      case COMPONENT_REF:
+	// TODO
+	// if (DECL_C_BIT_FIELD (TREE_OPERAND (x, 1)))
+	//   {
+	//     error ("cannot take address of bit-field %qD", TREE_OPERAND (x,
+	//     1)); return false;
+	//   }
+
+	/* FALLTHRU */
+      case ADDR_EXPR:
+      case ARRAY_REF:
+      case REALPART_EXPR:
+      case IMAGPART_EXPR:
+	x = TREE_OPERAND (x, 0);
+	break;
+
+      case COMPOUND_LITERAL_EXPR:
+	TREE_ADDRESSABLE (x) = 1;
+	TREE_ADDRESSABLE (COMPOUND_LITERAL_EXPR_DECL (x)) = 1;
+	return true;
+
+      case CONSTRUCTOR:
+	TREE_ADDRESSABLE (x) = 1;
+	return true;
+
+      case VAR_DECL:
+      case CONST_DECL:
+      case PARM_DECL:
+      case RESULT_DECL:
+	// (we don't have a concept of a "register" declaration)
+	// fallthrough */
+
+	/* FALLTHRU */
+      case FUNCTION_DECL:
+	TREE_ADDRESSABLE (x) = 1;
+
+	/* FALLTHRU */
+      default:
+	return true;
+      }
+
+  return false;
+}
+
+tree
+HIRCompileBase::address_expression (tree expr, Location location)
+{
+  if (expr == error_mark_node)
+    return error_mark_node;
+
+  if (!mark_addressable (expr, location))
+    return error_mark_node;
+
+  return build_fold_addr_expr_loc (location.gcc_location (), expr);
+}
+
+tree
+HIRCompileBase::indirect_expression (tree expr, Location locus)
+{
+  if (expr == error_mark_node)
+    return error_mark_node;
+
+  return build_fold_indirect_ref_loc (locus.gcc_location (), expr);
+}
+
+std::vector<Bvariable *>
+HIRCompileBase::compile_locals_for_block (Context *ctx, Resolver::Rib &rib,
+					  tree fndecl)
+{
+  std::vector<Bvariable *> locals;
+  for (auto it : rib.get_declarations ())
+    {
+      NodeId node_id = it.first;
+      HirId ref = UNKNOWN_HIRID;
+      if (!ctx->get_mappings ()->lookup_node_to_hir (node_id, &ref))
+	continue;
+
+      // we only care about local patterns
+      HIR::Pattern *pattern = ctx->get_mappings ()->lookup_hir_pattern (ref);
+      if (pattern == nullptr)
+	continue;
+
+      // lookup the type
+      TyTy::BaseType *tyty = nullptr;
+      if (!ctx->get_tyctx ()->lookup_type (ref, &tyty))
+	continue;
+
+      // compile the local
+      tree type = TyTyResolveCompile::compile (ctx, tyty);
+      Bvariable *compiled
+	= CompileVarDecl::compile (fndecl, type, pattern, ctx);
+      locals.push_back (compiled);
+    }
+  return locals;
+}
+
+void
+HIRCompileBase::compile_function_body (Context *ctx, tree fndecl,
+				       HIR::BlockExpr &function_body,
+				       bool has_return_type)
+{
+  for (auto &s : function_body.get_statements ())
+    {
+      auto compiled_expr = CompileStmt::Compile (s.get (), ctx);
+      if (compiled_expr != nullptr)
+	{
+	  tree s = convert_to_void (compiled_expr, ICV_STATEMENT);
+	  ctx->add_statement (s);
+	}
+    }
+
+  if (function_body.has_expr ())
+    {
+      // the previous passes will ensure this is a valid return
+      // or a valid trailing expression
+      tree compiled_expr
+	= CompileExpr::Compile (function_body.expr.get (), ctx);
+
+      if (compiled_expr != nullptr)
+	{
+	  if (has_return_type)
+	    {
+	      std::vector<tree> retstmts;
+	      retstmts.push_back (compiled_expr);
+
+	      auto ret = ctx->get_backend ()->return_statement (
+		fndecl, retstmts,
+		function_body.get_final_expr ()->get_locus ());
+	      ctx->add_statement (ret);
+	    }
+	  else
+	    {
+	      // FIXME can this actually happen?
+	      ctx->add_statement (compiled_expr);
+	    }
+	}
+    }
+}
+
+tree
+HIRCompileBase::compile_function (
+  Context *ctx, const std::string &fn_name, HIR::SelfParam &self_param,
+  std::vector<HIR::FunctionParam> &function_params,
+  const HIR::FunctionQualifiers &qualifiers, HIR::Visibility &visibility,
+  AST::AttrVec &outer_attrs, Location locus, HIR::BlockExpr *function_body,
+  const Resolver::CanonicalPath *canonical_path, TyTy::FnType *fntype,
+  bool function_has_return)
+{
+  tree compiled_fn_type = TyTyResolveCompile::compile (ctx, fntype);
+  std::string ir_symbol_name
+    = canonical_path->get () + fntype->subst_as_string ();
+
+  // we don't mangle the main fn since we haven't implemented the main shim
+  bool is_main_fn = fn_name.compare ("main") == 0;
+  std::string asm_name = fn_name;
+
+  unsigned int flags = 0;
+  tree fndecl = ctx->get_backend ()->function (compiled_fn_type, ir_symbol_name,
+					       "" /* asm_name */, flags, locus);
+
+  setup_fndecl (fndecl, is_main_fn, fntype->has_subsititions_defined (),
+		visibility, qualifiers, outer_attrs);
+  setup_abi_options (fndecl, qualifiers.get_abi ());
+
+  // conditionally mangle the function name
+  bool should_mangle = should_mangle_item (fndecl);
+  if (!is_main_fn && should_mangle)
+    asm_name = ctx->mangle_item (fntype, *canonical_path);
+  SET_DECL_ASSEMBLER_NAME (fndecl,
+			   get_identifier_with_length (asm_name.data (),
+						       asm_name.length ()));
+
+  // insert into the context
+  ctx->insert_function_decl (fntype, fndecl);
+
+  // setup the params
+  TyTy::BaseType *tyret = fntype->get_return_type ();
+  std::vector<Bvariable *> param_vars;
+  if (!self_param.is_error ())
+    {
+      rust_assert (fntype->is_method ());
+      TyTy::BaseType *self_tyty_lookup = fntype->get_self_type ();
+
+      tree self_type = TyTyResolveCompile::compile (ctx, self_tyty_lookup);
+      Bvariable *compiled_self_param
+	= CompileSelfParam::compile (ctx, fndecl, self_param, self_type,
+				     self_param.get_locus ());
+
+      param_vars.push_back (compiled_self_param);
+      ctx->insert_var_decl (self_param.get_mappings ().get_hirid (),
+			    compiled_self_param);
+    }
+
+  // offset from + 1 for the TyTy::FnType being used when this is a method to
+  // skip over Self on the FnType
+  bool is_method = !self_param.is_error ();
+  size_t i = is_method ? 1 : 0;
+  for (auto &referenced_param : function_params)
+    {
+      auto tyty_param = fntype->param_at (i++);
+      auto param_tyty = tyty_param.second;
+      auto compiled_param_type = TyTyResolveCompile::compile (ctx, param_tyty);
+
+      Location param_locus = referenced_param.get_locus ();
+      Bvariable *compiled_param_var
+	= CompileFnParam::compile (ctx, fndecl, &referenced_param,
+				   compiled_param_type, param_locus);
+
+      param_vars.push_back (compiled_param_var);
+
+      const HIR::Pattern &param_pattern = *referenced_param.get_param_name ();
+      ctx->insert_var_decl (param_pattern.get_pattern_mappings ().get_hirid (),
+			    compiled_param_var);
+    }
+
+  if (!ctx->get_backend ()->function_set_parameters (fndecl, param_vars))
+    return error_mark_node;
+
+  // lookup locals
+  auto body_mappings = function_body->get_mappings ();
+  Resolver::Rib *rib = nullptr;
+  bool ok
+    = ctx->get_resolver ()->find_name_rib (body_mappings.get_nodeid (), &rib);
+  rust_assert (ok);
+
+  std::vector<Bvariable *> locals
+    = compile_locals_for_block (ctx, *rib, fndecl);
+
+  tree enclosing_scope = NULL_TREE;
+  Location start_location = function_body->get_locus ();
+  Location end_location = function_body->get_end_locus ();
+
+  tree code_block = ctx->get_backend ()->block (fndecl, enclosing_scope, locals,
+						start_location, end_location);
+  ctx->push_block (code_block);
+
+  Bvariable *return_address = nullptr;
+  if (function_has_return)
+    {
+      tree return_type = TyTyResolveCompile::compile (ctx, tyret);
+
+      bool address_is_taken = false;
+      tree ret_var_stmt = NULL_TREE;
+
+      return_address
+	= ctx->get_backend ()->temporary_variable (fndecl, code_block,
+						   return_type, NULL,
+						   address_is_taken, locus,
+						   &ret_var_stmt);
+
+      ctx->add_statement (ret_var_stmt);
+    }
+
+  ctx->push_fn (fndecl, return_address);
+  compile_function_body (ctx, fndecl, *function_body, function_has_return);
+  tree bind_tree = ctx->pop_block ();
+
+  gcc_assert (TREE_CODE (bind_tree) == BIND_EXPR);
+  DECL_SAVED_TREE (fndecl) = bind_tree;
+
+  ctx->pop_fn ();
+  ctx->push_function (fndecl);
+
+  return fndecl;
+}
+
+tree
+HIRCompileBase::compile_constant_item (
+  Context *ctx, TyTy::BaseType *resolved_type,
+  const Resolver::CanonicalPath *canonical_path, HIR::Expr *const_value_expr,
+  Location locus)
+{
+  const std::string &ident = canonical_path->get ();
+  tree type = TyTyResolveCompile::compile (ctx, resolved_type);
+  tree const_type = build_qualified_type (type, TYPE_QUAL_CONST);
+
+  bool is_block_expr
+    = const_value_expr->get_expression_type () == HIR::Expr::ExprType::Block;
+
+  // compile the expression
+  tree folded_expr = error_mark_node;
+  if (!is_block_expr)
+    {
+      tree value = CompileExpr::Compile (const_value_expr, ctx);
+      folded_expr = fold_expr (value);
+    }
+  else
+    {
+      // in order to compile a block expr we want to reuse as much existing
+      // machineary that we already have. This means the best approach is to
+      // make a _fake_ function with a block so it can hold onto temps then
+      // use our constexpr code to fold it completely or error_mark_node
+      Backend::typed_identifier receiver;
+      tree compiled_fn_type = ctx->get_backend ()->function_type (
+	receiver, {}, {Backend::typed_identifier ("_", const_type, locus)},
+	NULL, locus);
+
+      tree fndecl
+	= ctx->get_backend ()->function (compiled_fn_type, ident, "", 0, locus);
+      TREE_READONLY (fndecl) = 1;
+
+      tree enclosing_scope = NULL_TREE;
+      HIR::BlockExpr *function_body
+	= static_cast<HIR::BlockExpr *> (const_value_expr);
+      Location start_location = function_body->get_locus ();
+      Location end_location = function_body->get_end_locus ();
+
+      tree code_block
+	= ctx->get_backend ()->block (fndecl, enclosing_scope, {},
+				      start_location, end_location);
+      ctx->push_block (code_block);
+
+      bool address_is_taken = false;
+      tree ret_var_stmt = NULL_TREE;
+      Bvariable *return_address
+	= ctx->get_backend ()->temporary_variable (fndecl, code_block,
+						   const_type, NULL,
+						   address_is_taken, locus,
+						   &ret_var_stmt);
+
+      ctx->add_statement (ret_var_stmt);
+      ctx->push_fn (fndecl, return_address);
+
+      compile_function_body (ctx, fndecl, *function_body, true);
+      tree bind_tree = ctx->pop_block ();
+
+      gcc_assert (TREE_CODE (bind_tree) == BIND_EXPR);
+      DECL_SAVED_TREE (fndecl) = bind_tree;
+
+      ctx->pop_fn ();
+
+      // lets fold it into a call expr
+      tree call = build_call_array_loc (locus.gcc_location (), const_type,
+					fndecl, 0, NULL);
+      folded_expr = fold_expr (call);
+    }
+
+  return named_constant_expression (const_type, ident, folded_expr, locus);
+}
+
+tree
+HIRCompileBase::named_constant_expression (tree type_tree,
+					   const std::string &name,
+					   tree const_val, Location location)
+{
+  if (type_tree == error_mark_node || const_val == error_mark_node)
+    return error_mark_node;
+
+  tree name_tree = get_identifier_with_length (name.data (), name.length ());
+  tree decl
+    = build_decl (location.gcc_location (), CONST_DECL, name_tree, type_tree);
+  DECL_INITIAL (decl) = const_val;
+  TREE_CONSTANT (decl) = 1;
+  TREE_READONLY (decl) = 1;
+
+  rust_preserve_from_gc (decl);
+  return decl;
+}
+
+} // namespace Compile
+} // namespace Rust
diff --git a/gcc/rust/backend/rust-compile-base.h b/gcc/rust/backend/rust-compile-base.h
new file mode 100644
index 00000000000..4c20933cafc
--- /dev/null
+++ b/gcc/rust/backend/rust-compile-base.h
@@ -0,0 +1,146 @@
+// 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_COMPILE_BASE
+#define RUST_COMPILE_BASE
+
+#include "rust-compile-context.h"
+#include "rust-compile-type.h"
+#include "rust-hir-visitor.h"
+#include "rust-hir-full.h"
+
+namespace Rust {
+namespace Compile {
+
+class HIRCompileBase
+{
+public:
+  virtual ~HIRCompileBase () {}
+
+protected:
+  HIRCompileBase (Context *ctx) : ctx (ctx) {}
+
+  Context *ctx;
+
+protected:
+  Context *get_context () { return ctx; }
+
+  tree coercion_site (HirId id, tree rvalue, const TyTy::BaseType *actual,
+		      const TyTy::BaseType *expected, Location lvalue_locus,
+		      Location rvalue_locus);
+  tree coercion_site1 (tree rvalue, const TyTy::BaseType *actual,
+		       const TyTy::BaseType *expected, Location lvalue_locus,
+		       Location rvalue_locus);
+
+  tree coerce_to_dyn_object (tree compiled_ref, const TyTy::BaseType *actual,
+			     const TyTy::DynamicObjectType *ty, Location locus);
+
+  tree compute_address_for_trait_item (
+    const Resolver::TraitItemReference *ref,
+    const TyTy::TypeBoundPredicate *predicate,
+    std::vector<std::pair<Resolver::TraitReference *, HIR::ImplBlock *>>
+      &receiver_bounds,
+    const TyTy::BaseType *receiver, const TyTy::BaseType *root, Location locus);
+
+  bool verify_array_capacities (tree ltype, tree rtype, Location ltype_locus,
+				Location rtype_locus);
+
+  tree query_compile (HirId ref, TyTy::BaseType *lookup,
+		      const HIR::PathIdentSegment &final_segment,
+		      const Analysis::NodeMapping &mappings,
+		      Location expr_locus, bool is_qualified_path);
+
+  tree resolve_adjustements (std::vector<Resolver::Adjustment> &adjustments,
+			     tree expression, Location locus);
+
+  tree resolve_deref_adjustment (Resolver::Adjustment &adjustment,
+				 tree expression, Location locus);
+
+  tree resolve_indirection_adjustment (Resolver::Adjustment &adjustment,
+				       tree expression, Location locus);
+
+  tree resolve_unsized_adjustment (Resolver::Adjustment &adjustment,
+				   tree expression, Location locus);
+
+  tree resolve_unsized_slice_adjustment (Resolver::Adjustment &adjustment,
+					 tree expression, Location locus);
+
+  tree resolve_unsized_dyn_adjustment (Resolver::Adjustment &adjustment,
+				       tree expression, Location locus);
+
+  static void setup_fndecl (tree fndecl, bool is_main_entry_point,
+			    bool is_generic_fn, HIR::Visibility &visibility,
+			    const HIR::FunctionQualifiers &qualifiers,
+			    const AST::AttrVec &attrs);
+
+  static void handle_inline_attribute_on_fndecl (tree fndecl,
+						 const AST::Attribute &attr);
+
+  static void handle_cold_attribute_on_fndecl (tree fndecl,
+					       const AST::Attribute &attr);
+
+  static void handle_must_use_attribute_on_fndecl (tree fndecl,
+						   const AST::Attribute &attr);
+
+  static void
+  handle_link_section_attribute_on_fndecl (tree fndecl,
+					   const AST::Attribute &attr);
+  static void
+  handle_deprecated_attribute_on_fndecl (tree fndecl,
+					 const AST::Attribute &attr);
+
+  static void handle_no_mangle_attribute_on_fndecl (tree fndecl,
+						    const AST::Attribute &attr);
+
+  static void setup_abi_options (tree fndecl, ABI abi);
+
+  static tree address_expression (tree expr, Location locus);
+
+  static tree indirect_expression (tree expr, Location locus);
+
+  static bool mark_addressable (tree, Location);
+
+  static std::vector<Bvariable *>
+  compile_locals_for_block (Context *ctx, Resolver::Rib &rib, tree fndecl);
+
+  static void compile_function_body (Context *ctx, tree fndecl,
+				     HIR::BlockExpr &function_body,
+				     bool has_return_type);
+
+  static tree compile_function (
+    Context *ctx, const std::string &fn_name, HIR::SelfParam &self_param,
+    std::vector<HIR::FunctionParam> &function_params,
+    const HIR::FunctionQualifiers &qualifiers, HIR::Visibility &visibility,
+    AST::AttrVec &outer_attrs, Location locus, HIR::BlockExpr *function_body,
+    const Resolver::CanonicalPath *canonical_path, TyTy::FnType *fntype,
+    bool function_has_return);
+
+  static tree
+  compile_constant_item (Context *ctx, TyTy::BaseType *resolved_type,
+			 const Resolver::CanonicalPath *canonical_path,
+			 HIR::Expr *const_value_expr, Location locus);
+
+  static tree named_constant_expression (tree type_tree,
+					 const std::string &name,
+					 tree const_val, Location location);
+};
+
+} // namespace Compile
+} // namespace Rust
+
+#endif // RUST_COMPILE_BASE
diff --git a/gcc/rust/backend/rust-compile-block.cc b/gcc/rust/backend/rust-compile-block.cc
new file mode 100644
index 00000000000..99674e2d1e7
--- /dev/null
+++ b/gcc/rust/backend/rust-compile-block.cc
@@ -0,0 +1,158 @@
+// 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/>.
+
+#include "rust-compile-block.h"
+#include "rust-compile-stmt.h"
+#include "rust-compile-expr.h"
+
+namespace Rust {
+namespace Compile {
+
+CompileBlock::CompileBlock (Context *ctx, Bvariable *result)
+  : HIRCompileBase (ctx), translated (nullptr), result (result)
+{}
+
+tree
+CompileBlock::compile (HIR::BlockExpr *expr, Context *ctx, Bvariable *result)
+{
+  CompileBlock compiler (ctx, result);
+  compiler.visit (*expr);
+  return compiler.translated;
+}
+
+void
+CompileBlock::visit (HIR::BlockExpr &expr)
+{
+  fncontext fnctx = ctx->peek_fn ();
+  tree fndecl = fnctx.fndecl;
+  Location start_location = expr.get_locus ();
+  Location end_location = expr.get_end_locus ();
+  auto body_mappings = expr.get_mappings ();
+
+  Resolver::Rib *rib = nullptr;
+  if (!ctx->get_resolver ()->find_name_rib (body_mappings.get_nodeid (), &rib))
+    {
+      rust_fatal_error (expr.get_locus (), "failed to setup locals per block");
+      return;
+    }
+
+  std::vector<Bvariable *> locals
+    = compile_locals_for_block (ctx, *rib, fndecl);
+
+  tree enclosing_scope = ctx->peek_enclosing_scope ();
+  tree new_block = ctx->get_backend ()->block (fndecl, enclosing_scope, locals,
+					       start_location, end_location);
+  ctx->push_block (new_block);
+
+  for (auto &s : expr.get_statements ())
+    {
+      auto compiled_expr = CompileStmt::Compile (s.get (), ctx);
+      if (compiled_expr != nullptr)
+	{
+	  tree s = convert_to_void (compiled_expr, ICV_STATEMENT);
+	  ctx->add_statement (s);
+	}
+    }
+
+  if (expr.has_expr ())
+    {
+      // the previous passes will ensure this is a valid return or
+      // a valid trailing expression
+      tree compiled_expr = CompileExpr::Compile (expr.expr.get (), ctx);
+      if (compiled_expr != nullptr)
+	{
+	  if (result == nullptr)
+	    {
+	      ctx->add_statement (compiled_expr);
+	    }
+	  else
+	    {
+	      tree result_reference = ctx->get_backend ()->var_expression (
+		result, expr.get_final_expr ()->get_locus ());
+
+	      tree assignment
+		= ctx->get_backend ()->assignment_statement (result_reference,
+							     compiled_expr,
+							     expr.get_locus ());
+	      ctx->add_statement (assignment);
+	    }
+	}
+    }
+
+  ctx->pop_block ();
+  translated = new_block;
+}
+
+void
+CompileConditionalBlocks::visit (HIR::IfExpr &expr)
+{
+  fncontext fnctx = ctx->peek_fn ();
+  tree fndecl = fnctx.fndecl;
+  tree condition_expr = CompileExpr::Compile (expr.get_if_condition (), ctx);
+  tree then_block = CompileBlock::compile (expr.get_if_block (), ctx, result);
+
+  translated
+    = ctx->get_backend ()->if_statement (fndecl, condition_expr, then_block,
+					 NULL, expr.get_locus ());
+}
+
+void
+CompileConditionalBlocks::visit (HIR::IfExprConseqElse &expr)
+{
+  fncontext fnctx = ctx->peek_fn ();
+  tree fndecl = fnctx.fndecl;
+  tree condition_expr = CompileExpr::Compile (expr.get_if_condition (), ctx);
+  tree then_block = CompileBlock::compile (expr.get_if_block (), ctx, result);
+  tree else_block = CompileBlock::compile (expr.get_else_block (), ctx, result);
+
+  translated
+    = ctx->get_backend ()->if_statement (fndecl, condition_expr, then_block,
+					 else_block, expr.get_locus ());
+}
+
+void
+CompileConditionalBlocks::visit (HIR::IfExprConseqIf &expr)
+{
+  fncontext fnctx = ctx->peek_fn ();
+  tree fndecl = fnctx.fndecl;
+  tree condition_expr = CompileExpr::Compile (expr.get_if_condition (), ctx);
+  tree then_block = CompileBlock::compile (expr.get_if_block (), ctx, result);
+
+  // else block
+  std::vector<Bvariable *> locals;
+  Location start_location = expr.get_conseq_if_expr ()->get_locus ();
+  Location end_location = expr.get_conseq_if_expr ()->get_locus (); // FIXME
+  tree enclosing_scope = ctx->peek_enclosing_scope ();
+  tree else_block = ctx->get_backend ()->block (fndecl, enclosing_scope, locals,
+						start_location, end_location);
+  ctx->push_block (else_block);
+
+  tree else_stmt_decl
+    = CompileConditionalBlocks::compile (expr.get_conseq_if_expr (), ctx,
+					 result);
+  ctx->add_statement (else_stmt_decl);
+
+  ctx->pop_block ();
+
+  translated
+    = ctx->get_backend ()->if_statement (fndecl, condition_expr, then_block,
+					 else_block, expr.get_locus ());
+}
+
+} // namespace Compile
+} // namespace Rust
diff --git a/gcc/rust/backend/rust-compile-block.h b/gcc/rust/backend/rust-compile-block.h
new file mode 100644
index 00000000000..cdd17f19ca2
--- /dev/null
+++ b/gcc/rust/backend/rust-compile-block.h
@@ -0,0 +1,211 @@
+// 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_COMPILE_BLOCK
+#define RUST_COMPILE_BLOCK
+
+#include "rust-compile-base.h"
+
+namespace Rust {
+namespace Compile {
+
+class CompileBlock : private HIRCompileBase
+{
+public:
+  static tree compile (HIR::BlockExpr *expr, Context *ctx, Bvariable *result);
+
+protected:
+  void visit (HIR::BlockExpr &expr);
+
+private:
+  CompileBlock (Context *ctx, Bvariable *result);
+
+  tree translated;
+  Bvariable *result;
+};
+
+class CompileConditionalBlocks : public HIRCompileBase,
+				 public HIR::HIRExpressionVisitor
+{
+public:
+  static tree compile (HIR::IfExpr *expr, Context *ctx, Bvariable *result)
+  {
+    CompileConditionalBlocks resolver (ctx, result);
+    expr->accept_vis (resolver);
+    return resolver.translated;
+  }
+
+  void visit (HIR::IfExpr &expr) override;
+  void visit (HIR::IfExprConseqElse &expr) override;
+  void visit (HIR::IfExprConseqIf &expr) override;
+
+  // Empty visit for unused Expression HIR nodes.
+  void visit (HIR::PathInExpression &) override {}
+  void visit (HIR::QualifiedPathInExpression &) override {}
+  void visit (HIR::ClosureExprInner &) override {}
+  void visit (HIR::ClosureExprInnerTyped &) override {}
+  void visit (HIR::StructExprFieldIdentifier &) override {}
+  void visit (HIR::StructExprFieldIdentifierValue &) override {}
+  void visit (HIR::StructExprFieldIndexValue &) override {}
+  void visit (HIR::StructExprStruct &) override {}
+  void visit (HIR::StructExprStructFields &) override {}
+  void visit (HIR::LiteralExpr &) override {}
+  void visit (HIR::BorrowExpr &) override {}
+  void visit (HIR::DereferenceExpr &) override {}
+  void visit (HIR::ErrorPropagationExpr &) override {}
+  void visit (HIR::NegationExpr &) override {}
+  void visit (HIR::ArithmeticOrLogicalExpr &) override {}
+  void visit (HIR::ComparisonExpr &) override {}
+  void visit (HIR::LazyBooleanExpr &) override {}
+  void visit (HIR::TypeCastExpr &) override {}
+  void visit (HIR::AssignmentExpr &) override {}
+  void visit (HIR::CompoundAssignmentExpr &) override {}
+  void visit (HIR::GroupedExpr &) override {}
+  void visit (HIR::ArrayExpr &) override {}
+  void visit (HIR::ArrayIndexExpr &) override {}
+  void visit (HIR::TupleExpr &) override {}
+  void visit (HIR::TupleIndexExpr &) override {}
+  void visit (HIR::CallExpr &) override {}
+  void visit (HIR::MethodCallExpr &) override {}
+  void visit (HIR::FieldAccessExpr &) override {}
+  void visit (HIR::BlockExpr &) override {}
+  void visit (HIR::ContinueExpr &) override {}
+  void visit (HIR::BreakExpr &) override {}
+  void visit (HIR::RangeFromToExpr &) override {}
+  void visit (HIR::RangeFromExpr &) override {}
+  void visit (HIR::RangeToExpr &) override {}
+  void visit (HIR::RangeFullExpr &) override {}
+  void visit (HIR::RangeFromToInclExpr &) override {}
+  void visit (HIR::RangeToInclExpr &) override {}
+  void visit (HIR::ReturnExpr &) override {}
+  void visit (HIR::UnsafeBlockExpr &) override {}
+  void visit (HIR::LoopExpr &) override {}
+  void visit (HIR::WhileLoopExpr &) override {}
+  void visit (HIR::WhileLetLoopExpr &) override {}
+  void visit (HIR::ForLoopExpr &) override {}
+  void visit (HIR::IfExprConseqIfLet &) override {}
+  void visit (HIR::IfLetExpr &) override {}
+  void visit (HIR::IfLetExprConseqElse &) override {}
+  void visit (HIR::IfLetExprConseqIf &) override {}
+  void visit (HIR::IfLetExprConseqIfLet &) override {}
+  void visit (HIR::MatchExpr &) override {}
+  void visit (HIR::AwaitExpr &) override {}
+  void visit (HIR::AsyncBlockExpr &) override {}
+
+private:
+  CompileConditionalBlocks (Context *ctx, Bvariable *result)
+    : HIRCompileBase (ctx), translated (nullptr), result (result)
+  {}
+
+  tree translated;
+  Bvariable *result;
+};
+
+class CompileExprWithBlock : public HIRCompileBase,
+			     public HIR::HIRExpressionVisitor
+{
+public:
+  static tree compile (HIR::ExprWithBlock *expr, Context *ctx,
+		       Bvariable *result)
+  {
+    CompileExprWithBlock resolver (ctx, result);
+    expr->accept_vis (resolver);
+    return resolver.translated;
+  }
+
+  void visit (HIR::IfExpr &expr) override
+  {
+    translated = CompileConditionalBlocks::compile (&expr, ctx, result);
+  }
+
+  void visit (HIR::IfExprConseqElse &expr) override
+  {
+    translated = CompileConditionalBlocks::compile (&expr, ctx, result);
+  }
+
+  void visit (HIR::IfExprConseqIf &expr) override
+  {
+    translated = CompileConditionalBlocks::compile (&expr, ctx, result);
+  }
+
+  // Empty visit for unused Expression HIR nodes.
+  void visit (HIR::PathInExpression &) override {}
+  void visit (HIR::QualifiedPathInExpression &) override {}
+  void visit (HIR::ClosureExprInner &) override {}
+  void visit (HIR::ClosureExprInnerTyped &) override {}
+  void visit (HIR::StructExprFieldIdentifier &) override {}
+  void visit (HIR::StructExprFieldIdentifierValue &) override {}
+  void visit (HIR::StructExprFieldIndexValue &) override {}
+  void visit (HIR::StructExprStruct &) override {}
+  void visit (HIR::StructExprStructFields &) override {}
+  void visit (HIR::LiteralExpr &) override {}
+  void visit (HIR::BorrowExpr &) override {}
+  void visit (HIR::DereferenceExpr &) override {}
+  void visit (HIR::ErrorPropagationExpr &) override {}
+  void visit (HIR::NegationExpr &) override {}
+  void visit (HIR::ArithmeticOrLogicalExpr &) override {}
+  void visit (HIR::ComparisonExpr &) override {}
+  void visit (HIR::LazyBooleanExpr &) override {}
+  void visit (HIR::TypeCastExpr &) override {}
+  void visit (HIR::AssignmentExpr &) override {}
+  void visit (HIR::CompoundAssignmentExpr &) override {}
+  void visit (HIR::GroupedExpr &) override {}
+  void visit (HIR::ArrayExpr &) override {}
+  void visit (HIR::ArrayIndexExpr &) override {}
+  void visit (HIR::TupleExpr &) override {}
+  void visit (HIR::TupleIndexExpr &) override {}
+  void visit (HIR::CallExpr &) override {}
+  void visit (HIR::MethodCallExpr &) override {}
+  void visit (HIR::FieldAccessExpr &) override {}
+  void visit (HIR::BlockExpr &) override {}
+  void visit (HIR::ContinueExpr &) override {}
+  void visit (HIR::BreakExpr &) override {}
+  void visit (HIR::RangeFromToExpr &) override {}
+  void visit (HIR::RangeFromExpr &) override {}
+  void visit (HIR::RangeToExpr &) override {}
+  void visit (HIR::RangeFullExpr &) override {}
+  void visit (HIR::RangeFromToInclExpr &) override {}
+  void visit (HIR::RangeToInclExpr &) override {}
+  void visit (HIR::ReturnExpr &) override {}
+  void visit (HIR::UnsafeBlockExpr &) override {}
+  void visit (HIR::LoopExpr &) override {}
+  void visit (HIR::WhileLoopExpr &) override {}
+  void visit (HIR::WhileLetLoopExpr &) override {}
+  void visit (HIR::ForLoopExpr &) override {}
+  void visit (HIR::IfExprConseqIfLet &) override {}
+  void visit (HIR::IfLetExpr &) override {}
+  void visit (HIR::IfLetExprConseqElse &) override {}
+  void visit (HIR::IfLetExprConseqIf &) override {}
+  void visit (HIR::IfLetExprConseqIfLet &) override {}
+  void visit (HIR::MatchExpr &) override {}
+  void visit (HIR::AwaitExpr &) override {}
+  void visit (HIR::AsyncBlockExpr &) override {}
+
+private:
+  CompileExprWithBlock (Context *ctx, Bvariable *result)
+    : HIRCompileBase (ctx), translated (nullptr), result (result)
+  {}
+
+  tree translated;
+  Bvariable *result;
+};
+
+} // namespace Compile
+} // namespace Rust
+
+#endif // RUST_COMPILE_BLOCK
diff --git a/gcc/rust/backend/rust-compile-context.cc b/gcc/rust/backend/rust-compile-context.cc
new file mode 100644
index 00000000000..cb2addf6c21
--- /dev/null
+++ b/gcc/rust/backend/rust-compile-context.cc
@@ -0,0 +1,146 @@
+// 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/>.
+
+#include "rust-compile-context.h"
+#include "rust-compile-type.h"
+
+namespace Rust {
+namespace Compile {
+
+Context::Context (::Backend *backend)
+  : backend (backend), resolver (Resolver::Resolver::get ()),
+    tyctx (Resolver::TypeCheckContext::get ()),
+    mappings (Analysis::Mappings::get ()), mangler (Mangler ())
+{
+  setup_builtins ();
+}
+
+void
+Context::setup_builtins ()
+{
+  auto builtins = resolver->get_builtin_types ();
+  for (auto it = builtins.begin (); it != builtins.end (); it++)
+    {
+      HirId ref;
+      bool ok = tyctx->lookup_type_by_node_id ((*it)->get_node_id (), &ref);
+      rust_assert (ok);
+
+      TyTy::BaseType *lookup;
+      ok = tyctx->lookup_type (ref, &lookup);
+      rust_assert (ok);
+
+      TyTyResolveCompile::compile (this, lookup);
+    }
+}
+
+hashval_t
+Context::type_hasher (tree type)
+{
+  inchash::hash hstate;
+
+  hstate.add_int (TREE_CODE (type));
+
+  if (TYPE_NAME (type))
+    {
+      hashval_t record_name_hash
+	= IDENTIFIER_HASH_VALUE (DECL_NAME (TYPE_NAME (type)));
+      hstate.add_object (record_name_hash);
+    }
+
+  for (tree t = TYPE_ATTRIBUTES (type); t; t = TREE_CHAIN (t))
+    /* Just the identifier is adequate to distinguish.  */
+    hstate.add_object (IDENTIFIER_HASH_VALUE (TREE_PURPOSE (t)));
+
+  switch (TREE_CODE (type))
+    {
+    case METHOD_TYPE:
+      hstate.add_object (TYPE_HASH (TYPE_METHOD_BASETYPE (type)));
+      /* FALLTHROUGH. */
+    case FUNCTION_TYPE:
+      for (tree t = TYPE_ARG_TYPES (type); t; t = TREE_CHAIN (t))
+	if (TREE_VALUE (t) != error_mark_node)
+	  hstate.add_object (TYPE_HASH (TREE_VALUE (t)));
+      break;
+
+    case OFFSET_TYPE:
+      hstate.add_object (TYPE_HASH (TYPE_OFFSET_BASETYPE (type)));
+      break;
+
+      case ARRAY_TYPE: {
+	if (TYPE_DOMAIN (type))
+	  hstate.add_object (TYPE_HASH (TYPE_DOMAIN (type)));
+	if (!AGGREGATE_TYPE_P (TREE_TYPE (type)))
+	  {
+	    unsigned typeless = TYPE_TYPELESS_STORAGE (type);
+	    hstate.add_object (typeless);
+	  }
+      }
+      break;
+
+      case INTEGER_TYPE: {
+	tree t = TYPE_MAX_VALUE (type);
+	if (!t)
+	  t = TYPE_MIN_VALUE (type);
+	for (int i = 0; i < TREE_INT_CST_NUNITS (t); i++)
+	  hstate.add_object (TREE_INT_CST_ELT (t, i));
+	break;
+      }
+
+    case REAL_TYPE:
+      case FIXED_POINT_TYPE: {
+	unsigned prec = TYPE_PRECISION (type);
+	hstate.add_object (prec);
+	break;
+      }
+
+    case VECTOR_TYPE:
+      hstate.add_poly_int (TYPE_VECTOR_SUBPARTS (type));
+      break;
+
+    case RECORD_TYPE:
+    case UNION_TYPE:
+      case QUAL_UNION_TYPE: {
+	for (tree t = TYPE_FIELDS (type); t; t = TREE_CHAIN (t))
+	  {
+	    hashval_t name_hash = IDENTIFIER_HASH_VALUE (DECL_NAME (t));
+	    hashval_t type_hash = type_hasher (TREE_TYPE (t));
+	    hstate.add_object (name_hash);
+	    hstate.add_object (type_hash);
+	  }
+      }
+      break;
+
+    case BOOLEAN_TYPE:
+      break;
+
+    case REFERENCE_TYPE:
+      case POINTER_TYPE: {
+	hashval_t type_hash = type_hasher (TREE_TYPE (type));
+	hstate.add_object (type_hash);
+      }
+      break;
+
+    default:
+      break;
+    }
+
+  return hstate.end ();
+}
+
+} // namespace Compile
+} // namespace Rust
diff --git a/gcc/rust/backend/rust-compile-context.h b/gcc/rust/backend/rust-compile-context.h
new file mode 100644
index 00000000000..096b65f8b39
--- /dev/null
+++ b/gcc/rust/backend/rust-compile-context.h
@@ -0,0 +1,343 @@
+// 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_COMPILE_CONTEXT
+#define RUST_COMPILE_CONTEXT
+
+#include "rust-system.h"
+#include "rust-hir-map.h"
+#include "rust-name-resolver.h"
+#include "rust-hir-type-check.h"
+#include "rust-backend.h"
+#include "rust-hir-full.h"
+#include "rust-mangle.h"
+#include "rust-tree.h"
+
+namespace Rust {
+namespace Compile {
+
+struct fncontext
+{
+  tree fndecl;
+  ::Bvariable *ret_addr;
+};
+
+class Context
+{
+public:
+  Context (::Backend *backend);
+
+  void setup_builtins ();
+
+  bool lookup_compiled_types (tree t, tree *type)
+  {
+    hashval_t h = type_hasher (t);
+    auto it = compiled_type_map.find (h);
+    if (it == compiled_type_map.end ())
+      return false;
+
+    *type = it->second;
+    return true;
+  }
+
+  tree insert_compiled_type (tree type)
+  {
+    hashval_t h = type_hasher (type);
+    auto it = compiled_type_map.find (h);
+    if (it != compiled_type_map.end ())
+      return it->second;
+
+    compiled_type_map.insert ({h, type});
+    push_type (type);
+    return type;
+  }
+
+  tree insert_main_variant (tree type)
+  {
+    hashval_t h = type_hasher (type);
+    auto it = main_variants.find (h);
+    if (it != main_variants.end ())
+      return it->second;
+
+    main_variants.insert ({h, type});
+    return type;
+  }
+
+  ::Backend *get_backend () { return backend; }
+  Resolver::Resolver *get_resolver () { return resolver; }
+  Resolver::TypeCheckContext *get_tyctx () { return tyctx; }
+  Analysis::Mappings *get_mappings () { return mappings; }
+
+  void push_block (tree scope)
+  {
+    scope_stack.push_back (scope);
+    statements.push_back ({});
+  }
+
+  tree pop_block ()
+  {
+    auto block = scope_stack.back ();
+    scope_stack.pop_back ();
+
+    auto stmts = statements.back ();
+    statements.pop_back ();
+
+    backend->block_add_statements (block, stmts);
+
+    return block;
+  }
+
+  tree peek_enclosing_scope ()
+  {
+    if (scope_stack.size () == 0)
+      return nullptr;
+
+    return scope_stack.back ();
+  }
+
+  void add_statement_to_enclosing_scope (tree stmt)
+  {
+    statements.at (statements.size () - 2).push_back (stmt);
+  }
+
+  void add_statement (tree stmt) { statements.back ().push_back (stmt); }
+
+  void insert_var_decl (HirId id, ::Bvariable *decl)
+  {
+    compiled_var_decls[id] = decl;
+  }
+
+  bool lookup_var_decl (HirId id, ::Bvariable **decl)
+  {
+    auto it = compiled_var_decls.find (id);
+    if (it == compiled_var_decls.end ())
+      return false;
+
+    *decl = it->second;
+    return true;
+  }
+
+  void insert_function_decl (const TyTy::FnType *ref, tree fn)
+  {
+    auto id = ref->get_ty_ref ();
+    auto dId = ref->get_id ();
+
+    rust_assert (compiled_fn_map.find (id) == compiled_fn_map.end ());
+    compiled_fn_map[id] = fn;
+
+    auto it = mono_fns.find (dId);
+    if (it == mono_fns.end ())
+      mono_fns[dId] = {};
+
+    mono_fns[dId].push_back ({ref, fn});
+  }
+
+  bool lookup_function_decl (HirId id, tree *fn, DefId dId = UNKNOWN_DEFID,
+			     const TyTy::BaseType *ref = nullptr)
+  {
+    // for for any monomorphized fns
+    if (ref != nullptr)
+      {
+	rust_assert (dId != UNKNOWN_DEFID);
+
+	auto it = mono_fns.find (dId);
+	if (it == mono_fns.end ())
+	  return false;
+
+	for (auto &e : mono_fns[dId])
+	  {
+	    const TyTy::BaseType *r = e.first;
+	    tree f = e.second;
+	    if (ref->is_equal (*r))
+	      {
+		*fn = f;
+		return true;
+	      }
+	  }
+	return false;
+      }
+
+    auto it = compiled_fn_map.find (id);
+    if (it == compiled_fn_map.end ())
+      return false;
+
+    *fn = it->second;
+    return true;
+  }
+
+  void insert_const_decl (HirId id, tree expr) { compiled_consts[id] = expr; }
+
+  bool lookup_const_decl (HirId id, tree *expr)
+  {
+    auto it = compiled_consts.find (id);
+    if (it == compiled_consts.end ())
+      return false;
+
+    *expr = it->second;
+    return true;
+  }
+
+  void insert_label_decl (HirId id, tree label) { compiled_labels[id] = label; }
+
+  bool lookup_label_decl (HirId id, tree *label)
+  {
+    auto it = compiled_labels.find (id);
+    if (it == compiled_labels.end ())
+      return false;
+
+    *label = it->second;
+    return true;
+  }
+
+  void insert_pattern_binding (HirId id, tree binding)
+  {
+    implicit_pattern_bindings[id] = binding;
+  }
+
+  bool lookup_pattern_binding (HirId id, tree *binding)
+  {
+    auto it = implicit_pattern_bindings.find (id);
+    if (it == implicit_pattern_bindings.end ())
+      return false;
+
+    *binding = it->second;
+    return true;
+  }
+
+  void push_fn (tree fn, ::Bvariable *ret_addr)
+  {
+    fn_stack.push_back (fncontext{fn, ret_addr});
+  }
+  void pop_fn () { fn_stack.pop_back (); }
+
+  bool in_fn () { return fn_stack.size () != 0; }
+
+  // Note: it is undefined behavior to call peek_fn () if fn_stack is empty.
+  fncontext peek_fn ()
+  {
+    rust_assert (!fn_stack.empty ());
+    return fn_stack.back ();
+  }
+
+  void push_type (tree t) { type_decls.push_back (t); }
+  void push_var (::Bvariable *v) { var_decls.push_back (v); }
+  void push_const (tree c) { const_decls.push_back (c); }
+  void push_function (tree f) { func_decls.push_back (f); }
+
+  void write_to_backend ()
+  {
+    backend->write_global_definitions (type_decls, const_decls, func_decls,
+				       var_decls);
+  }
+
+  bool function_completed (tree fn)
+  {
+    for (auto it = func_decls.begin (); it != func_decls.end (); it++)
+      {
+	tree i = (*it);
+	if (i == fn)
+	  {
+	    return true;
+	  }
+      }
+    return false;
+  }
+
+  void push_loop_context (Bvariable *var) { loop_value_stack.push_back (var); }
+
+  Bvariable *peek_loop_context () { return loop_value_stack.back (); }
+
+  Bvariable *pop_loop_context ()
+  {
+    auto back = loop_value_stack.back ();
+    loop_value_stack.pop_back ();
+    return back;
+  }
+
+  void push_loop_begin_label (tree label)
+  {
+    loop_begin_labels.push_back (label);
+  }
+
+  tree peek_loop_begin_label () { return loop_begin_labels.back (); }
+
+  tree pop_loop_begin_label ()
+  {
+    tree pop = loop_begin_labels.back ();
+    loop_begin_labels.pop_back ();
+    return pop;
+  }
+
+  void push_const_context (void) { const_context++; }
+  void pop_const_context (void)
+  {
+    if (const_context > 0)
+      const_context--;
+  }
+  bool const_context_p (void) { return (const_context > 0); }
+
+  std::string mangle_item (const TyTy::BaseType *ty,
+			   const Resolver::CanonicalPath &path) const
+  {
+    return mangler.mangle_item (ty, path);
+  }
+
+  std::vector<tree> &get_type_decls () { return type_decls; }
+  std::vector<::Bvariable *> &get_var_decls () { return var_decls; }
+  std::vector<tree> &get_const_decls () { return const_decls; }
+  std::vector<tree> &get_func_decls () { return func_decls; }
+
+  static hashval_t type_hasher (tree type);
+
+private:
+  ::Backend *backend;
+  Resolver::Resolver *resolver;
+  Resolver::TypeCheckContext *tyctx;
+  Analysis::Mappings *mappings;
+  Mangler mangler;
+
+  // state
+  std::vector<fncontext> fn_stack;
+  std::map<HirId, ::Bvariable *> compiled_var_decls;
+  std::map<hashval_t, tree> compiled_type_map;
+  std::map<HirId, tree> compiled_fn_map;
+  std::map<HirId, tree> compiled_consts;
+  std::map<HirId, tree> compiled_labels;
+  std::vector<::std::vector<tree>> statements;
+  std::vector<tree> scope_stack;
+  std::vector<::Bvariable *> loop_value_stack;
+  std::vector<tree> loop_begin_labels;
+  std::map<DefId, std::vector<std::pair<const TyTy::BaseType *, tree>>>
+    mono_fns;
+  std::map<HirId, tree> implicit_pattern_bindings;
+  std::map<hashval_t, tree> main_variants;
+
+  // To GCC middle-end
+  std::vector<tree> type_decls;
+  std::vector<::Bvariable *> var_decls;
+  std::vector<tree> const_decls;
+  std::vector<tree> func_decls;
+
+  // Nonzero iff we are currently compiling something inside a constant context.
+  unsigned int const_context = 0;
+};
+
+} // namespace Compile
+} // namespace Rust
+
+#endif // RUST_COMPILE_CONTEXT
diff --git a/gcc/rust/backend/rust-compile-expr.cc b/gcc/rust/backend/rust-compile-expr.cc
new file mode 100644
index 00000000000..865ad250f2c
--- /dev/null
+++ b/gcc/rust/backend/rust-compile-expr.cc
@@ -0,0 +1,2764 @@
+// 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/>.
+
+#include "rust-compile-expr.h"
+#include "rust-compile-struct-field-expr.h"
+#include "rust-hir-trait-resolve.h"
+#include "rust-hir-path-probe.h"
+#include "rust-hir-type-bounds.h"
+#include "rust-compile-pattern.h"
+#include "rust-compile-resolve-path.h"
+#include "rust-compile-block.h"
+#include "rust-compile-implitem.h"
+#include "rust-constexpr.h"
+
+#include "fold-const.h"
+#include "realmpfr.h"
+#include "convert.h"
+#include "print-tree.h"
+
+namespace Rust {
+namespace Compile {
+
+CompileExpr::CompileExpr (Context *ctx)
+  : HIRCompileBase (ctx), translated (error_mark_node)
+{}
+
+tree
+CompileExpr::Compile (HIR::Expr *expr, Context *ctx)
+{
+  CompileExpr compiler (ctx);
+  expr->accept_vis (compiler);
+  return compiler.translated;
+}
+
+void
+CompileExpr::visit (HIR::TupleIndexExpr &expr)
+{
+  HIR::Expr *tuple_expr = expr.get_tuple_expr ().get ();
+  TupleIndex index = expr.get_tuple_index ();
+
+  tree receiver_ref = CompileExpr::Compile (tuple_expr, ctx);
+
+  TyTy::BaseType *tuple_expr_ty = nullptr;
+  bool ok
+    = ctx->get_tyctx ()->lookup_type (tuple_expr->get_mappings ().get_hirid (),
+				      &tuple_expr_ty);
+  rust_assert (ok);
+
+  // do we need to add an indirect reference
+  if (tuple_expr_ty->get_kind () == TyTy::TypeKind::REF)
+    {
+      tree indirect = indirect_expression (receiver_ref, expr.get_locus ());
+      receiver_ref = indirect;
+    }
+
+  translated
+    = ctx->get_backend ()->struct_field_expression (receiver_ref, index,
+						    expr.get_locus ());
+}
+
+void
+CompileExpr::visit (HIR::TupleExpr &expr)
+{
+  if (expr.is_unit ())
+    {
+      translated = ctx->get_backend ()->unit_expression ();
+      return;
+    }
+
+  TyTy::BaseType *tyty = nullptr;
+  if (!ctx->get_tyctx ()->lookup_type (expr.get_mappings ().get_hirid (),
+				       &tyty))
+    {
+      rust_fatal_error (expr.get_locus (),
+			"did not resolve type for this TupleExpr");
+      return;
+    }
+
+  tree tuple_type = TyTyResolveCompile::compile (ctx, tyty);
+  rust_assert (tuple_type != nullptr);
+
+  // this assumes all fields are in order from type resolution
+  std::vector<tree> vals;
+  for (auto &elem : expr.get_tuple_elems ())
+    {
+      auto e = CompileExpr::Compile (elem.get (), ctx);
+      vals.push_back (e);
+    }
+
+  translated
+    = ctx->get_backend ()->constructor_expression (tuple_type, false, vals, -1,
+						   expr.get_locus ());
+}
+
+void
+CompileExpr::visit (HIR::ReturnExpr &expr)
+{
+  auto fncontext = ctx->peek_fn ();
+
+  std::vector<tree> retstmts;
+  if (expr.has_return_expr ())
+    {
+      tree compiled_expr = CompileExpr::Compile (expr.return_expr.get (), ctx);
+      rust_assert (compiled_expr != nullptr);
+
+      retstmts.push_back (compiled_expr);
+    }
+
+  auto s = ctx->get_backend ()->return_statement (fncontext.fndecl, retstmts,
+						  expr.get_locus ());
+  ctx->add_statement (s);
+}
+
+void
+CompileExpr::visit (HIR::ArithmeticOrLogicalExpr &expr)
+{
+  auto op = expr.get_expr_type ();
+  auto lhs = CompileExpr::Compile (expr.get_lhs (), ctx);
+  auto rhs = CompileExpr::Compile (expr.get_rhs (), ctx);
+
+  // this might be an operator overload situation lets check
+  TyTy::FnType *fntype;
+  bool is_op_overload = ctx->get_tyctx ()->lookup_operator_overload (
+    expr.get_mappings ().get_hirid (), &fntype);
+  if (is_op_overload)
+    {
+      auto lang_item_type
+	= Analysis::RustLangItem::OperatorToLangItem (expr.get_expr_type ());
+      translated = resolve_operator_overload (lang_item_type, expr, lhs, rhs,
+					      expr.get_lhs (), expr.get_rhs ());
+      return;
+    }
+
+  translated
+    = ctx->get_backend ()->arithmetic_or_logical_expression (op, lhs, rhs,
+							     expr.get_locus ());
+}
+
+void
+CompileExpr::visit (HIR::CompoundAssignmentExpr &expr)
+{
+  auto op = expr.get_expr_type ();
+  auto lhs = CompileExpr::Compile (expr.get_left_expr ().get (), ctx);
+  auto rhs = CompileExpr::Compile (expr.get_right_expr ().get (), ctx);
+
+  // this might be an operator overload situation lets check
+  TyTy::FnType *fntype;
+  bool is_op_overload = ctx->get_tyctx ()->lookup_operator_overload (
+    expr.get_mappings ().get_hirid (), &fntype);
+  if (is_op_overload)
+    {
+      auto lang_item_type
+	= Analysis::RustLangItem::CompoundAssignmentOperatorToLangItem (
+	  expr.get_expr_type ());
+      auto compound_assignment
+	= resolve_operator_overload (lang_item_type, expr, lhs, rhs,
+				     expr.get_left_expr ().get (),
+				     expr.get_right_expr ().get ());
+      ctx->add_statement (compound_assignment);
+
+      return;
+    }
+
+  auto operator_expr
+    = ctx->get_backend ()->arithmetic_or_logical_expression (op, lhs, rhs,
+							     expr.get_locus ());
+  tree assignment
+    = ctx->get_backend ()->assignment_statement (lhs, operator_expr,
+						 expr.get_locus ());
+  ctx->add_statement (assignment);
+}
+
+void
+CompileExpr::visit (HIR::NegationExpr &expr)
+{
+  auto op = expr.get_expr_type ();
+  auto negated_expr = CompileExpr::Compile (expr.get_expr ().get (), ctx);
+  auto location = expr.get_locus ();
+
+  // this might be an operator overload situation lets check
+  TyTy::FnType *fntype;
+  bool is_op_overload = ctx->get_tyctx ()->lookup_operator_overload (
+    expr.get_mappings ().get_hirid (), &fntype);
+  if (is_op_overload)
+    {
+      auto lang_item_type
+	= Analysis::RustLangItem::NegationOperatorToLangItem (op);
+      translated
+	= resolve_operator_overload (lang_item_type, expr, negated_expr,
+				     nullptr, expr.get_expr ().get (), nullptr);
+      return;
+    }
+
+  translated
+    = ctx->get_backend ()->negation_expression (op, negated_expr, location);
+}
+
+void
+CompileExpr::visit (HIR::ComparisonExpr &expr)
+{
+  auto op = expr.get_expr_type ();
+  auto lhs = CompileExpr::Compile (expr.get_lhs (), ctx);
+  auto rhs = CompileExpr::Compile (expr.get_rhs (), ctx);
+  auto location = expr.get_locus ();
+
+  translated
+    = ctx->get_backend ()->comparison_expression (op, lhs, rhs, location);
+}
+
+void
+CompileExpr::visit (HIR::LazyBooleanExpr &expr)
+{
+  auto op = expr.get_expr_type ();
+  auto lhs = CompileExpr::Compile (expr.get_lhs (), ctx);
+  auto rhs = CompileExpr::Compile (expr.get_rhs (), ctx);
+  auto location = expr.get_locus ();
+
+  translated
+    = ctx->get_backend ()->lazy_boolean_expression (op, lhs, rhs, location);
+}
+
+void
+CompileExpr::visit (HIR::TypeCastExpr &expr)
+{
+  TyTy::BaseType *type_to_cast_to_ty = nullptr;
+  if (!ctx->get_tyctx ()->lookup_type (expr.get_mappings ().get_hirid (),
+				       &type_to_cast_to_ty))
+    {
+      translated = error_mark_node;
+      return;
+    }
+
+  TyTy::BaseType *casted_tyty = nullptr;
+  if (!ctx->get_tyctx ()->lookup_type (
+	expr.get_casted_expr ()->get_mappings ().get_hirid (), &casted_tyty))
+    {
+      translated = error_mark_node;
+      return;
+    }
+
+  auto type_to_cast_to = TyTyResolveCompile::compile (ctx, type_to_cast_to_ty);
+  auto casted_expr = CompileExpr::Compile (expr.get_casted_expr ().get (), ctx);
+
+  std::vector<Resolver::Adjustment> *adjustments = nullptr;
+  bool ok = ctx->get_tyctx ()->lookup_cast_autoderef_mappings (
+    expr.get_mappings ().get_hirid (), &adjustments);
+  if (ok)
+    {
+      casted_expr
+	= resolve_adjustements (*adjustments, casted_expr, expr.get_locus ());
+    }
+
+  translated
+    = type_cast_expression (type_to_cast_to, casted_expr, expr.get_locus ());
+}
+
+void
+CompileExpr::visit (HIR::IfExpr &expr)
+{
+  auto stmt = CompileConditionalBlocks::compile (&expr, ctx, nullptr);
+  ctx->add_statement (stmt);
+}
+
+void
+CompileExpr::visit (HIR::IfExprConseqElse &expr)
+{
+  TyTy::BaseType *if_type = nullptr;
+  if (!ctx->get_tyctx ()->lookup_type (expr.get_mappings ().get_hirid (),
+				       &if_type))
+    {
+      rust_error_at (expr.get_locus (),
+		     "failed to lookup type of IfExprConseqElse");
+      return;
+    }
+
+  Bvariable *tmp = NULL;
+  bool needs_temp = !if_type->is_unit ();
+  if (needs_temp)
+    {
+      fncontext fnctx = ctx->peek_fn ();
+      tree enclosing_scope = ctx->peek_enclosing_scope ();
+      tree block_type = TyTyResolveCompile::compile (ctx, if_type);
+
+      bool is_address_taken = false;
+      tree ret_var_stmt = nullptr;
+      tmp = ctx->get_backend ()->temporary_variable (
+	fnctx.fndecl, enclosing_scope, block_type, NULL, is_address_taken,
+	expr.get_locus (), &ret_var_stmt);
+      ctx->add_statement (ret_var_stmt);
+    }
+
+  auto stmt = CompileConditionalBlocks::compile (&expr, ctx, tmp);
+  ctx->add_statement (stmt);
+
+  if (tmp != NULL)
+    {
+      translated = ctx->get_backend ()->var_expression (tmp, expr.get_locus ());
+    }
+}
+
+void
+CompileExpr::visit (HIR::IfExprConseqIf &expr)
+{
+  TyTy::BaseType *if_type = nullptr;
+  if (!ctx->get_tyctx ()->lookup_type (expr.get_mappings ().get_hirid (),
+				       &if_type))
+    {
+      rust_error_at (expr.get_locus (),
+		     "failed to lookup type of IfExprConseqElse");
+      return;
+    }
+
+  Bvariable *tmp = NULL;
+  bool needs_temp = !if_type->is_unit ();
+  if (needs_temp)
+    {
+      fncontext fnctx = ctx->peek_fn ();
+      tree enclosing_scope = ctx->peek_enclosing_scope ();
+      tree block_type = TyTyResolveCompile::compile (ctx, if_type);
+
+      bool is_address_taken = false;
+      tree ret_var_stmt = nullptr;
+      tmp = ctx->get_backend ()->temporary_variable (
+	fnctx.fndecl, enclosing_scope, block_type, NULL, is_address_taken,
+	expr.get_locus (), &ret_var_stmt);
+      ctx->add_statement (ret_var_stmt);
+    }
+
+  auto stmt = CompileConditionalBlocks::compile (&expr, ctx, tmp);
+  ctx->add_statement (stmt);
+
+  if (tmp != NULL)
+    {
+      translated = ctx->get_backend ()->var_expression (tmp, expr.get_locus ());
+    }
+}
+
+void
+CompileExpr::visit (HIR::BlockExpr &expr)
+{
+  TyTy::BaseType *block_tyty = nullptr;
+  if (!ctx->get_tyctx ()->lookup_type (expr.get_mappings ().get_hirid (),
+				       &block_tyty))
+    {
+      rust_error_at (expr.get_locus (), "failed to lookup type of BlockExpr");
+      return;
+    }
+
+  Bvariable *tmp = NULL;
+  bool needs_temp = !block_tyty->is_unit ();
+  if (needs_temp)
+    {
+      fncontext fnctx = ctx->peek_fn ();
+      tree enclosing_scope = ctx->peek_enclosing_scope ();
+      tree block_type = TyTyResolveCompile::compile (ctx, block_tyty);
+
+      bool is_address_taken = false;
+      tree ret_var_stmt = nullptr;
+      tmp = ctx->get_backend ()->temporary_variable (
+	fnctx.fndecl, enclosing_scope, block_type, NULL, is_address_taken,
+	expr.get_locus (), &ret_var_stmt);
+      ctx->add_statement (ret_var_stmt);
+    }
+
+  auto block_stmt = CompileBlock::compile (&expr, ctx, tmp);
+  rust_assert (TREE_CODE (block_stmt) == BIND_EXPR);
+  ctx->add_statement (block_stmt);
+
+  if (tmp != NULL)
+    {
+      translated = ctx->get_backend ()->var_expression (tmp, expr.get_locus ());
+    }
+}
+
+void
+CompileExpr::visit (HIR::UnsafeBlockExpr &expr)
+{
+  expr.get_block_expr ()->accept_vis (*this);
+}
+
+void
+CompileExpr::visit (HIR::StructExprStruct &struct_expr)
+{
+  TyTy::BaseType *tyty = nullptr;
+  if (!ctx->get_tyctx ()->lookup_type (struct_expr.get_mappings ().get_hirid (),
+				       &tyty))
+    {
+      rust_error_at (struct_expr.get_locus (), "unknown type");
+      return;
+    }
+
+  rust_assert (tyty->is_unit ());
+  translated = ctx->get_backend ()->unit_expression ();
+}
+
+void
+CompileExpr::visit (HIR::StructExprStructFields &struct_expr)
+{
+  TyTy::BaseType *tyty = nullptr;
+  if (!ctx->get_tyctx ()->lookup_type (struct_expr.get_mappings ().get_hirid (),
+				       &tyty))
+    {
+      rust_error_at (struct_expr.get_locus (), "unknown type");
+      return;
+    }
+
+  // it must be an ADT
+  rust_assert (tyty->get_kind () == TyTy::TypeKind::ADT);
+  TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (tyty);
+
+  // what variant is it?
+  int union_disriminator = struct_expr.union_index;
+  TyTy::VariantDef *variant = nullptr;
+  if (!adt->is_enum ())
+    {
+      rust_assert (adt->number_of_variants () == 1);
+      variant = adt->get_variants ().at (0);
+    }
+  else
+    {
+      HirId variant_id;
+      bool ok = ctx->get_tyctx ()->lookup_variant_definition (
+	struct_expr.get_struct_name ().get_mappings ().get_hirid (),
+	&variant_id);
+      rust_assert (ok);
+
+      ok
+	= adt->lookup_variant_by_id (variant_id, &variant, &union_disriminator);
+      rust_assert (ok);
+    }
+
+  // compile it
+  tree compiled_adt_type = TyTyResolveCompile::compile (ctx, tyty);
+
+  std::vector<tree> arguments;
+  if (adt->is_union ())
+    {
+      rust_assert (struct_expr.get_fields ().size () == 1);
+
+      // assignments are coercion sites so lets convert the rvalue if
+      // necessary
+      auto respective_field = variant->get_field_at_index (union_disriminator);
+      auto expected = respective_field->get_field_type ();
+
+      // process arguments
+      auto &argument = struct_expr.get_fields ().at (0);
+      auto lvalue_locus
+	= ctx->get_mappings ()->lookup_location (expected->get_ty_ref ());
+      auto rvalue_locus = argument->get_locus ();
+      auto rvalue = CompileStructExprField::Compile (argument.get (), ctx);
+
+      TyTy::BaseType *actual = nullptr;
+      bool ok = ctx->get_tyctx ()->lookup_type (
+	argument->get_mappings ().get_hirid (), &actual);
+
+      if (ok)
+	{
+	  rvalue
+	    = coercion_site (argument->get_mappings ().get_hirid (), rvalue,
+			     actual, expected, lvalue_locus, rvalue_locus);
+	}
+
+      // add it to the list
+      arguments.push_back (rvalue);
+    }
+  else
+    {
+      // this assumes all fields are in order from type resolution and if a
+      // base struct was specified those fields are filed via accesors
+      for (size_t i = 0; i < struct_expr.get_fields ().size (); i++)
+	{
+	  // assignments are coercion sites so lets convert the rvalue if
+	  // necessary
+	  auto respective_field = variant->get_field_at_index (i);
+	  auto expected = respective_field->get_field_type ();
+
+	  // process arguments
+	  auto &argument = struct_expr.get_fields ().at (i);
+	  auto lvalue_locus
+	    = ctx->get_mappings ()->lookup_location (expected->get_ty_ref ());
+	  auto rvalue_locus = argument->get_locus ();
+	  auto rvalue = CompileStructExprField::Compile (argument.get (), ctx);
+
+	  TyTy::BaseType *actual = nullptr;
+	  bool ok = ctx->get_tyctx ()->lookup_type (
+	    argument->get_mappings ().get_hirid (), &actual);
+
+	  // coerce it if required/possible see
+	  // compile/torture/struct_base_init_1.rs
+	  if (ok)
+	    {
+	      rvalue
+		= coercion_site (argument->get_mappings ().get_hirid (), rvalue,
+				 actual, expected, lvalue_locus, rvalue_locus);
+	    }
+
+	  // add it to the list
+	  arguments.push_back (rvalue);
+	}
+    }
+
+  // the constructor depends on whether this is actually an enum or not if
+  // its an enum we need to setup the discriminator
+  std::vector<tree> ctor_arguments;
+  if (adt->is_enum ())
+    {
+      HIR::Expr *discrim_expr = variant->get_discriminant ();
+      tree discrim_expr_node = CompileExpr::Compile (discrim_expr, ctx);
+      tree folded_discrim_expr = fold_expr (discrim_expr_node);
+      tree qualifier = folded_discrim_expr;
+
+      ctor_arguments.push_back (qualifier);
+    }
+  for (auto &arg : arguments)
+    ctor_arguments.push_back (arg);
+
+  translated = ctx->get_backend ()->constructor_expression (
+    compiled_adt_type, adt->is_enum (), ctor_arguments, union_disriminator,
+    struct_expr.get_locus ());
+}
+
+void
+CompileExpr::visit (HIR::GroupedExpr &expr)
+{
+  translated = CompileExpr::Compile (expr.get_expr_in_parens ().get (), ctx);
+}
+
+void
+CompileExpr::visit (HIR::FieldAccessExpr &expr)
+{
+  HIR::Expr *receiver_expr = expr.get_receiver_expr ().get ();
+  tree receiver_ref = CompileExpr::Compile (receiver_expr, ctx);
+
+  // resolve the receiver back to ADT type
+  TyTy::BaseType *receiver = nullptr;
+  if (!ctx->get_tyctx ()->lookup_type (
+	expr.get_receiver_expr ()->get_mappings ().get_hirid (), &receiver))
+    {
+      rust_error_at (expr.get_receiver_expr ()->get_locus (),
+		     "unresolved type for receiver");
+      return;
+    }
+
+  size_t field_index = 0;
+  if (receiver->get_kind () == TyTy::TypeKind::ADT)
+    {
+      TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (receiver);
+      rust_assert (!adt->is_enum ());
+      rust_assert (adt->number_of_variants () == 1);
+
+      TyTy::VariantDef *variant = adt->get_variants ().at (0);
+      bool ok
+	= variant->lookup_field (expr.get_field_name (), nullptr, &field_index);
+      rust_assert (ok);
+    }
+  else if (receiver->get_kind () == TyTy::TypeKind::REF)
+    {
+      TyTy::ReferenceType *r = static_cast<TyTy::ReferenceType *> (receiver);
+      TyTy::BaseType *b = r->get_base ();
+      rust_assert (b->get_kind () == TyTy::TypeKind::ADT);
+
+      TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (b);
+      rust_assert (!adt->is_enum ());
+      rust_assert (adt->number_of_variants () == 1);
+
+      TyTy::VariantDef *variant = adt->get_variants ().at (0);
+      bool ok
+	= variant->lookup_field (expr.get_field_name (), nullptr, &field_index);
+      rust_assert (ok);
+
+      tree indirect = indirect_expression (receiver_ref, expr.get_locus ());
+      receiver_ref = indirect;
+    }
+
+  translated
+    = ctx->get_backend ()->struct_field_expression (receiver_ref, field_index,
+						    expr.get_locus ());
+}
+
+void
+CompileExpr::visit (HIR::QualifiedPathInExpression &expr)
+{
+  translated = ResolvePathRef::Compile (expr, ctx);
+}
+
+void
+CompileExpr::visit (HIR::PathInExpression &expr)
+{
+  translated = ResolvePathRef::Compile (expr, ctx);
+}
+
+void
+CompileExpr::visit (HIR::LoopExpr &expr)
+{
+  TyTy::BaseType *block_tyty = nullptr;
+  if (!ctx->get_tyctx ()->lookup_type (expr.get_mappings ().get_hirid (),
+				       &block_tyty))
+    {
+      rust_error_at (expr.get_locus (), "failed to lookup type of BlockExpr");
+      return;
+    }
+
+  fncontext fnctx = ctx->peek_fn ();
+  tree enclosing_scope = ctx->peek_enclosing_scope ();
+  tree block_type = TyTyResolveCompile::compile (ctx, block_tyty);
+
+  bool is_address_taken = false;
+  tree ret_var_stmt = NULL_TREE;
+  Bvariable *tmp = ctx->get_backend ()->temporary_variable (
+    fnctx.fndecl, enclosing_scope, block_type, NULL, is_address_taken,
+    expr.get_locus (), &ret_var_stmt);
+  ctx->add_statement (ret_var_stmt);
+  ctx->push_loop_context (tmp);
+
+  if (expr.has_loop_label ())
+    {
+      HIR::LoopLabel &loop_label = expr.get_loop_label ();
+      tree label
+	= ctx->get_backend ()->label (fnctx.fndecl,
+				      loop_label.get_lifetime ().get_name (),
+				      loop_label.get_locus ());
+      tree label_decl = ctx->get_backend ()->label_definition_statement (label);
+      ctx->add_statement (label_decl);
+      ctx->insert_label_decl (
+	loop_label.get_lifetime ().get_mappings ().get_hirid (), label);
+    }
+
+  tree loop_begin_label
+    = ctx->get_backend ()->label (fnctx.fndecl, "", expr.get_locus ());
+  tree loop_begin_label_decl
+    = ctx->get_backend ()->label_definition_statement (loop_begin_label);
+  ctx->add_statement (loop_begin_label_decl);
+  ctx->push_loop_begin_label (loop_begin_label);
+
+  tree code_block
+    = CompileBlock::compile (expr.get_loop_block ().get (), ctx, nullptr);
+  tree loop_expr
+    = ctx->get_backend ()->loop_expression (code_block, expr.get_locus ());
+  ctx->add_statement (loop_expr);
+
+  ctx->pop_loop_context ();
+  translated = ctx->get_backend ()->var_expression (tmp, expr.get_locus ());
+
+  ctx->pop_loop_begin_label ();
+}
+
+void
+CompileExpr::visit (HIR::WhileLoopExpr &expr)
+{
+  fncontext fnctx = ctx->peek_fn ();
+  if (expr.has_loop_label ())
+    {
+      HIR::LoopLabel &loop_label = expr.get_loop_label ();
+      tree label
+	= ctx->get_backend ()->label (fnctx.fndecl,
+				      loop_label.get_lifetime ().get_name (),
+				      loop_label.get_locus ());
+      tree label_decl = ctx->get_backend ()->label_definition_statement (label);
+      ctx->add_statement (label_decl);
+      ctx->insert_label_decl (
+	loop_label.get_lifetime ().get_mappings ().get_hirid (), label);
+    }
+
+  std::vector<Bvariable *> locals;
+  Location start_location = expr.get_loop_block ()->get_locus ();
+  Location end_location = expr.get_loop_block ()->get_locus (); // FIXME
+
+  tree enclosing_scope = ctx->peek_enclosing_scope ();
+  tree loop_block
+    = ctx->get_backend ()->block (fnctx.fndecl, enclosing_scope, locals,
+				  start_location, end_location);
+  ctx->push_block (loop_block);
+
+  tree loop_begin_label
+    = ctx->get_backend ()->label (fnctx.fndecl, "", expr.get_locus ());
+  tree loop_begin_label_decl
+    = ctx->get_backend ()->label_definition_statement (loop_begin_label);
+  ctx->add_statement (loop_begin_label_decl);
+  ctx->push_loop_begin_label (loop_begin_label);
+
+  tree condition
+    = CompileExpr::Compile (expr.get_predicate_expr ().get (), ctx);
+  tree exit_expr
+    = ctx->get_backend ()->exit_expression (condition, expr.get_locus ());
+  ctx->add_statement (exit_expr);
+
+  tree code_block_stmt
+    = CompileBlock::compile (expr.get_loop_block ().get (), ctx, nullptr);
+  rust_assert (TREE_CODE (code_block_stmt) == BIND_EXPR);
+  ctx->add_statement (code_block_stmt);
+
+  ctx->pop_loop_begin_label ();
+  ctx->pop_block ();
+
+  tree loop_expr
+    = ctx->get_backend ()->loop_expression (loop_block, expr.get_locus ());
+  ctx->add_statement (loop_expr);
+}
+
+void
+CompileExpr::visit (HIR::BreakExpr &expr)
+{
+  if (expr.has_break_expr ())
+    {
+      tree compiled_expr = CompileExpr::Compile (expr.get_expr ().get (), ctx);
+
+      Bvariable *loop_result_holder = ctx->peek_loop_context ();
+      tree result_reference
+	= ctx->get_backend ()->var_expression (loop_result_holder,
+					       expr.get_expr ()->get_locus ());
+
+      tree assignment
+	= ctx->get_backend ()->assignment_statement (result_reference,
+						     compiled_expr,
+						     expr.get_locus ());
+      ctx->add_statement (assignment);
+    }
+
+  if (expr.has_label ())
+    {
+      NodeId resolved_node_id = UNKNOWN_NODEID;
+      if (!ctx->get_resolver ()->lookup_resolved_label (
+	    expr.get_label ().get_mappings ().get_nodeid (), &resolved_node_id))
+	{
+	  rust_error_at (
+	    expr.get_label ().get_locus (),
+	    "failed to resolve compiled label for label %s",
+	    expr.get_label ().get_mappings ().as_string ().c_str ());
+	  return;
+	}
+
+      HirId ref = UNKNOWN_HIRID;
+      if (!ctx->get_mappings ()->lookup_node_to_hir (resolved_node_id, &ref))
+	{
+	  rust_fatal_error (expr.get_locus (), "reverse lookup label failure");
+	  return;
+	}
+
+      tree label = NULL_TREE;
+      if (!ctx->lookup_label_decl (ref, &label))
+	{
+	  rust_error_at (expr.get_label ().get_locus (),
+			 "failed to lookup compiled label");
+	  return;
+	}
+
+      tree goto_label
+	= ctx->get_backend ()->goto_statement (label, expr.get_locus ());
+      ctx->add_statement (goto_label);
+    }
+  else
+    {
+      tree exit_expr = ctx->get_backend ()->exit_expression (
+	ctx->get_backend ()->boolean_constant_expression (true),
+	expr.get_locus ());
+      ctx->add_statement (exit_expr);
+    }
+}
+
+void
+CompileExpr::visit (HIR::ContinueExpr &expr)
+{
+  tree label = ctx->peek_loop_begin_label ();
+  if (expr.has_label ())
+    {
+      NodeId resolved_node_id = UNKNOWN_NODEID;
+      if (!ctx->get_resolver ()->lookup_resolved_label (
+	    expr.get_label ().get_mappings ().get_nodeid (), &resolved_node_id))
+	{
+	  rust_error_at (
+	    expr.get_label ().get_locus (),
+	    "failed to resolve compiled label for label %s",
+	    expr.get_label ().get_mappings ().as_string ().c_str ());
+	  return;
+	}
+
+      HirId ref = UNKNOWN_HIRID;
+      if (!ctx->get_mappings ()->lookup_node_to_hir (resolved_node_id, &ref))
+	{
+	  rust_fatal_error (expr.get_locus (), "reverse lookup label failure");
+	  return;
+	}
+
+      if (!ctx->lookup_label_decl (ref, &label))
+	{
+	  rust_error_at (expr.get_label ().get_locus (),
+			 "failed to lookup compiled label");
+	  return;
+	}
+    }
+
+  translated = ctx->get_backend ()->goto_statement (label, expr.get_locus ());
+}
+
+void
+CompileExpr::visit (HIR::BorrowExpr &expr)
+{
+  tree main_expr = CompileExpr::Compile (expr.get_expr ().get (), ctx);
+  if (SLICE_TYPE_P (TREE_TYPE (main_expr)))
+    {
+      translated = main_expr;
+      return;
+    }
+
+  TyTy::BaseType *tyty = nullptr;
+  if (!ctx->get_tyctx ()->lookup_type (expr.get_mappings ().get_hirid (),
+				       &tyty))
+    return;
+
+  translated = address_expression (main_expr, expr.get_locus ());
+}
+
+void
+CompileExpr::visit (HIR::DereferenceExpr &expr)
+{
+  TyTy::BaseType *tyty = nullptr;
+  if (!ctx->get_tyctx ()->lookup_type (expr.get_mappings ().get_hirid (),
+				       &tyty))
+    {
+      rust_fatal_error (expr.get_locus (),
+			"did not resolve type for this TupleExpr");
+      return;
+    }
+
+  tree main_expr = CompileExpr::Compile (expr.get_expr ().get (), ctx);
+
+  // this might be an operator overload situation lets check
+  TyTy::FnType *fntype;
+  bool is_op_overload = ctx->get_tyctx ()->lookup_operator_overload (
+    expr.get_mappings ().get_hirid (), &fntype);
+  if (is_op_overload)
+    {
+      auto lang_item_type = Analysis::RustLangItem::ItemType::DEREF;
+      tree operator_overload_call
+	= resolve_operator_overload (lang_item_type, expr, main_expr, nullptr,
+				     expr.get_expr ().get (), nullptr);
+
+      // rust deref always returns a reference from this overload then we can
+      // actually do the indirection
+      main_expr = operator_overload_call;
+    }
+
+  tree expected_type = TyTyResolveCompile::compile (ctx, tyty);
+  if (SLICE_TYPE_P (TREE_TYPE (main_expr)) && SLICE_TYPE_P (expected_type))
+    {
+      translated = main_expr;
+      return;
+    }
+
+  translated = indirect_expression (main_expr, expr.get_locus ());
+}
+
+void
+CompileExpr::visit (HIR::LiteralExpr &expr)
+{
+  TyTy::BaseType *tyty = nullptr;
+  if (!ctx->get_tyctx ()->lookup_type (expr.get_mappings ().get_hirid (),
+				       &tyty))
+    return;
+
+  switch (expr.get_lit_type ())
+    {
+    case HIR::Literal::BOOL:
+      translated = compile_bool_literal (expr, tyty);
+      return;
+
+    case HIR::Literal::INT:
+      translated = compile_integer_literal (expr, tyty);
+      return;
+
+    case HIR::Literal::FLOAT:
+      translated = compile_float_literal (expr, tyty);
+      return;
+
+    case HIR::Literal::CHAR:
+      translated = compile_char_literal (expr, tyty);
+      return;
+
+    case HIR::Literal::BYTE:
+      translated = compile_byte_literal (expr, tyty);
+      return;
+
+    case HIR::Literal::STRING:
+      translated = compile_string_literal (expr, tyty);
+      return;
+
+    case HIR::Literal::BYTE_STRING:
+      translated = compile_byte_string_literal (expr, tyty);
+      return;
+    }
+}
+
+void
+CompileExpr::visit (HIR::AssignmentExpr &expr)
+{
+  auto lvalue = CompileExpr::Compile (expr.get_lhs (), ctx);
+  auto rvalue = CompileExpr::Compile (expr.get_rhs (), ctx);
+
+  // assignments are coercion sites so lets convert the rvalue if necessary
+  TyTy::BaseType *expected = nullptr;
+  TyTy::BaseType *actual = nullptr;
+
+  bool ok;
+  ok = ctx->get_tyctx ()->lookup_type (
+    expr.get_lhs ()->get_mappings ().get_hirid (), &expected);
+  rust_assert (ok);
+
+  ok = ctx->get_tyctx ()->lookup_type (
+    expr.get_rhs ()->get_mappings ().get_hirid (), &actual);
+  rust_assert (ok);
+
+  rvalue = coercion_site (expr.get_mappings ().get_hirid (), rvalue, actual,
+			  expected, expr.get_lhs ()->get_locus (),
+			  expr.get_rhs ()->get_locus ());
+
+  tree assignment
+    = ctx->get_backend ()->assignment_statement (lvalue, rvalue,
+						 expr.get_locus ());
+
+  ctx->add_statement (assignment);
+}
+
+// Helper for sort_tuple_patterns.
+// Determine whether Patterns a and b are really the same pattern.
+// FIXME: This is a nasty hack to avoid properly implementing a comparison
+//        for Patterns, which we really probably do want at some point.
+static bool
+patterns_mergeable (HIR::Pattern *a, HIR::Pattern *b)
+{
+  if (!a || !b)
+    return false;
+
+  HIR::Pattern::PatternType pat_type = a->get_pattern_type ();
+  if (b->get_pattern_type () != pat_type)
+    return false;
+
+  switch (pat_type)
+    {
+      case HIR::Pattern::PatternType::PATH: {
+	// FIXME: this is far too naive
+	HIR::PathPattern &aref = *static_cast<HIR::PathPattern *> (a);
+	HIR::PathPattern &bref = *static_cast<HIR::PathPattern *> (b);
+	if (aref.get_num_segments () != bref.get_num_segments ())
+	  return false;
+
+	const auto &asegs = aref.get_segments ();
+	const auto &bsegs = bref.get_segments ();
+	for (size_t i = 0; i < asegs.size (); i++)
+	  {
+	    if (asegs[i].as_string () != bsegs[i].as_string ())
+	      return false;
+	  }
+	return true;
+      }
+      break;
+      case HIR::Pattern::PatternType::LITERAL: {
+	HIR::LiteralPattern &aref = *static_cast<HIR::LiteralPattern *> (a);
+	HIR::LiteralPattern &bref = *static_cast<HIR::LiteralPattern *> (b);
+	return aref.get_literal ().is_equal (bref.get_literal ());
+      }
+      break;
+      case HIR::Pattern::PatternType::IDENTIFIER: {
+	// TODO
+      }
+      break;
+    case HIR::Pattern::PatternType::WILDCARD:
+      return true;
+      break;
+
+      // TODO
+
+    default:;
+    }
+  return false;
+}
+
+// A little container for rearranging the patterns and cases in a match
+// expression while simplifying.
+struct PatternMerge
+{
+  std::unique_ptr<HIR::MatchCase> wildcard;
+  std::vector<std::unique_ptr<HIR::Pattern>> heads;
+  std::vector<std::vector<HIR::MatchCase>> cases;
+};
+
+// Helper for simplify_tuple_match.
+// For each tuple pattern in a given match, pull out the first elt of the
+// tuple and construct a new MatchCase with the remaining tuple elts as the
+// pattern. Return a mapping from each _unique_ first tuple element to a
+// vec of cases for a new match.
+//
+// FIXME: This used to be a std::map<Pattern, Vec<MatchCase>>, but it doesn't
+// actually work like we want - the Pattern includes an HIR ID, which is unique
+// per Pattern object. This means we don't have a good means for comparing
+// Patterns. It would probably be best to actually implement a means of
+// properly comparing patterns, and then use an actual map.
+//
+static struct PatternMerge
+sort_tuple_patterns (HIR::MatchExpr &expr)
+{
+  rust_assert (expr.get_scrutinee_expr ()->get_expression_type ()
+	       == HIR::Expr::ExprType::Tuple);
+
+  struct PatternMerge result;
+  result.wildcard = nullptr;
+  result.heads = std::vector<std::unique_ptr<HIR::Pattern>> ();
+  result.cases = std::vector<std::vector<HIR::MatchCase>> ();
+
+  for (auto &match_case : expr.get_match_cases ())
+    {
+      HIR::MatchArm &case_arm = match_case.get_arm ();
+
+      // FIXME: Note we are only dealing with the first pattern in the arm.
+      // The patterns vector in the arm might hold many patterns, which are the
+      // patterns separated by the '|' token. Rustc abstracts these as "Or"
+      // patterns, and part of its simplification process is to get rid of them.
+      // We should get rid of the ORs too, maybe here or earlier than here?
+      auto pat = case_arm.get_patterns ()[0]->clone_pattern ();
+
+      // Record wildcards so we can add them in inner matches.
+      if (pat->get_pattern_type () == HIR::Pattern::PatternType::WILDCARD)
+	{
+	  // The *whole* pattern is a wild card (_).
+	  result.wildcard
+	    = std::unique_ptr<HIR::MatchCase> (new HIR::MatchCase (match_case));
+	  continue;
+	}
+
+      rust_assert (pat->get_pattern_type ()
+		   == HIR::Pattern::PatternType::TUPLE);
+
+      auto ref = *static_cast<HIR::TuplePattern *> (pat.get ());
+
+      rust_assert (ref.has_tuple_pattern_items ());
+
+      auto items
+	= HIR::TuplePattern (ref).get_items ()->clone_tuple_pattern_items ();
+      if (items->get_pattern_type ()
+	  == HIR::TuplePatternItems::TuplePatternItemType::MULTIPLE)
+	{
+	  auto items_ref
+	    = *static_cast<HIR::TuplePatternItemsMultiple *> (items.get ());
+
+	  // Pop the first pattern out
+	  auto patterns = std::vector<std::unique_ptr<HIR::Pattern>> ();
+	  auto first = items_ref.get_patterns ()[0]->clone_pattern ();
+	  for (auto p = items_ref.get_patterns ().begin () + 1;
+	       p != items_ref.get_patterns ().end (); p++)
+	    {
+	      patterns.push_back ((*p)->clone_pattern ());
+	    }
+
+	  // if there is only one pattern left, don't make a tuple out of it
+	  std::unique_ptr<HIR::Pattern> result_pattern;
+	  if (patterns.size () == 1)
+	    {
+	      result_pattern = std::move (patterns[0]);
+	    }
+	  else
+	    {
+	      auto new_items = std::unique_ptr<HIR::TuplePatternItems> (
+		new HIR::TuplePatternItemsMultiple (std::move (patterns)));
+
+	      // Construct a TuplePattern from the rest of the patterns
+	      result_pattern = std::unique_ptr<HIR::Pattern> (
+		new HIR::TuplePattern (ref.get_pattern_mappings (),
+				       std::move (new_items),
+				       ref.get_locus ()));
+	    }
+
+	  // I don't know why we need to make foo separately here but
+	  // using the { new_tuple } syntax in new_arm constructor does not
+	  // compile.
+	  auto foo = std::vector<std::unique_ptr<HIR::Pattern>> ();
+	  foo.emplace_back (std::move (result_pattern));
+	  HIR::MatchArm new_arm (std::move (foo), Location (), nullptr,
+				 AST::AttrVec ());
+
+	  HIR::MatchCase new_case (match_case.get_mappings (), new_arm,
+				   match_case.get_expr ()->clone_expr ());
+
+	  bool pushed = false;
+	  for (size_t i = 0; i < result.heads.size (); i++)
+	    {
+	      if (patterns_mergeable (result.heads[i].get (), first.get ()))
+		{
+		  result.cases[i].push_back (new_case);
+		  pushed = true;
+		}
+	    }
+
+	  if (!pushed)
+	    {
+	      result.heads.push_back (std::move (first));
+	      result.cases.push_back ({new_case});
+	    }
+	}
+      else /* TuplePatternItemType::RANGED */
+	{
+	  // FIXME
+	  gcc_unreachable ();
+	}
+    }
+
+  return result;
+}
+
+// Helper for CompileExpr::visit (HIR::MatchExpr).
+// Given a MatchExpr where the scrutinee is some kind of tuple, build an
+// equivalent match where only one element of the tuple is examined at a time.
+// This resulting match can then be lowered to a SWITCH_EXPR tree directly.
+//
+// The approach is as follows:
+// 1. Split the scrutinee and each pattern into the first (head) and the
+//    rest (tail).
+// 2. Build a mapping of unique pattern heads to the cases (tail and expr)
+//    that shared that pattern head in the original match.
+//    (This is the job of sort_tuple_patterns ()).
+// 3. For each unique pattern head, build a new MatchCase where the pattern
+//    is the unique head, and the expression is a new match where:
+//    - The scrutinee is the tail of the original scrutinee
+//    - The cases are are those built by the mapping in step 2, i.e. the
+//      tails of the patterns and the corresponing expressions from the
+//      original match expression.
+// 4. Do this recursively for each inner match, until there is nothing more
+//    to simplify.
+// 5. Build the resulting match which scrutinizes the head of the original
+//    scrutinee, using the cases built in step 3.
+static HIR::MatchExpr
+simplify_tuple_match (HIR::MatchExpr &expr)
+{
+  if (expr.get_scrutinee_expr ()->get_expression_type ()
+      != HIR::Expr::ExprType::Tuple)
+    return expr;
+
+  auto ref = *static_cast<HIR::TupleExpr *> (expr.get_scrutinee_expr ().get ());
+
+  auto &tail = ref.get_tuple_elems ();
+  rust_assert (tail.size () > 1);
+
+  auto head = std::move (tail[0]);
+  tail.erase (tail.begin (), tail.begin () + 1);
+
+  // e.g.
+  // match (tupA, tupB, tupC) {
+  //   (a1, b1, c1) => { blk1 },
+  //   (a2, b2, c2) => { blk2 },
+  //   (a1, b3, c3) => { blk3 },
+  // }
+  // tail = (tupB, tupC)
+  // head = tupA
+
+  // Make sure the tail is only a tuple if it consists of at least 2 elements.
+  std::unique_ptr<HIR::Expr> remaining;
+  if (tail.size () == 1)
+    remaining = std::move (tail[0]);
+  else
+    remaining = std::unique_ptr<HIR::Expr> (
+      new HIR::TupleExpr (ref.get_mappings (), std::move (tail),
+			  AST::AttrVec (), ref.get_outer_attrs (),
+			  ref.get_locus ()));
+
+  // e.g.
+  // a1 -> [(b1, c1) => { blk1 },
+  //        (b3, c3) => { blk3 }]
+  // a2 -> [(b2, c2) => { blk2 }]
+  struct PatternMerge map = sort_tuple_patterns (expr);
+
+  std::vector<HIR::MatchCase> cases;
+  // Construct the inner match for each unique first elt of the tuple
+  // patterns
+  for (size_t i = 0; i < map.heads.size (); i++)
+    {
+      auto inner_match_cases = map.cases[i];
+
+      // If there is a wildcard at the outer match level, then need to
+      // propegate the wildcard case into *every* inner match.
+      // FIXME: It is probably not correct to add this unconditionally, what if
+      // we have a pattern like (a, _, c)? Then there is already a wildcard in
+      // the inner matches, and having two will cause two 'default:' blocks
+      // which is an error.
+      if (map.wildcard != nullptr)
+	{
+	  inner_match_cases.push_back (*(map.wildcard.get ()));
+	}
+
+      // match (tupB, tupC) {
+      //   (b1, c1) => { blk1 },
+      //   (b3, c3) => { blk3 }
+      // }
+      HIR::MatchExpr inner_match (expr.get_mappings (),
+				  remaining->clone_expr (), inner_match_cases,
+				  AST::AttrVec (), expr.get_outer_attrs (),
+				  expr.get_locus ());
+
+      inner_match = simplify_tuple_match (inner_match);
+
+      auto outer_arm_pat = std::vector<std::unique_ptr<HIR::Pattern>> ();
+      outer_arm_pat.emplace_back (map.heads[i]->clone_pattern ());
+
+      HIR::MatchArm outer_arm (std::move (outer_arm_pat), expr.get_locus ());
+
+      // Need to move the inner match to the heap and put it in a unique_ptr to
+      // build the actual match case of the outer expression
+      // auto inner_expr = std::unique_ptr<HIR::Expr> (new HIR::MatchExpr
+      // (inner_match));
+      auto inner_expr = inner_match.clone_expr ();
+
+      // a1 => match (tupB, tupC) { ... }
+      HIR::MatchCase outer_case (expr.get_mappings (), outer_arm,
+				 std::move (inner_expr));
+
+      cases.push_back (outer_case);
+    }
+
+  // If there was a wildcard, make sure to include it at the outer match level
+  // too.
+  if (map.wildcard != nullptr)
+    {
+      cases.push_back (*(map.wildcard.get ()));
+    }
+
+  // match tupA {
+  //   a1 => match (tupB, tupC) {
+  //     (b1, c1) => { blk1 },
+  //     (b3, c3) => { blk3 }
+  //   }
+  //   a2 => match (tupB, tupC) {
+  //     (b2, c2) => { blk2 }
+  //   }
+  // }
+  HIR::MatchExpr outer_match (expr.get_mappings (), std::move (head), cases,
+			      AST::AttrVec (), expr.get_outer_attrs (),
+			      expr.get_locus ());
+
+  return outer_match;
+}
+
+// Helper for CompileExpr::visit (HIR::MatchExpr).
+// Check that the scrutinee of EXPR is a valid kind of expression to match on.
+// Return the TypeKind of the scrutinee if it is valid, or TyTy::TypeKind::ERROR
+// if not.
+static TyTy::TypeKind
+check_match_scrutinee (HIR::MatchExpr &expr, Context *ctx)
+{
+  TyTy::BaseType *scrutinee_expr_tyty = nullptr;
+  if (!ctx->get_tyctx ()->lookup_type (
+	expr.get_scrutinee_expr ()->get_mappings ().get_hirid (),
+	&scrutinee_expr_tyty))
+    {
+      return TyTy::TypeKind::ERROR;
+    }
+
+  TyTy::TypeKind scrutinee_kind = scrutinee_expr_tyty->get_kind ();
+  rust_assert ((TyTy::is_primitive_type_kind (scrutinee_kind)
+		&& scrutinee_kind != TyTy::TypeKind::NEVER)
+	       || scrutinee_kind == TyTy::TypeKind::ADT
+	       || scrutinee_kind == TyTy::TypeKind::TUPLE);
+
+  if (scrutinee_kind == TyTy::TypeKind::ADT)
+    {
+      // this will need to change but for now the first pass implementation,
+      // lets assert this is the case
+      TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (scrutinee_expr_tyty);
+      rust_assert (adt->is_enum ());
+      rust_assert (adt->number_of_variants () > 0);
+    }
+  else if (scrutinee_kind == TyTy::TypeKind::FLOAT)
+    {
+      // FIXME: CASE_LABEL_EXPR does not support floating point types.
+      // Find another way to compile these.
+      rust_sorry_at (expr.get_locus (),
+		     "match on floating-point types is not yet supported");
+    }
+
+  TyTy::BaseType *expr_tyty = nullptr;
+  if (!ctx->get_tyctx ()->lookup_type (expr.get_mappings ().get_hirid (),
+				       &expr_tyty))
+    {
+      return TyTy::TypeKind::ERROR;
+    }
+
+  return scrutinee_kind;
+}
+
+void
+CompileExpr::visit (HIR::MatchExpr &expr)
+{
+  // https://gcc.gnu.org/onlinedocs/gccint/Basic-Statements.html#Basic-Statements
+  // TODO
+  // SWITCH_ALL_CASES_P is true if the switch includes a default label or the
+  // case label ranges cover all possible values of the condition expression
+
+  /* Switch expression.
+
+     TREE_TYPE is the original type of the condition, before any
+     language required type conversions.  It may be NULL, in which case
+     the original type and final types are assumed to be the same.
+
+     Operand 0 is the expression used to perform the branch,
+     Operand 1 is the body of the switch, which probably contains
+       CASE_LABEL_EXPRs.  It may also be NULL, in which case operand 2
+       must not be NULL.  */
+  // DEFTREECODE (SWITCH_EXPR, "switch_expr", tcc_statement, 2)
+
+  /* Used to represent a case label.
+
+     Operand 0 is CASE_LOW.  It may be NULL_TREE, in which case the label
+       is a 'default' label.
+     Operand 1 is CASE_HIGH.  If it is NULL_TREE, the label is a simple
+       (one-value) case label.  If it is non-NULL_TREE, the case is a range.
+     Operand 2 is CASE_LABEL, which has the corresponding LABEL_DECL.
+     Operand 3 is CASE_CHAIN.  This operand is only used in tree-cfg.cc to
+       speed up the lookup of case labels which use a particular edge in
+       the control flow graph.  */
+  // DEFTREECODE (CASE_LABEL_EXPR, "case_label_expr", tcc_statement, 4)
+
+  TyTy::TypeKind scrutinee_kind = check_match_scrutinee (expr, ctx);
+  if (scrutinee_kind == TyTy::TypeKind::ERROR)
+    {
+      translated = error_mark_node;
+      return;
+    }
+
+  TyTy::BaseType *expr_tyty = nullptr;
+  if (!ctx->get_tyctx ()->lookup_type (expr.get_mappings ().get_hirid (),
+				       &expr_tyty))
+    {
+      translated = error_mark_node;
+      return;
+    }
+
+  fncontext fnctx = ctx->peek_fn ();
+  Bvariable *tmp = NULL;
+  bool needs_temp = !expr_tyty->is_unit ();
+  if (needs_temp)
+    {
+      tree enclosing_scope = ctx->peek_enclosing_scope ();
+      tree block_type = TyTyResolveCompile::compile (ctx, expr_tyty);
+
+      bool is_address_taken = false;
+      tree ret_var_stmt = nullptr;
+      tmp = ctx->get_backend ()->temporary_variable (
+	fnctx.fndecl, enclosing_scope, block_type, NULL, is_address_taken,
+	expr.get_locus (), &ret_var_stmt);
+      ctx->add_statement (ret_var_stmt);
+    }
+
+  // lets compile the scrutinee expression
+  tree match_scrutinee_expr
+    = CompileExpr::Compile (expr.get_scrutinee_expr ().get (), ctx);
+
+  tree match_scrutinee_expr_qualifier_expr;
+  if (TyTy::is_primitive_type_kind (scrutinee_kind))
+    {
+      match_scrutinee_expr_qualifier_expr = match_scrutinee_expr;
+    }
+  else if (scrutinee_kind == TyTy::TypeKind::ADT)
+    {
+      // need to access qualifier the field, if we use QUAL_UNION_TYPE this
+      // would be DECL_QUALIFIER i think. For now this will just access the
+      // first record field and its respective qualifier because it will always
+      // be set because this is all a big special union
+      tree scrutinee_first_record_expr
+	= ctx->get_backend ()->struct_field_expression (
+	  match_scrutinee_expr, 0, expr.get_scrutinee_expr ()->get_locus ());
+      match_scrutinee_expr_qualifier_expr
+	= ctx->get_backend ()->struct_field_expression (
+	  scrutinee_first_record_expr, 0,
+	  expr.get_scrutinee_expr ()->get_locus ());
+    }
+  else if (scrutinee_kind == TyTy::TypeKind::TUPLE)
+    {
+      // match on tuple becomes a series of nested switches, with one level
+      // for each element of the tuple from left to right.
+      auto exprtype = expr.get_scrutinee_expr ()->get_expression_type ();
+      switch (exprtype)
+	{
+	  case HIR::Expr::ExprType::Tuple: {
+	    // Build an equivalent expression which is nicer to lower.
+	    HIR::MatchExpr outer_match = simplify_tuple_match (expr);
+
+	    // We've rearranged the match into something that lowers better
+	    // to GENERIC trees.
+	    // For actually doing the lowering we need to compile the match
+	    // we've just made. But we're half-way through compiling the
+	    // original one.
+	    // ...
+	    // For now, let's just replace the original with the rearranged one
+	    // we just made, and compile that instead. What could go wrong? :)
+	    //
+	    // FIXME: What about when we decide a temporary is needed above?
+	    //        We might have already pushed a statement for it that
+	    //        we no longer need. Probably need to rearrange the order
+	    //        of these steps.
+	    expr = outer_match;
+
+	    scrutinee_kind = check_match_scrutinee (expr, ctx);
+	    if (scrutinee_kind == TyTy::TypeKind::ERROR)
+	      {
+		translated = error_mark_node;
+		return;
+	      }
+
+	    // Now compile the scrutinee of the simplified match.
+	    // FIXME: this part is duplicated from above.
+	    match_scrutinee_expr
+	      = CompileExpr::Compile (expr.get_scrutinee_expr ().get (), ctx);
+
+	    if (TyTy::is_primitive_type_kind (scrutinee_kind))
+	      {
+		match_scrutinee_expr_qualifier_expr = match_scrutinee_expr;
+	      }
+	    else if (scrutinee_kind == TyTy::TypeKind::ADT)
+	      {
+		// need to access qualifier the field, if we use QUAL_UNION_TYPE
+		// this would be DECL_QUALIFIER i think. For now this will just
+		// access the first record field and its respective qualifier
+		// because it will always be set because this is all a big
+		// special union
+		tree scrutinee_first_record_expr
+		  = ctx->get_backend ()->struct_field_expression (
+		    match_scrutinee_expr, 0,
+		    expr.get_scrutinee_expr ()->get_locus ());
+		match_scrutinee_expr_qualifier_expr
+		  = ctx->get_backend ()->struct_field_expression (
+		    scrutinee_first_record_expr, 0,
+		    expr.get_scrutinee_expr ()->get_locus ());
+	      }
+	    else
+	      {
+		// FIXME: There are other cases, but it better not be a Tuple
+		gcc_unreachable ();
+	      }
+	  }
+	  break;
+
+	  case HIR::Expr::ExprType::Path: {
+	    // FIXME
+	    gcc_unreachable ();
+	  }
+	  break;
+
+	default:
+	  gcc_unreachable ();
+	}
+    }
+  else
+    {
+      // FIXME: match on other types of expressions not yet implemented.
+      gcc_unreachable ();
+    }
+
+  // setup the end label so the cases can exit properly
+  tree fndecl = fnctx.fndecl;
+  Location end_label_locus = expr.get_locus (); // FIXME
+  tree end_label
+    = ctx->get_backend ()->label (fndecl,
+				  "" /* empty creates an artificial label */,
+				  end_label_locus);
+  tree end_label_decl_statement
+    = ctx->get_backend ()->label_definition_statement (end_label);
+
+  // setup the switch-body-block
+  Location start_location; // FIXME
+  Location end_location;   // FIXME
+  tree enclosing_scope = ctx->peek_enclosing_scope ();
+  tree switch_body_block
+    = ctx->get_backend ()->block (fndecl, enclosing_scope, {}, start_location,
+				  end_location);
+  ctx->push_block (switch_body_block);
+
+  for (auto &kase : expr.get_match_cases ())
+    {
+      // for now lets just get single pattern's working
+      HIR::MatchArm &kase_arm = kase.get_arm ();
+      rust_assert (kase_arm.get_patterns ().size () > 0);
+
+      // generate implicit label
+      Location arm_locus = kase_arm.get_locus ();
+      tree case_label = ctx->get_backend ()->label (
+	fndecl, "" /* empty creates an artificial label */, arm_locus);
+
+      // setup the bindings for the block
+      for (auto &kase_pattern : kase_arm.get_patterns ())
+	{
+	  tree switch_kase_expr
+	    = CompilePatternCaseLabelExpr::Compile (kase_pattern.get (),
+						    case_label, ctx);
+	  ctx->add_statement (switch_kase_expr);
+
+	  CompilePatternBindings::Compile (kase_pattern.get (),
+					   match_scrutinee_expr, ctx);
+	}
+
+      // compile the expr and setup the assignment if required when tmp != NULL
+      tree kase_expr_tree = CompileExpr::Compile (kase.get_expr ().get (), ctx);
+      if (tmp != NULL)
+	{
+	  tree result_reference
+	    = ctx->get_backend ()->var_expression (tmp, arm_locus);
+	  tree assignment
+	    = ctx->get_backend ()->assignment_statement (result_reference,
+							 kase_expr_tree,
+							 arm_locus);
+	  ctx->add_statement (assignment);
+	}
+
+      // go to end label
+      tree goto_end_label = build1_loc (arm_locus.gcc_location (), GOTO_EXPR,
+					void_type_node, end_label);
+      ctx->add_statement (goto_end_label);
+    }
+
+  // setup the switch expression
+  tree match_body = ctx->pop_block ();
+  tree match_expr_stmt
+    = build2_loc (expr.get_locus ().gcc_location (), SWITCH_EXPR,
+		  TREE_TYPE (match_scrutinee_expr_qualifier_expr),
+		  match_scrutinee_expr_qualifier_expr, match_body);
+  ctx->add_statement (match_expr_stmt);
+  ctx->add_statement (end_label_decl_statement);
+
+  if (tmp != NULL)
+    {
+      translated = ctx->get_backend ()->var_expression (tmp, expr.get_locus ());
+    }
+}
+
+void
+CompileExpr::visit (HIR::CallExpr &expr)
+{
+  TyTy::BaseType *tyty = nullptr;
+  if (!ctx->get_tyctx ()->lookup_type (
+	expr.get_fnexpr ()->get_mappings ().get_hirid (), &tyty))
+    {
+      rust_error_at (expr.get_locus (), "unknown type");
+      return;
+    }
+
+  // must be a tuple constructor
+  bool is_fn = tyty->get_kind () == TyTy::TypeKind::FNDEF
+	       || tyty->get_kind () == TyTy::TypeKind::FNPTR;
+  bool is_adt_ctor = !is_fn;
+  if (is_adt_ctor)
+    {
+      rust_assert (tyty->get_kind () == TyTy::TypeKind::ADT);
+      TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (tyty);
+      tree compiled_adt_type = TyTyResolveCompile::compile (ctx, tyty);
+
+      // what variant is it?
+      int union_disriminator = -1;
+      TyTy::VariantDef *variant = nullptr;
+      if (!adt->is_enum ())
+	{
+	  rust_assert (adt->number_of_variants () == 1);
+	  variant = adt->get_variants ().at (0);
+	}
+      else
+	{
+	  HirId variant_id;
+	  bool ok = ctx->get_tyctx ()->lookup_variant_definition (
+	    expr.get_fnexpr ()->get_mappings ().get_hirid (), &variant_id);
+	  rust_assert (ok);
+
+	  ok = adt->lookup_variant_by_id (variant_id, &variant,
+					  &union_disriminator);
+	  rust_assert (ok);
+	}
+
+      // this assumes all fields are in order from type resolution and if a
+      // base struct was specified those fields are filed via accesors
+      std::vector<tree> arguments;
+      for (size_t i = 0; i < expr.get_arguments ().size (); i++)
+	{
+	  auto &argument = expr.get_arguments ().at (i);
+	  auto rvalue = CompileExpr::Compile (argument.get (), ctx);
+
+	  // assignments are coercion sites so lets convert the rvalue if
+	  // necessary
+	  auto respective_field = variant->get_field_at_index (i);
+	  auto expected = respective_field->get_field_type ();
+
+	  TyTy::BaseType *actual = nullptr;
+	  bool ok = ctx->get_tyctx ()->lookup_type (
+	    argument->get_mappings ().get_hirid (), &actual);
+	  rust_assert (ok);
+
+	  // coerce it if required
+	  Location lvalue_locus
+	    = ctx->get_mappings ()->lookup_location (expected->get_ty_ref ());
+	  Location rvalue_locus = argument->get_locus ();
+	  rvalue
+	    = coercion_site (argument->get_mappings ().get_hirid (), rvalue,
+			     actual, expected, lvalue_locus, rvalue_locus);
+
+	  // add it to the list
+	  arguments.push_back (rvalue);
+	}
+
+      // the constructor depends on whether this is actually an enum or not if
+      // its an enum we need to setup the discriminator
+      std::vector<tree> ctor_arguments;
+      if (adt->is_enum ())
+	{
+	  HIR::Expr *discrim_expr = variant->get_discriminant ();
+	  tree discrim_expr_node = CompileExpr::Compile (discrim_expr, ctx);
+	  tree folded_discrim_expr = fold_expr (discrim_expr_node);
+	  tree qualifier = folded_discrim_expr;
+
+	  ctor_arguments.push_back (qualifier);
+	}
+      for (auto &arg : arguments)
+	ctor_arguments.push_back (arg);
+
+      translated = ctx->get_backend ()->constructor_expression (
+	compiled_adt_type, adt->is_enum (), ctor_arguments, union_disriminator,
+	expr.get_locus ());
+
+      return;
+    }
+
+  auto get_parameter_tyty_at_index
+    = [] (const TyTy::BaseType *base, size_t index,
+	  TyTy::BaseType **result) -> bool {
+    bool is_fn = base->get_kind () == TyTy::TypeKind::FNDEF
+		 || base->get_kind () == TyTy::TypeKind::FNPTR;
+    rust_assert (is_fn);
+
+    if (base->get_kind () == TyTy::TypeKind::FNPTR)
+      {
+	const TyTy::FnPtr *fn = static_cast<const TyTy::FnPtr *> (base);
+	*result = fn->param_at (index);
+
+	return true;
+      }
+
+    const TyTy::FnType *fn = static_cast<const TyTy::FnType *> (base);
+    auto param = fn->param_at (index);
+    *result = param.second;
+
+    return true;
+  };
+
+  bool is_varadic = false;
+  if (tyty->get_kind () == TyTy::TypeKind::FNDEF)
+    {
+      const TyTy::FnType *fn = static_cast<const TyTy::FnType *> (tyty);
+      is_varadic = fn->is_varadic ();
+    }
+
+  size_t required_num_args;
+  if (tyty->get_kind () == TyTy::TypeKind::FNDEF)
+    {
+      const TyTy::FnType *fn = static_cast<const TyTy::FnType *> (tyty);
+      required_num_args = fn->num_params ();
+    }
+  else
+    {
+      const TyTy::FnPtr *fn = static_cast<const TyTy::FnPtr *> (tyty);
+      required_num_args = fn->num_params ();
+    }
+
+  std::vector<tree> args;
+  for (size_t i = 0; i < expr.get_arguments ().size (); i++)
+    {
+      auto &argument = expr.get_arguments ().at (i);
+      auto rvalue = CompileExpr::Compile (argument.get (), ctx);
+
+      if (is_varadic && i >= required_num_args)
+	{
+	  args.push_back (rvalue);
+	  continue;
+	}
+
+      // assignments are coercion sites so lets convert the rvalue if
+      // necessary
+      bool ok;
+      TyTy::BaseType *expected = nullptr;
+      ok = get_parameter_tyty_at_index (tyty, i, &expected);
+      rust_assert (ok);
+
+      TyTy::BaseType *actual = nullptr;
+      ok = ctx->get_tyctx ()->lookup_type (
+	argument->get_mappings ().get_hirid (), &actual);
+      rust_assert (ok);
+
+      // coerce it if required
+      Location lvalue_locus
+	= ctx->get_mappings ()->lookup_location (expected->get_ty_ref ());
+      Location rvalue_locus = argument->get_locus ();
+      rvalue = coercion_site (argument->get_mappings ().get_hirid (), rvalue,
+			      actual, expected, lvalue_locus, rvalue_locus);
+
+      // add it to the list
+      args.push_back (rvalue);
+    }
+
+  // must be a call to a function
+  auto fn_address = CompileExpr::Compile (expr.get_fnexpr (), ctx);
+  translated = ctx->get_backend ()->call_expression (fn_address, args, nullptr,
+						     expr.get_locus ());
+}
+
+void
+CompileExpr::visit (HIR::MethodCallExpr &expr)
+{
+  // method receiver
+  tree self = CompileExpr::Compile (expr.get_receiver ().get (), ctx);
+
+  // lookup the resolved name
+  NodeId resolved_node_id = UNKNOWN_NODEID;
+  if (!ctx->get_resolver ()->lookup_resolved_name (
+	expr.get_mappings ().get_nodeid (), &resolved_node_id))
+    {
+      rust_error_at (expr.get_locus (), "failed to lookup resolved MethodCall");
+      return;
+    }
+
+  // reverse lookup
+  HirId ref;
+  if (!ctx->get_mappings ()->lookup_node_to_hir (resolved_node_id, &ref))
+    {
+      rust_fatal_error (expr.get_locus (), "reverse lookup failure");
+      return;
+    }
+
+  // lookup the expected function type
+  TyTy::BaseType *lookup_fntype = nullptr;
+  bool ok = ctx->get_tyctx ()->lookup_type (
+    expr.get_method_name ().get_mappings ().get_hirid (), &lookup_fntype);
+  rust_assert (ok);
+  rust_assert (lookup_fntype->get_kind () == TyTy::TypeKind::FNDEF);
+  TyTy::FnType *fntype = static_cast<TyTy::FnType *> (lookup_fntype);
+
+  TyTy::BaseType *receiver = nullptr;
+  ok = ctx->get_tyctx ()->lookup_receiver (expr.get_mappings ().get_hirid (),
+					   &receiver);
+  rust_assert (ok);
+
+  bool is_dyn_dispatch
+    = receiver->get_root ()->get_kind () == TyTy::TypeKind::DYNAMIC;
+  bool is_generic_receiver = receiver->get_kind () == TyTy::TypeKind::PARAM;
+  if (is_generic_receiver)
+    {
+      TyTy::ParamType *p = static_cast<TyTy::ParamType *> (receiver);
+      receiver = p->resolve ();
+    }
+
+  tree fn_expr = error_mark_node;
+  if (is_dyn_dispatch)
+    {
+      const TyTy::DynamicObjectType *dyn
+	= static_cast<const TyTy::DynamicObjectType *> (receiver->get_root ());
+
+      std::vector<HIR::Expr *> arguments;
+      for (auto &arg : expr.get_arguments ())
+	arguments.push_back (arg.get ());
+
+      fn_expr
+	= get_fn_addr_from_dyn (dyn, receiver, fntype, self, expr.get_locus ());
+      self = get_receiver_from_dyn (dyn, receiver, fntype, self,
+				    expr.get_locus ());
+    }
+  else
+    {
+      // lookup compiled functions since it may have already been compiled
+      HIR::PathExprSegment method_name = expr.get_method_name ();
+      HIR::PathIdentSegment segment_name = method_name.get_segment ();
+      fn_expr
+	= resolve_method_address (fntype, ref, receiver, segment_name,
+				  expr.get_mappings (), expr.get_locus ());
+    }
+
+  // lookup the autoderef mappings
+  HirId autoderef_mappings_id
+    = expr.get_receiver ()->get_mappings ().get_hirid ();
+  std::vector<Resolver::Adjustment> *adjustments = nullptr;
+  ok = ctx->get_tyctx ()->lookup_autoderef_mappings (autoderef_mappings_id,
+						     &adjustments);
+  rust_assert (ok);
+
+  // apply adjustments for the fn call
+  self = resolve_adjustements (*adjustments, self,
+			       expr.get_receiver ()->get_locus ());
+
+  std::vector<tree> args;
+  args.push_back (self); // adjusted self
+
+  // normal args
+  for (size_t i = 0; i < expr.get_arguments ().size (); i++)
+    {
+      auto &argument = expr.get_arguments ().at (i);
+      auto rvalue = CompileExpr::Compile (argument.get (), ctx);
+
+      // assignments are coercion sites so lets convert the rvalue if
+      // necessary, offset from the already adjusted implicit self
+      bool ok;
+      TyTy::BaseType *expected = fntype->param_at (i + 1).second;
+
+      TyTy::BaseType *actual = nullptr;
+      ok = ctx->get_tyctx ()->lookup_type (
+	argument->get_mappings ().get_hirid (), &actual);
+      rust_assert (ok);
+
+      // coerce it if required
+      Location lvalue_locus
+	= ctx->get_mappings ()->lookup_location (expected->get_ty_ref ());
+      Location rvalue_locus = argument->get_locus ();
+      rvalue = coercion_site (argument->get_mappings ().get_hirid (), rvalue,
+			      actual, expected, lvalue_locus, rvalue_locus);
+
+      // add it to the list
+      args.push_back (rvalue);
+    }
+
+  translated = ctx->get_backend ()->call_expression (fn_expr, args, nullptr,
+						     expr.get_locus ());
+}
+
+tree
+CompileExpr::get_fn_addr_from_dyn (const TyTy::DynamicObjectType *dyn,
+				   TyTy::BaseType *receiver,
+				   TyTy::FnType *fntype, tree receiver_ref,
+				   Location expr_locus)
+{
+  size_t offs = 0;
+  const Resolver::TraitItemReference *ref = nullptr;
+  for (auto &bound : dyn->get_object_items ())
+    {
+      const Resolver::TraitItemReference *item = bound.first;
+      auto t = item->get_tyty ();
+      rust_assert (t->get_kind () == TyTy::TypeKind::FNDEF);
+      auto ft = static_cast<TyTy::FnType *> (t);
+
+      if (ft->get_id () == fntype->get_id ())
+	{
+	  ref = item;
+	  break;
+	}
+      offs++;
+    }
+
+  if (ref == nullptr)
+    return error_mark_node;
+
+  // get any indirection sorted out
+  if (receiver->get_kind () == TyTy::TypeKind::REF)
+    {
+      tree indirect = indirect_expression (receiver_ref, expr_locus);
+      receiver_ref = indirect;
+    }
+
+  // cast it to the correct fntype
+  tree expected_fntype = TyTyResolveCompile::compile (ctx, fntype, true);
+  tree idx = build_int_cst (size_type_node, offs);
+
+  tree vtable_ptr
+    = ctx->get_backend ()->struct_field_expression (receiver_ref, 1,
+						    expr_locus);
+  tree vtable_array_access = build4_loc (expr_locus.gcc_location (), ARRAY_REF,
+					 TREE_TYPE (TREE_TYPE (vtable_ptr)),
+					 vtable_ptr, idx, NULL_TREE, NULL_TREE);
+
+  tree vcall
+    = build3_loc (expr_locus.gcc_location (), OBJ_TYPE_REF, expected_fntype,
+		  vtable_array_access, receiver_ref, idx);
+
+  return vcall;
+}
+
+tree
+CompileExpr::get_receiver_from_dyn (const TyTy::DynamicObjectType *dyn,
+				    TyTy::BaseType *receiver,
+				    TyTy::FnType *fntype, tree receiver_ref,
+				    Location expr_locus)
+{
+  // get any indirection sorted out
+  if (receiver->get_kind () == TyTy::TypeKind::REF)
+    {
+      tree indirect = indirect_expression (receiver_ref, expr_locus);
+      receiver_ref = indirect;
+    }
+
+  // access the offs + 1 for the fnptr and offs=0 for the reciever obj
+  return ctx->get_backend ()->struct_field_expression (receiver_ref, 0,
+						       expr_locus);
+}
+
+tree
+CompileExpr::resolve_method_address (TyTy::FnType *fntype, HirId ref,
+				     TyTy::BaseType *receiver,
+				     HIR::PathIdentSegment &segment,
+				     Analysis::NodeMapping expr_mappings,
+				     Location expr_locus)
+{
+  // lookup compiled functions since it may have already been compiled
+  tree fn = NULL_TREE;
+  if (ctx->lookup_function_decl (fntype->get_ty_ref (), &fn))
+    {
+      return address_expression (fn, expr_locus);
+    }
+
+  // Now we can try and resolve the address since this might be a forward
+  // declared function, generic function which has not be compiled yet or
+  // its an not yet trait bound function
+  HIR::ImplItem *resolved_item
+    = ctx->get_mappings ()->lookup_hir_implitem (ref, nullptr);
+  if (resolved_item != nullptr)
+    {
+      if (!fntype->has_subsititions_defined ())
+	return CompileInherentImplItem::Compile (resolved_item, ctx);
+
+      return CompileInherentImplItem::Compile (resolved_item, ctx, fntype);
+    }
+
+  // it might be resolved to a trait item
+  HIR::TraitItem *trait_item
+    = ctx->get_mappings ()->lookup_hir_trait_item (ref);
+  HIR::Trait *trait = ctx->get_mappings ()->lookup_trait_item_mapping (
+    trait_item->get_mappings ().get_hirid ());
+
+  Resolver::TraitReference *trait_ref
+    = &Resolver::TraitReference::error_node ();
+  bool ok = ctx->get_tyctx ()->lookup_trait_reference (
+    trait->get_mappings ().get_defid (), &trait_ref);
+  rust_assert (ok);
+
+  // the type resolver can only resolve type bounds to their trait
+  // item so its up to us to figure out if this path should resolve
+  // to an trait-impl-block-item or if it can be defaulted to the
+  // trait-impl-item's definition
+
+  auto root = receiver->get_root ();
+  std::vector<Resolver::PathProbeCandidate> candidates
+    = Resolver::PathProbeType::Probe (root, segment, true /* probe_impls */,
+				      false /* probe_bounds */,
+				      true /* ignore_mandatory_trait_items */);
+  if (candidates.size () == 0)
+    {
+      // this means we are defaulting back to the trait_item if
+      // possible
+      Resolver::TraitItemReference *trait_item_ref = nullptr;
+      bool ok = trait_ref->lookup_hir_trait_item (*trait_item, &trait_item_ref);
+      rust_assert (ok);				    // found
+      rust_assert (trait_item_ref->is_optional ()); // has definition
+
+      // FIXME Optional means it has a definition and an associated
+      // block which can be a default implementation, if it does not
+      // contain an implementation we should actually return
+      // error_mark_node
+
+      return CompileTraitItem::Compile (trait_item_ref->get_hir_trait_item (),
+					ctx, fntype, true, expr_locus);
+    }
+  else
+    {
+      // FIXME this will be a case to return error_mark_node, there is
+      // an error scenario where a Trait Foo has a method Bar, but this
+      // receiver does not implement this trait or has an incompatible
+      // implementation and we should just return error_mark_node
+
+      rust_assert (candidates.size () == 1);
+      auto &candidate = candidates.at (0);
+      rust_assert (candidate.is_impl_candidate ());
+      rust_assert (candidate.ty->get_kind () == TyTy::TypeKind::FNDEF);
+      TyTy::FnType *candidate_call = static_cast<TyTy::FnType *> (candidate.ty);
+
+      HIR::ImplItem *impl_item = candidate.item.impl.impl_item;
+      if (!candidate_call->has_subsititions_defined ())
+	return CompileInherentImplItem::Compile (impl_item, ctx);
+
+      TyTy::BaseType *monomorphized = candidate_call;
+      if (candidate_call->needs_generic_substitutions ())
+	{
+	  TyTy::BaseType *infer_impl_call
+	    = candidate_call->infer_substitions (expr_locus);
+	  monomorphized = infer_impl_call->unify (fntype);
+	}
+
+      return CompileInherentImplItem::Compile (impl_item, ctx, monomorphized);
+    }
+}
+
+tree
+CompileExpr::resolve_operator_overload (
+  Analysis::RustLangItem::ItemType lang_item_type, HIR::OperatorExprMeta expr,
+  tree lhs, tree rhs, HIR::Expr *lhs_expr, HIR::Expr *rhs_expr)
+{
+  TyTy::FnType *fntype;
+  bool is_op_overload = ctx->get_tyctx ()->lookup_operator_overload (
+    expr.get_mappings ().get_hirid (), &fntype);
+  rust_assert (is_op_overload);
+
+  // lookup the resolved name
+  NodeId resolved_node_id = UNKNOWN_NODEID;
+  bool ok = ctx->get_resolver ()->lookup_resolved_name (
+    expr.get_mappings ().get_nodeid (), &resolved_node_id);
+  rust_assert (ok);
+
+  // reverse lookup
+  HirId ref;
+  ok = ctx->get_mappings ()->lookup_node_to_hir (resolved_node_id, &ref);
+  rust_assert (ok);
+
+  TyTy::BaseType *receiver = nullptr;
+  ok = ctx->get_tyctx ()->lookup_receiver (expr.get_mappings ().get_hirid (),
+					   &receiver);
+  rust_assert (ok);
+
+  bool is_generic_receiver = receiver->get_kind () == TyTy::TypeKind::PARAM;
+  if (is_generic_receiver)
+    {
+      TyTy::ParamType *p = static_cast<TyTy::ParamType *> (receiver);
+      receiver = p->resolve ();
+    }
+
+  // lookup compiled functions since it may have already been compiled
+  HIR::PathIdentSegment segment_name (
+    Analysis::RustLangItem::ToString (lang_item_type));
+  tree fn_expr
+    = resolve_method_address (fntype, ref, receiver, segment_name,
+			      expr.get_mappings (), expr.get_locus ());
+
+  // lookup the autoderef mappings
+  std::vector<Resolver::Adjustment> *adjustments = nullptr;
+  ok = ctx->get_tyctx ()->lookup_autoderef_mappings (
+    expr.get_lvalue_mappings ().get_hirid (), &adjustments);
+  rust_assert (ok);
+
+  // apply adjustments for the fn call
+  tree self = resolve_adjustements (*adjustments, lhs, lhs_expr->get_locus ());
+
+  std::vector<tree> args;
+  args.push_back (self); // adjusted self
+  if (rhs != nullptr)	 // can be null for negation_expr (unary ones)
+    args.push_back (rhs);
+
+  return ctx->get_backend ()->call_expression (fn_expr, args, nullptr,
+					       expr.get_locus ());
+}
+
+tree
+CompileExpr::compile_bool_literal (const HIR::LiteralExpr &expr,
+				   const TyTy::BaseType *tyty)
+{
+  rust_assert (expr.get_lit_type () == HIR::Literal::BOOL);
+
+  const auto literal_value = expr.get_literal ();
+  bool bval = literal_value.as_string ().compare ("true") == 0;
+  return ctx->get_backend ()->boolean_constant_expression (bval);
+}
+
+tree
+CompileExpr::compile_integer_literal (const HIR::LiteralExpr &expr,
+				      const TyTy::BaseType *tyty)
+{
+  rust_assert (expr.get_lit_type () == HIR::Literal::INT);
+  const auto literal_value = expr.get_literal ();
+
+  tree type = TyTyResolveCompile::compile (ctx, tyty);
+
+  mpz_t ival;
+  if (mpz_init_set_str (ival, literal_value.as_string ().c_str (), 10) != 0)
+    {
+      rust_error_at (expr.get_locus (), "bad number in literal");
+      return error_mark_node;
+    }
+
+  mpz_t type_min;
+  mpz_t type_max;
+  mpz_init (type_min);
+  mpz_init (type_max);
+  get_type_static_bounds (type, type_min, type_max);
+
+  if (mpz_cmp (ival, type_min) < 0 || mpz_cmp (ival, type_max) > 0)
+    {
+      rust_error_at (expr.get_locus (),
+		     "integer overflows the respective type %<%s%>",
+		     tyty->get_name ().c_str ());
+      return error_mark_node;
+    }
+  return double_int_to_tree (type, mpz_get_double_int (type, ival, true));
+}
+
+tree
+CompileExpr::compile_float_literal (const HIR::LiteralExpr &expr,
+				    const TyTy::BaseType *tyty)
+{
+  rust_assert (expr.get_lit_type () == HIR::Literal::FLOAT);
+  const auto literal_value = expr.get_literal ();
+
+  mpfr_t fval;
+  if (mpfr_init_set_str (fval, literal_value.as_string ().c_str (), 10,
+			 MPFR_RNDN)
+      != 0)
+    {
+      rust_error_at (expr.get_locus (), "bad number in literal");
+      return error_mark_node;
+    }
+
+  tree type = TyTyResolveCompile::compile (ctx, tyty);
+
+  // taken from:
+  // see go/gofrontend/expressions.cc:check_float_type
+  mpfr_exp_t exp = mpfr_get_exp (fval);
+  bool real_value_overflow = exp > TYPE_PRECISION (type);
+
+  REAL_VALUE_TYPE r1;
+  real_from_mpfr (&r1, fval, type, GMP_RNDN);
+  REAL_VALUE_TYPE r2;
+  real_convert (&r2, TYPE_MODE (type), &r1);
+
+  tree real_value = build_real (type, r2);
+  if (TREE_OVERFLOW (real_value) || real_value_overflow)
+    {
+      rust_error_at (expr.get_locus (),
+		     "decimal overflows the respective type %<%s%>",
+		     tyty->get_name ().c_str ());
+      return error_mark_node;
+    }
+
+  return real_value;
+}
+
+tree
+CompileExpr::compile_char_literal (const HIR::LiteralExpr &expr,
+				   const TyTy::BaseType *tyty)
+{
+  rust_assert (expr.get_lit_type () == HIR::Literal::CHAR);
+  const auto literal_value = expr.get_literal ();
+
+  // FIXME needs wchar_t
+  char c = literal_value.as_string ().c_str ()[0];
+  return ctx->get_backend ()->wchar_constant_expression (c);
+}
+
+tree
+CompileExpr::compile_byte_literal (const HIR::LiteralExpr &expr,
+				   const TyTy::BaseType *tyty)
+{
+  rust_assert (expr.get_lit_type () == HIR::Literal::BYTE);
+  const auto literal_value = expr.get_literal ();
+
+  tree type = TyTyResolveCompile::compile (ctx, tyty);
+  char c = literal_value.as_string ().c_str ()[0];
+  return build_int_cst (type, c);
+}
+
+tree
+CompileExpr::compile_string_literal (const HIR::LiteralExpr &expr,
+				     const TyTy::BaseType *tyty)
+{
+  tree fat_pointer = TyTyResolveCompile::compile (ctx, tyty);
+
+  rust_assert (expr.get_lit_type () == HIR::Literal::STRING);
+  const auto literal_value = expr.get_literal ();
+
+  auto base = ctx->get_backend ()->string_constant_expression (
+    literal_value.as_string ());
+  tree data = address_expression (base, expr.get_locus ());
+
+  TyTy::BaseType *usize = nullptr;
+  bool ok = ctx->get_tyctx ()->lookup_builtin ("usize", &usize);
+  rust_assert (ok);
+  tree type = TyTyResolveCompile::compile (ctx, usize);
+
+  mpz_t ival;
+  mpz_init_set_ui (ival, literal_value.as_string ().size ());
+  tree size = double_int_to_tree (type, mpz_get_double_int (type, ival, true));
+
+  return ctx->get_backend ()->constructor_expression (fat_pointer, false,
+						      {data, size}, -1,
+						      expr.get_locus ());
+}
+
+tree
+CompileExpr::compile_byte_string_literal (const HIR::LiteralExpr &expr,
+					  const TyTy::BaseType *tyty)
+{
+  rust_assert (expr.get_lit_type () == HIR::Literal::BYTE_STRING);
+
+  // the type here is &[ty; capacity]
+  rust_assert (tyty->get_kind () == TyTy::TypeKind::REF);
+  const auto ref_tyty = static_cast<const TyTy::ReferenceType *> (tyty);
+  auto base_tyty = ref_tyty->get_base ();
+  rust_assert (base_tyty->get_kind () == TyTy::TypeKind::ARRAY);
+  auto array_tyty = static_cast<TyTy::ArrayType *> (base_tyty);
+
+  std::string value_str = expr.get_literal ().as_string ();
+  std::vector<tree> vals;
+  std::vector<unsigned long> indexes;
+  for (size_t i = 0; i < value_str.size (); i++)
+    {
+      char b = value_str.at (i);
+      tree bb = ctx->get_backend ()->char_constant_expression (b);
+      vals.push_back (bb);
+      indexes.push_back (i);
+    }
+
+  tree array_type = TyTyResolveCompile::compile (ctx, array_tyty);
+  tree constructed
+    = ctx->get_backend ()->array_constructor_expression (array_type, indexes,
+							 vals,
+							 expr.get_locus ());
+
+  return address_expression (constructed, expr.get_locus ());
+}
+
+tree
+CompileExpr::type_cast_expression (tree type_to_cast_to, tree expr_tree,
+				   Location location)
+{
+  if (type_to_cast_to == error_mark_node || expr_tree == error_mark_node
+      || TREE_TYPE (expr_tree) == error_mark_node)
+    return error_mark_node;
+
+  if (ctx->get_backend ()->type_size (type_to_cast_to) == 0
+      || TREE_TYPE (expr_tree) == void_type_node)
+    {
+      // Do not convert zero-sized types.
+      return expr_tree;
+    }
+  else if (TREE_CODE (type_to_cast_to) == INTEGER_TYPE)
+    {
+      tree cast = fold (convert_to_integer (type_to_cast_to, expr_tree));
+      // FIXME check for TREE_OVERFLOW?
+      return cast;
+    }
+  else if (TREE_CODE (type_to_cast_to) == REAL_TYPE)
+    {
+      tree cast = fold (convert_to_real (type_to_cast_to, expr_tree));
+      // FIXME
+      // We might need to check that the tree is MAX val and thusly saturate it
+      // to inf. we can get the bounds and check the value if its >= or <= to
+      // the min and max bounds
+      //
+      // https://github.com/Rust-GCC/gccrs/issues/635
+      return cast;
+    }
+  else if (TREE_CODE (type_to_cast_to) == COMPLEX_TYPE)
+    {
+      return fold (convert_to_complex (type_to_cast_to, expr_tree));
+    }
+  else if (TREE_CODE (type_to_cast_to) == POINTER_TYPE
+	   && TREE_CODE (TREE_TYPE (expr_tree)) == INTEGER_TYPE)
+    {
+      return fold (convert_to_pointer (type_to_cast_to, expr_tree));
+    }
+  else if (TREE_CODE (type_to_cast_to) == RECORD_TYPE
+	   || TREE_CODE (type_to_cast_to) == ARRAY_TYPE)
+    {
+      return fold_build1_loc (location.gcc_location (), VIEW_CONVERT_EXPR,
+			      type_to_cast_to, expr_tree);
+    }
+  else if (TREE_CODE (type_to_cast_to) == POINTER_TYPE
+	   && SLICE_TYPE_P (TREE_TYPE (expr_tree)))
+    {
+      // returning a raw cast using NOP_EXPR seems to resut in an ICE:
+      //
+      // Analyzing compilation unit
+      // Performing interprocedural optimizations
+      //  <*free_lang_data> {heap 2644k} <visibility> {heap 2644k}
+      //  <build_ssa_passes> {heap 2644k} <opt_local_passes> {heap 2644k}during
+      //  GIMPLE pass: cddce
+      // In function ‘*T::as_ptr<i32>’:
+      // rust1: internal compiler error: in propagate_necessity, at
+      // tree-ssa-dce.cc:984 0x1d5b43e propagate_necessity
+      //         ../../gccrs/gcc/tree-ssa-dce.cc:984
+      // 0x1d5e180 perform_tree_ssa_dce
+      //         ../../gccrs/gcc/tree-ssa-dce.cc:1876
+      // 0x1d5e2c8 tree_ssa_cd_dce
+      //         ../../gccrs/gcc/tree-ssa-dce.cc:1920
+      // 0x1d5e49a execute
+      //         ../../gccrs/gcc/tree-ssa-dce.cc:1992
+
+      // this is returning the direct raw pointer of the slice an assumes a very
+      // specific layout
+      return ctx->get_backend ()->struct_field_expression (expr_tree, 0,
+							   location);
+    }
+
+  return fold_convert_loc (location.gcc_location (), type_to_cast_to,
+			   expr_tree);
+}
+
+void
+CompileExpr::visit (HIR::ArrayExpr &expr)
+{
+  TyTy::BaseType *tyty = nullptr;
+  if (!ctx->get_tyctx ()->lookup_type (expr.get_mappings ().get_hirid (),
+				       &tyty))
+    {
+      rust_fatal_error (expr.get_locus (),
+			"did not resolve type for this array expr");
+      return;
+    }
+
+  tree array_type = TyTyResolveCompile::compile (ctx, tyty);
+  if (TREE_CODE (array_type) != ARRAY_TYPE)
+    {
+      translated = error_mark_node;
+      return;
+    }
+
+  rust_assert (tyty->get_kind () == TyTy::TypeKind::ARRAY);
+  const TyTy::ArrayType &array_tyty
+    = static_cast<const TyTy::ArrayType &> (*tyty);
+
+  HIR::ArrayElems &elements = *expr.get_internal_elements ();
+  switch (elements.get_array_expr_type ())
+    {
+      case HIR::ArrayElems::ArrayExprType::VALUES: {
+	HIR::ArrayElemsValues &elems
+	  = static_cast<HIR::ArrayElemsValues &> (elements);
+	translated
+	  = array_value_expr (expr.get_locus (), array_tyty, array_type, elems);
+      }
+      return;
+
+    case HIR::ArrayElems::ArrayExprType::COPIED:
+      HIR::ArrayElemsCopied &elems
+	= static_cast<HIR::ArrayElemsCopied &> (elements);
+      translated
+	= array_copied_expr (expr.get_locus (), array_tyty, array_type, elems);
+    }
+}
+
+tree
+CompileExpr::array_value_expr (Location expr_locus,
+			       const TyTy::ArrayType &array_tyty,
+			       tree array_type, HIR::ArrayElemsValues &elems)
+{
+  std::vector<unsigned long> indexes;
+  std::vector<tree> constructor;
+  size_t i = 0;
+  for (auto &elem : elems.get_values ())
+    {
+      tree translated_expr = CompileExpr::Compile (elem.get (), ctx);
+      constructor.push_back (translated_expr);
+      indexes.push_back (i++);
+    }
+
+  return ctx->get_backend ()->array_constructor_expression (array_type, indexes,
+							    constructor,
+							    expr_locus);
+}
+
+tree
+CompileExpr::array_copied_expr (Location expr_locus,
+				const TyTy::ArrayType &array_tyty,
+				tree array_type, HIR::ArrayElemsCopied &elems)
+{
+  //  see gcc/cp/typeck2.cc:1369-1401
+  gcc_assert (TREE_CODE (array_type) == ARRAY_TYPE);
+  tree domain = TYPE_DOMAIN (array_type);
+  if (!domain)
+    return error_mark_node;
+
+  if (!TREE_CONSTANT (TYPE_MAX_VALUE (domain)))
+    {
+      rust_error_at (expr_locus, "non const capacity domain %qT", array_type);
+      return error_mark_node;
+    }
+
+  tree capacity_expr = CompileExpr::Compile (elems.get_num_copies_expr (), ctx);
+  if (!TREE_CONSTANT (capacity_expr))
+    {
+      rust_error_at (expr_locus, "non const num copies %qT", array_type);
+      return error_mark_node;
+    }
+
+  // get the compiled value
+  tree translated_expr = CompileExpr::Compile (elems.get_elem_to_copy (), ctx);
+
+  tree max_domain = TYPE_MAX_VALUE (domain);
+  tree min_domain = TYPE_MIN_VALUE (domain);
+
+  auto max = wi::to_offset (max_domain);
+  auto min = wi::to_offset (min_domain);
+  auto precision = TYPE_PRECISION (TREE_TYPE (domain));
+  auto sign = TYPE_SIGN (TREE_TYPE (domain));
+  unsigned HOST_WIDE_INT len
+    = wi::ext (max - min + 1, precision, sign).to_uhwi ();
+
+  // In a const context we must initialize the entire array, which entails
+  // allocating for each element. If the user wants a huge array, we will OOM
+  // and die horribly.
+  if (ctx->const_context_p ())
+    {
+      size_t idx = 0;
+      std::vector<unsigned long> indexes;
+      std::vector<tree> constructor;
+      for (unsigned HOST_WIDE_INT i = 0; i < len; i++)
+	{
+	  constructor.push_back (translated_expr);
+	  indexes.push_back (idx++);
+	}
+
+      return ctx->get_backend ()->array_constructor_expression (array_type,
+								indexes,
+								constructor,
+								expr_locus);
+    }
+
+  else
+    {
+      // Create a new block scope in which to initialize the array
+      tree fndecl = NULL_TREE;
+      if (ctx->in_fn ())
+	fndecl = ctx->peek_fn ().fndecl;
+
+      std::vector<Bvariable *> locals;
+      tree enclosing_scope = ctx->peek_enclosing_scope ();
+      tree init_block
+	= ctx->get_backend ()->block (fndecl, enclosing_scope, locals,
+				      expr_locus, expr_locus);
+      ctx->push_block (init_block);
+
+      tree tmp;
+      tree stmts
+	= ctx->get_backend ()->array_initializer (fndecl, init_block,
+						  array_type, capacity_expr,
+						  translated_expr, &tmp,
+						  expr_locus);
+      ctx->add_statement (stmts);
+
+      tree block = ctx->pop_block ();
+
+      // The result is a compound expression which creates a temporary array,
+      // initializes all the elements in a loop, and then yeilds the array.
+      return ctx->get_backend ()->compound_expression (block, tmp, expr_locus);
+    }
+}
+
+tree
+HIRCompileBase::resolve_adjustements (
+  std::vector<Resolver::Adjustment> &adjustments, tree expression,
+  Location locus)
+{
+  tree e = expression;
+  for (auto &adjustment : adjustments)
+    {
+      switch (adjustment.get_type ())
+	{
+	case Resolver::Adjustment::AdjustmentType::ERROR:
+	  return error_mark_node;
+
+	case Resolver::Adjustment::AdjustmentType::IMM_REF:
+	  case Resolver::Adjustment::AdjustmentType::MUT_REF: {
+	    if (!SLICE_TYPE_P (TREE_TYPE (e)))
+	      {
+		e = address_expression (e, locus);
+	      }
+	  }
+	  break;
+
+	case Resolver::Adjustment::AdjustmentType::DEREF:
+	case Resolver::Adjustment::AdjustmentType::DEREF_MUT:
+	  e = resolve_deref_adjustment (adjustment, e, locus);
+	  break;
+
+	case Resolver::Adjustment::AdjustmentType::INDIRECTION:
+	  e = resolve_indirection_adjustment (adjustment, e, locus);
+	  break;
+
+	case Resolver::Adjustment::AdjustmentType::UNSIZE:
+	  e = resolve_unsized_adjustment (adjustment, e, locus);
+	  break;
+	}
+    }
+
+  return e;
+}
+
+tree
+HIRCompileBase::resolve_deref_adjustment (Resolver::Adjustment &adjustment,
+					  tree expression, Location locus)
+{
+  rust_assert (adjustment.is_deref_adjustment ()
+	       || adjustment.is_deref_mut_adjustment ());
+  rust_assert (adjustment.has_operator_overload ());
+
+  TyTy::FnType *lookup = adjustment.get_deref_operator_fn ();
+  HIR::ImplItem *resolved_item = adjustment.get_deref_hir_item ();
+
+  tree fn_address = error_mark_node;
+  if (!lookup->has_subsititions_defined ())
+    fn_address = CompileInherentImplItem::Compile (resolved_item, ctx, nullptr,
+						   true, locus);
+  else
+    fn_address = CompileInherentImplItem::Compile (resolved_item, ctx, lookup,
+						   true, locus);
+
+  // does it need a reference to call
+  tree adjusted_argument = expression;
+  bool needs_borrow = adjustment.get_deref_adjustment_type ()
+		      != Resolver::Adjustment::AdjustmentType::ERROR;
+  if (needs_borrow)
+    {
+      adjusted_argument = address_expression (expression, locus);
+    }
+
+  // make the call
+  return ctx->get_backend ()->call_expression (fn_address, {adjusted_argument},
+					       nullptr, locus);
+}
+
+tree
+HIRCompileBase::resolve_indirection_adjustment (
+  Resolver::Adjustment &adjustment, tree expression, Location locus)
+{
+  return indirect_expression (expression, locus);
+}
+
+tree
+HIRCompileBase::resolve_unsized_adjustment (Resolver::Adjustment &adjustment,
+					    tree expression, Location locus)
+{
+  bool expect_slice
+    = adjustment.get_expected ()->get_kind () == TyTy::TypeKind::SLICE;
+  bool expect_dyn
+    = adjustment.get_expected ()->get_kind () == TyTy::TypeKind::DYNAMIC;
+
+  // assumes this is an array
+  tree expr_type = TREE_TYPE (expression);
+  if (expect_slice)
+    {
+      rust_assert (TREE_CODE (expr_type) == ARRAY_TYPE);
+      return resolve_unsized_slice_adjustment (adjustment, expression, locus);
+    }
+
+  rust_assert (expect_dyn);
+  return resolve_unsized_dyn_adjustment (adjustment, expression, locus);
+}
+
+tree
+HIRCompileBase::resolve_unsized_slice_adjustment (
+  Resolver::Adjustment &adjustment, tree expression, Location locus)
+{
+  // assumes this is an array
+  tree expr_type = TREE_TYPE (expression);
+  rust_assert (TREE_CODE (expr_type) == ARRAY_TYPE);
+
+  // takes an array and returns a fat-pointer so this becomes a constructor
+  // expression
+  rust_assert (adjustment.get_expected ()->get_kind ()
+	       == TyTy::TypeKind::SLICE);
+  tree fat_pointer
+    = TyTyResolveCompile::compile (ctx, adjustment.get_expected ());
+
+  // make a constructor for this
+  tree data = address_expression (expression, locus);
+
+  // fetch the size from the domain
+  tree domain = TYPE_DOMAIN (expr_type);
+  unsigned HOST_WIDE_INT array_size
+    = wi::ext (wi::to_offset (TYPE_MAX_VALUE (domain))
+		 - wi::to_offset (TYPE_MIN_VALUE (domain)) + 1,
+	       TYPE_PRECISION (TREE_TYPE (domain)),
+	       TYPE_SIGN (TREE_TYPE (domain)))
+	.to_uhwi ();
+  tree size = build_int_cst (size_type_node, array_size);
+
+  return ctx->get_backend ()->constructor_expression (fat_pointer, false,
+						      {data, size}, -1, locus);
+}
+
+tree
+HIRCompileBase::resolve_unsized_dyn_adjustment (
+  Resolver::Adjustment &adjustment, tree expression, Location locus)
+{
+  tree rvalue = expression;
+  Location rvalue_locus = locus;
+
+  const TyTy::BaseType *actual = adjustment.get_actual ();
+  const TyTy::BaseType *expected = adjustment.get_expected ();
+
+  const TyTy::DynamicObjectType *dyn
+    = static_cast<const TyTy::DynamicObjectType *> (expected);
+
+  rust_debug ("resolve_unsized_dyn_adjustment actual={%s} dyn={%s}",
+	      actual->debug_str ().c_str (), dyn->debug_str ().c_str ());
+
+  return coerce_to_dyn_object (rvalue, actual, dyn, rvalue_locus);
+}
+
+void
+CompileExpr::visit (HIR::RangeFromToExpr &expr)
+{
+  tree from = CompileExpr::Compile (expr.get_from_expr ().get (), ctx);
+  tree to = CompileExpr::Compile (expr.get_to_expr ().get (), ctx);
+  if (from == error_mark_node || to == error_mark_node)
+    {
+      translated = error_mark_node;
+      return;
+    }
+
+  TyTy::BaseType *tyty = nullptr;
+  bool ok
+    = ctx->get_tyctx ()->lookup_type (expr.get_mappings ().get_hirid (), &tyty);
+  rust_assert (ok);
+
+  tree adt = TyTyResolveCompile::compile (ctx, tyty);
+
+  // make the constructor
+  translated
+    = ctx->get_backend ()->constructor_expression (adt, false, {from, to}, -1,
+						   expr.get_locus ());
+}
+
+void
+CompileExpr::visit (HIR::RangeFromExpr &expr)
+{
+  tree from = CompileExpr::Compile (expr.get_from_expr ().get (), ctx);
+  if (from == error_mark_node)
+    {
+      translated = error_mark_node;
+      return;
+    }
+
+  TyTy::BaseType *tyty = nullptr;
+  bool ok
+    = ctx->get_tyctx ()->lookup_type (expr.get_mappings ().get_hirid (), &tyty);
+  rust_assert (ok);
+
+  tree adt = TyTyResolveCompile::compile (ctx, tyty);
+
+  // make the constructor
+  translated
+    = ctx->get_backend ()->constructor_expression (adt, false, {from}, -1,
+						   expr.get_locus ());
+}
+
+void
+CompileExpr::visit (HIR::RangeToExpr &expr)
+{
+  tree to = CompileExpr::Compile (expr.get_to_expr ().get (), ctx);
+  if (to == error_mark_node)
+    {
+      translated = error_mark_node;
+      return;
+    }
+
+  TyTy::BaseType *tyty = nullptr;
+  bool ok
+    = ctx->get_tyctx ()->lookup_type (expr.get_mappings ().get_hirid (), &tyty);
+  rust_assert (ok);
+
+  tree adt = TyTyResolveCompile::compile (ctx, tyty);
+
+  // make the constructor
+  translated
+    = ctx->get_backend ()->constructor_expression (adt, false, {to}, -1,
+						   expr.get_locus ());
+}
+
+void
+CompileExpr::visit (HIR::RangeFullExpr &expr)
+{
+  TyTy::BaseType *tyty = nullptr;
+  bool ok
+    = ctx->get_tyctx ()->lookup_type (expr.get_mappings ().get_hirid (), &tyty);
+  rust_assert (ok);
+
+  tree adt = TyTyResolveCompile::compile (ctx, tyty);
+  translated = ctx->get_backend ()->constructor_expression (adt, false, {}, -1,
+							    expr.get_locus ());
+}
+
+void
+CompileExpr::visit (HIR::RangeFromToInclExpr &expr)
+{
+  tree from = CompileExpr::Compile (expr.get_from_expr ().get (), ctx);
+  tree to = CompileExpr::Compile (expr.get_to_expr ().get (), ctx);
+  if (from == error_mark_node || to == error_mark_node)
+    {
+      translated = error_mark_node;
+      return;
+    }
+
+  TyTy::BaseType *tyty = nullptr;
+  bool ok
+    = ctx->get_tyctx ()->lookup_type (expr.get_mappings ().get_hirid (), &tyty);
+  rust_assert (ok);
+
+  tree adt = TyTyResolveCompile::compile (ctx, tyty);
+
+  // make the constructor
+  translated
+    = ctx->get_backend ()->constructor_expression (adt, false, {from, to}, -1,
+						   expr.get_locus ());
+}
+
+void
+CompileExpr::visit (HIR::ArrayIndexExpr &expr)
+{
+  tree array_reference = CompileExpr::Compile (expr.get_array_expr (), ctx);
+  tree index = CompileExpr::Compile (expr.get_index_expr (), ctx);
+
+  // this might be an core::ops::index lang item situation
+  TyTy::FnType *fntype;
+  bool is_op_overload = ctx->get_tyctx ()->lookup_operator_overload (
+    expr.get_mappings ().get_hirid (), &fntype);
+  if (is_op_overload)
+    {
+      auto lang_item_type = Analysis::RustLangItem::ItemType::INDEX;
+      tree operator_overload_call
+	= resolve_operator_overload (lang_item_type, expr, array_reference,
+				     index, expr.get_array_expr (),
+				     expr.get_index_expr ());
+
+      tree actual_type = TREE_TYPE (operator_overload_call);
+      bool can_indirect = TYPE_PTR_P (actual_type) || TYPE_REF_P (actual_type);
+      if (!can_indirect)
+	{
+	  // nothing to do
+	  translated = operator_overload_call;
+	  return;
+	}
+
+      // rust deref always returns a reference from this overload then we can
+      // actually do the indirection
+      translated
+	= indirect_expression (operator_overload_call, expr.get_locus ());
+      return;
+    }
+
+  // lets check if the array is a reference type then we can add an
+  // indirection if required
+  TyTy::BaseType *array_expr_ty = nullptr;
+  bool ok = ctx->get_tyctx ()->lookup_type (
+    expr.get_array_expr ()->get_mappings ().get_hirid (), &array_expr_ty);
+  rust_assert (ok);
+
+  // do we need to add an indirect reference
+  if (array_expr_ty->get_kind () == TyTy::TypeKind::REF)
+    {
+      array_reference
+	= indirect_expression (array_reference, expr.get_locus ());
+    }
+
+  translated
+    = ctx->get_backend ()->array_index_expression (array_reference, index,
+						   expr.get_locus ());
+}
+
+} // namespace Compile
+} // namespace Rust
diff --git a/gcc/rust/backend/rust-compile-expr.h b/gcc/rust/backend/rust-compile-expr.h
new file mode 100644
index 00000000000..4c1f95ade29
--- /dev/null
+++ b/gcc/rust/backend/rust-compile-expr.h
@@ -0,0 +1,148 @@
+// 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_COMPILE_EXPR
+#define RUST_COMPILE_EXPR
+
+#include "rust-compile-base.h"
+
+namespace Rust {
+namespace Compile {
+
+class CompileExpr : private HIRCompileBase, protected HIR::HIRExpressionVisitor
+{
+public:
+  static tree Compile (HIR::Expr *expr, Context *ctx);
+
+  void visit (HIR::TupleIndexExpr &expr) override;
+  void visit (HIR::TupleExpr &expr) override;
+  void visit (HIR::ReturnExpr &expr) override;
+  void visit (HIR::CallExpr &expr) override;
+  void visit (HIR::MethodCallExpr &expr) override;
+  void visit (HIR::LiteralExpr &expr) override;
+  void visit (HIR::AssignmentExpr &expr) override;
+  void visit (HIR::CompoundAssignmentExpr &expr) override;
+  void visit (HIR::ArrayIndexExpr &expr) override;
+  void visit (HIR::ArrayExpr &expr) override;
+  void visit (HIR::ArithmeticOrLogicalExpr &expr) override;
+  void visit (HIR::ComparisonExpr &expr) override;
+  void visit (HIR::LazyBooleanExpr &expr) override;
+  void visit (HIR::NegationExpr &expr) override;
+  void visit (HIR::TypeCastExpr &expr) override;
+  void visit (HIR::IfExpr &expr) override;
+  void visit (HIR::IfExprConseqIf &expr) override;
+  void visit (HIR::IfExprConseqElse &expr) override;
+  void visit (HIR::BlockExpr &expr) override;
+  void visit (HIR::UnsafeBlockExpr &expr) override;
+  void visit (HIR::StructExprStruct &struct_expr) override;
+  void visit (HIR::StructExprStructFields &struct_expr) override;
+  void visit (HIR::GroupedExpr &expr) override;
+  void visit (HIR::FieldAccessExpr &expr) override;
+  void visit (HIR::QualifiedPathInExpression &expr) override;
+  void visit (HIR::PathInExpression &expr) override;
+  void visit (HIR::LoopExpr &expr) override;
+  void visit (HIR::WhileLoopExpr &expr) override;
+  void visit (HIR::BreakExpr &expr) override;
+  void visit (HIR::ContinueExpr &expr) override;
+  void visit (HIR::BorrowExpr &expr) override;
+  void visit (HIR::DereferenceExpr &expr) override;
+  void visit (HIR::MatchExpr &expr) override;
+  void visit (HIR::RangeFromToExpr &expr) override;
+  void visit (HIR::RangeFromExpr &expr) override;
+  void visit (HIR::RangeToExpr &expr) override;
+  void visit (HIR::RangeFullExpr &expr) override;
+  void visit (HIR::RangeFromToInclExpr &expr) override;
+
+  // Empty visit for unused Expression HIR nodes.
+  void visit (HIR::ClosureExprInner &) override {}
+  void visit (HIR::ClosureExprInnerTyped &) override {}
+  void visit (HIR::StructExprFieldIdentifier &) override {}
+  void visit (HIR::StructExprFieldIdentifierValue &) override {}
+  void visit (HIR::StructExprFieldIndexValue &) override {}
+  void visit (HIR::ErrorPropagationExpr &) override {}
+  void visit (HIR::RangeToInclExpr &) override {}
+  void visit (HIR::WhileLetLoopExpr &) override {}
+  void visit (HIR::ForLoopExpr &) override {}
+  void visit (HIR::IfExprConseqIfLet &) override {}
+  void visit (HIR::IfLetExpr &) override {}
+  void visit (HIR::IfLetExprConseqElse &) override {}
+  void visit (HIR::IfLetExprConseqIf &) override {}
+  void visit (HIR::IfLetExprConseqIfLet &) override {}
+  void visit (HIR::AwaitExpr &) override {}
+  void visit (HIR::AsyncBlockExpr &) override {}
+
+protected:
+  tree get_fn_addr_from_dyn (const TyTy::DynamicObjectType *dyn,
+			     TyTy::BaseType *receiver, TyTy::FnType *fntype,
+			     tree receiver_ref, Location expr_locus);
+
+  tree get_receiver_from_dyn (const TyTy::DynamicObjectType *dyn,
+			      TyTy::BaseType *receiver, TyTy::FnType *fntype,
+			      tree receiver_ref, Location expr_locus);
+
+  tree resolve_method_address (TyTy::FnType *fntype, HirId ref,
+			       TyTy::BaseType *receiver,
+			       HIR::PathIdentSegment &segment,
+			       Analysis::NodeMapping expr_mappings,
+			       Location expr_locus);
+
+  tree
+  resolve_operator_overload (Analysis::RustLangItem::ItemType lang_item_type,
+			     HIR::OperatorExprMeta expr, tree lhs, tree rhs,
+			     HIR::Expr *lhs_expr, HIR::Expr *rhs_expr);
+
+  tree compile_bool_literal (const HIR::LiteralExpr &expr,
+			     const TyTy::BaseType *tyty);
+
+  tree compile_integer_literal (const HIR::LiteralExpr &expr,
+				const TyTy::BaseType *tyty);
+
+  tree compile_float_literal (const HIR::LiteralExpr &expr,
+			      const TyTy::BaseType *tyty);
+
+  tree compile_char_literal (const HIR::LiteralExpr &expr,
+			     const TyTy::BaseType *tyty);
+
+  tree compile_byte_literal (const HIR::LiteralExpr &expr,
+			     const TyTy::BaseType *tyty);
+
+  tree compile_string_literal (const HIR::LiteralExpr &expr,
+			       const TyTy::BaseType *tyty);
+
+  tree compile_byte_string_literal (const HIR::LiteralExpr &expr,
+				    const TyTy::BaseType *tyty);
+
+  tree type_cast_expression (tree type_to_cast_to, tree expr, Location locus);
+
+  tree array_value_expr (Location expr_locus, const TyTy::ArrayType &array_tyty,
+			 tree array_type, HIR::ArrayElemsValues &elems);
+
+  tree array_copied_expr (Location expr_locus,
+			  const TyTy::ArrayType &array_tyty, tree array_type,
+			  HIR::ArrayElemsCopied &elems);
+
+private:
+  CompileExpr (Context *ctx);
+
+  tree translated;
+};
+
+} // namespace Compile
+} // namespace Rust
+
+#endif // RUST_COMPILE_EXPR
diff --git a/gcc/rust/backend/rust-compile-extern.h b/gcc/rust/backend/rust-compile-extern.h
new file mode 100644
index 00000000000..45a507e03be
--- /dev/null
+++ b/gcc/rust/backend/rust-compile-extern.h
@@ -0,0 +1,172 @@
+// 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_COMPILE_EXTERN_ITEM
+#define RUST_COMPILE_EXTERN_ITEM
+
+#include "rust-compile-base.h"
+#include "rust-compile-intrinsic.h"
+
+namespace Rust {
+namespace Compile {
+
+class CompileExternItem : public HIRCompileBase,
+			  public HIR::HIRExternalItemVisitor
+{
+public:
+  static tree compile (HIR::ExternalItem *item, Context *ctx,
+		       TyTy::BaseType *concrete = nullptr,
+		       bool is_query_mode = false,
+		       Location ref_locus = Location ())
+  {
+    CompileExternItem compiler (ctx, concrete, ref_locus);
+    item->accept_vis (compiler);
+
+    if (is_query_mode && compiler.reference == error_mark_node)
+      rust_internal_error_at (ref_locus, "failed to compile extern item: %s",
+			      item->as_string ().c_str ());
+
+    return compiler.reference;
+  }
+
+  void visit (HIR::ExternalStaticItem &item) override
+  {
+    // check if its already been compiled
+    Bvariable *lookup = ctx->get_backend ()->error_variable ();
+    if (ctx->lookup_var_decl (item.get_mappings ().get_hirid (), &lookup))
+      {
+	reference = ctx->get_backend ()->var_expression (lookup, ref_locus);
+	return;
+      }
+
+    TyTy::BaseType *resolved_type = nullptr;
+    bool ok = ctx->get_tyctx ()->lookup_type (item.get_mappings ().get_hirid (),
+					      &resolved_type);
+    rust_assert (ok);
+
+    std::string name = item.get_item_name ();
+    // FIXME this is assuming C ABI
+    std::string asm_name = name;
+
+    tree type = TyTyResolveCompile::compile (ctx, resolved_type);
+    bool is_external = true;
+    bool is_hidden = false;
+    bool in_unique_section = false;
+
+    Bvariable *static_global
+      = ctx->get_backend ()->global_variable (name, asm_name, type, is_external,
+					      is_hidden, in_unique_section,
+					      item.get_locus ());
+    ctx->insert_var_decl (item.get_mappings ().get_hirid (), static_global);
+    ctx->push_var (static_global);
+
+    reference = ctx->get_backend ()->var_expression (static_global, ref_locus);
+  }
+
+  void visit (HIR::ExternalFunctionItem &function) override
+  {
+    TyTy::BaseType *fntype_tyty;
+    if (!ctx->get_tyctx ()->lookup_type (function.get_mappings ().get_hirid (),
+					 &fntype_tyty))
+      {
+	rust_fatal_error (function.get_locus (),
+			  "failed to lookup function type");
+	return;
+      }
+
+    rust_assert (fntype_tyty->get_kind () == TyTy::TypeKind::FNDEF);
+    TyTy::FnType *fntype = static_cast<TyTy::FnType *> (fntype_tyty);
+    if (fntype->has_subsititions_defined ())
+      {
+	// we cant do anything for this only when it is used and a concrete type
+	// is given
+	if (concrete == nullptr)
+	  return;
+	else
+	  {
+	    rust_assert (concrete->get_kind () == TyTy::TypeKind::FNDEF);
+	    fntype = static_cast<TyTy::FnType *> (concrete);
+	  }
+      }
+
+    // items can be forward compiled which means we may not need to invoke this
+    // code. We might also have already compiled this generic function as well.
+    tree lookup = NULL_TREE;
+    if (ctx->lookup_function_decl (fntype->get_ty_ref (), &lookup,
+				   fntype->get_id (), fntype))
+      {
+	reference = address_expression (lookup, ref_locus);
+	return;
+      }
+
+    if (fntype->has_subsititions_defined ())
+      {
+	// override the Hir Lookups for the substituions in this context
+	fntype->override_context ();
+      }
+
+    if (fntype->get_abi () == ABI::INTRINSIC)
+      {
+	Intrinsics compile (ctx);
+	tree fndecl = compile.compile (fntype);
+	ctx->insert_function_decl (fntype, fndecl);
+	return;
+      }
+
+    tree compiled_fn_type = TyTyResolveCompile::compile (ctx, fntype);
+    std::string ir_symbol_name = function.get_item_name ();
+    std::string asm_name = function.get_item_name ();
+    if (fntype->get_abi () == ABI::RUST)
+      {
+	// then we need to get the canonical path of it and mangle it
+	const Resolver::CanonicalPath *canonical_path = nullptr;
+	bool ok = ctx->get_mappings ()->lookup_canonical_path (
+	  function.get_mappings ().get_nodeid (), &canonical_path);
+	rust_assert (ok);
+
+	ir_symbol_name = canonical_path->get () + fntype->subst_as_string ();
+	asm_name = ctx->mangle_item (fntype, *canonical_path);
+      }
+
+    const unsigned int flags = Backend::function_is_declaration;
+    tree fndecl
+      = ctx->get_backend ()->function (compiled_fn_type, ir_symbol_name,
+				       asm_name, flags, function.get_locus ());
+    TREE_PUBLIC (fndecl) = 1;
+    setup_abi_options (fndecl, fntype->get_abi ());
+
+    ctx->insert_function_decl (fntype, fndecl);
+
+    reference = address_expression (fndecl, ref_locus);
+  }
+
+private:
+  CompileExternItem (Context *ctx, TyTy::BaseType *concrete, Location ref_locus)
+    : HIRCompileBase (ctx), concrete (concrete), reference (error_mark_node),
+      ref_locus (ref_locus)
+  {}
+
+  TyTy::BaseType *concrete;
+  tree reference;
+  Location ref_locus;
+};
+
+} // namespace Compile
+} // namespace Rust
+
+#endif // RUST_COMPILE_EXTERN_ITEM
diff --git a/gcc/rust/backend/rust-compile-fnparam.cc b/gcc/rust/backend/rust-compile-fnparam.cc
new file mode 100644
index 00000000000..3f0ec82b625
--- /dev/null
+++ b/gcc/rust/backend/rust-compile-fnparam.cc
@@ -0,0 +1,121 @@
+// 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/>.
+
+#include "rust-compile-fnparam.h"
+#include "rust-compile-pattern.h"
+
+#include "gimple-expr.h"
+
+namespace Rust {
+namespace Compile {
+
+CompileFnParam::CompileFnParam (Context *ctx, tree fndecl, tree decl_type,
+				Location locus)
+  : HIRCompileBase (ctx), fndecl (fndecl), decl_type (decl_type), locus (locus),
+    compiled_param (ctx->get_backend ()->error_variable ())
+{}
+
+Bvariable *
+CompileFnParam::compile (Context *ctx, tree fndecl, HIR::FunctionParam *param,
+			 tree decl_type, Location locus)
+{
+  CompileFnParam compiler (ctx, fndecl, decl_type, locus);
+  param->get_param_name ()->accept_vis (compiler);
+  return compiler.compiled_param;
+}
+
+Bvariable *
+CompileFnParam::compile (Context *ctx, tree fndecl, HIR::Pattern *param,
+			 tree decl_type, Location locus)
+{
+  CompileFnParam compiler (ctx, fndecl, decl_type, locus);
+  param->accept_vis (compiler);
+  return compiler.compiled_param;
+}
+
+void
+CompileFnParam::visit (HIR::IdentifierPattern &pattern)
+{
+  if (!pattern.is_mut ())
+    decl_type = ctx->get_backend ()->immutable_type (decl_type);
+
+  compiled_param
+    = ctx->get_backend ()->parameter_variable (fndecl,
+					       pattern.get_identifier (),
+					       decl_type, locus);
+}
+
+void
+CompileFnParam::visit (HIR::WildcardPattern &pattern)
+{
+  decl_type = ctx->get_backend ()->immutable_type (decl_type);
+
+  compiled_param
+    = ctx->get_backend ()->parameter_variable (fndecl, "_", decl_type, locus);
+}
+
+void
+CompileFnParam::visit (HIR::StructPattern &pattern)
+{
+  // generate the anon param
+  tree tmp_ident = create_tmp_var_name ("RSTPRM");
+  std::string cpp_str_identifier = std::string (IDENTIFIER_POINTER (tmp_ident));
+
+  decl_type = ctx->get_backend ()->immutable_type (decl_type);
+  compiled_param
+    = ctx->get_backend ()->parameter_variable (fndecl, cpp_str_identifier,
+					       decl_type, locus);
+
+  // setup the pattern bindings
+  tree anon_param = ctx->get_backend ()->var_expression (compiled_param, locus);
+  CompilePatternBindings::Compile (&pattern, anon_param, ctx);
+}
+
+void
+CompileFnParam::visit (HIR::TupleStructPattern &pattern)
+{
+  // generate the anon param
+  tree tmp_ident = create_tmp_var_name ("RSTPRM");
+  std::string cpp_str_identifier = std::string (IDENTIFIER_POINTER (tmp_ident));
+
+  decl_type = ctx->get_backend ()->immutable_type (decl_type);
+  compiled_param
+    = ctx->get_backend ()->parameter_variable (fndecl, cpp_str_identifier,
+					       decl_type, locus);
+
+  // setup the pattern bindings
+  tree anon_param = ctx->get_backend ()->var_expression (compiled_param, locus);
+  CompilePatternBindings::Compile (&pattern, anon_param, ctx);
+}
+
+Bvariable *
+CompileSelfParam::compile (Context *ctx, tree fndecl, HIR::SelfParam &self,
+			   tree decl_type, Location locus)
+{
+  bool is_immutable
+    = self.get_self_kind () == HIR::SelfParam::ImplicitSelfKind::IMM
+      || self.get_self_kind () == HIR::SelfParam::ImplicitSelfKind::IMM_REF;
+  if (is_immutable)
+    decl_type = ctx->get_backend ()->immutable_type (decl_type);
+
+  return ctx->get_backend ()->parameter_variable (fndecl, "self", decl_type,
+						  locus);
+}
+
+} // namespace Compile
+} // namespace Rust
diff --git a/gcc/rust/backend/rust-compile-fnparam.h b/gcc/rust/backend/rust-compile-fnparam.h
new file mode 100644
index 00000000000..0dbbd99ef08
--- /dev/null
+++ b/gcc/rust/backend/rust-compile-fnparam.h
@@ -0,0 +1,70 @@
+// 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_COMPILE_FNPARAM
+#define RUST_COMPILE_FNPARAM
+
+#include "rust-compile-base.h"
+
+namespace Rust {
+namespace Compile {
+
+class CompileFnParam : private HIRCompileBase, protected HIR::HIRPatternVisitor
+{
+public:
+  static Bvariable *compile (Context *ctx, tree fndecl,
+			     HIR::FunctionParam *param, tree decl_type,
+			     Location locus);
+  static Bvariable *compile (Context *ctx, tree fndecl, HIR::Pattern *param,
+			     tree decl_type, Location locus);
+
+  void visit (HIR::IdentifierPattern &pattern) override;
+  void visit (HIR::WildcardPattern &pattern) override;
+  void visit (HIR::StructPattern &) override;
+  void visit (HIR::TupleStructPattern &) override;
+
+  // Empty visit for unused Pattern HIR nodes.
+  void visit (HIR::GroupedPattern &) override {}
+  void visit (HIR::LiteralPattern &) override {}
+  void visit (HIR::PathInExpression &) override {}
+  void visit (HIR::QualifiedPathInExpression &) override {}
+  void visit (HIR::RangePattern &) override {}
+  void visit (HIR::ReferencePattern &) override {}
+  void visit (HIR::SlicePattern &) override {}
+  void visit (HIR::TuplePattern &) override {}
+
+private:
+  CompileFnParam (Context *ctx, tree fndecl, tree decl_type, Location locus);
+
+  tree fndecl;
+  tree decl_type;
+  Location locus;
+  Bvariable *compiled_param;
+};
+
+class CompileSelfParam : private HIRCompileBase
+{
+public:
+  static Bvariable *compile (Context *ctx, tree fndecl, HIR::SelfParam &self,
+			     tree decl_type, Location locus);
+};
+
+} // namespace Compile
+} // namespace Rust
+
+#endif // RUST_COMPILE_FNPARAM
diff --git a/gcc/rust/backend/rust-compile-implitem.cc b/gcc/rust/backend/rust-compile-implitem.cc
new file mode 100644
index 00000000000..d0f70a70228
--- /dev/null
+++ b/gcc/rust/backend/rust-compile-implitem.cc
@@ -0,0 +1,101 @@
+// 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/>.
+
+#include "rust-compile-implitem.h"
+#include "rust-compile-expr.h"
+#include "rust-compile-fnparam.h"
+
+namespace Rust {
+namespace Compile {
+
+void
+CompileTraitItem::visit (HIR::TraitItemConst &constant)
+{
+  rust_assert (concrete != nullptr);
+  TyTy::BaseType *resolved_type = concrete;
+
+  const Resolver::CanonicalPath *canonical_path = nullptr;
+  bool ok = ctx->get_mappings ()->lookup_canonical_path (
+    constant.get_mappings ().get_nodeid (), &canonical_path);
+  rust_assert (ok);
+
+  HIR::Expr *const_value_expr = constant.get_expr ().get ();
+  tree const_expr
+    = compile_constant_item (ctx, resolved_type, canonical_path,
+			     const_value_expr, constant.get_locus ());
+  ctx->push_const (const_expr);
+  ctx->insert_const_decl (constant.get_mappings ().get_hirid (), const_expr);
+
+  reference = const_expr;
+}
+
+void
+CompileTraitItem::visit (HIR::TraitItemFunc &func)
+{
+  rust_assert (func.has_block_defined ());
+
+  rust_assert (concrete->get_kind () == TyTy::TypeKind::FNDEF);
+  TyTy::FnType *fntype = static_cast<TyTy::FnType *> (concrete);
+  fntype->monomorphize ();
+
+  // items can be forward compiled which means we may not need to invoke this
+  // code. We might also have already compiled this generic function as well.
+  tree lookup = NULL_TREE;
+  if (ctx->lookup_function_decl (fntype->get_ty_ref (), &lookup,
+				 fntype->get_id (), fntype))
+    {
+      // has this been added to the list then it must be finished
+      if (ctx->function_completed (lookup))
+	{
+	  tree dummy = NULL_TREE;
+	  if (!ctx->lookup_function_decl (fntype->get_ty_ref (), &dummy))
+	    {
+	      ctx->insert_function_decl (fntype, lookup);
+	    }
+
+	  reference = address_expression (lookup, ref_locus);
+	  return;
+	}
+    }
+
+  if (fntype->has_subsititions_defined ())
+    {
+      // override the Hir Lookups for the substituions in this context
+      fntype->override_context ();
+    }
+
+  const Resolver::CanonicalPath *canonical_path = nullptr;
+  bool ok = ctx->get_mappings ()->lookup_canonical_path (
+    func.get_mappings ().get_nodeid (), &canonical_path);
+  rust_assert (ok);
+
+  // FIXME: How do we get the proper visibility here?
+  auto vis = HIR::Visibility (HIR::Visibility::VisType::PUBLIC);
+  HIR::TraitFunctionDecl &function = func.get_decl ();
+  tree fndecl
+    = compile_function (ctx, function.get_function_name (),
+			function.get_self (), function.get_function_params (),
+			function.get_qualifiers (), vis,
+			func.get_outer_attrs (), func.get_locus (),
+			func.get_block_expr ().get (), canonical_path, fntype,
+			function.has_return_type ());
+  reference = address_expression (fndecl, ref_locus);
+}
+
+} // namespace Compile
+} // namespace Rust
diff --git a/gcc/rust/backend/rust-compile-implitem.h b/gcc/rust/backend/rust-compile-implitem.h
new file mode 100644
index 00000000000..ac9478af150
--- /dev/null
+++ b/gcc/rust/backend/rust-compile-implitem.h
@@ -0,0 +1,91 @@
+// 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_COMPILE_IMPLITEM_H
+#define RUST_COMPILE_IMPLITEM_H
+
+#include "rust-compile-item.h"
+#include "rust-compile-expr.h"
+#include "rust-compile-fnparam.h"
+
+namespace Rust {
+namespace Compile {
+
+// this is a proxy for HIR::ImplItem's back to use the normel HIR::Item path
+class CompileInherentImplItem : public CompileItem
+{
+public:
+  static tree Compile (HIR::ImplItem *item, Context *ctx,
+		       TyTy::BaseType *concrete = nullptr,
+		       bool is_query_mode = false,
+		       Location ref_locus = Location ())
+  {
+    CompileInherentImplItem compiler (ctx, concrete, ref_locus);
+    item->accept_vis (compiler);
+
+    if (is_query_mode && compiler.reference == error_mark_node)
+      rust_internal_error_at (ref_locus, "failed to compile impl item: %s",
+			      item->as_string ().c_str ());
+
+    return compiler.reference;
+  }
+
+private:
+  CompileInherentImplItem (Context *ctx, TyTy::BaseType *concrete,
+			   Location ref_locus)
+    : CompileItem (ctx, concrete, ref_locus)
+  {}
+};
+
+class CompileTraitItem : public HIRCompileBase, public HIR::HIRTraitItemVisitor
+{
+public:
+  static tree Compile (HIR::TraitItem *item, Context *ctx,
+		       TyTy::BaseType *concrete, bool is_query_mode = false,
+		       Location ref_locus = Location ())
+  {
+    CompileTraitItem compiler (ctx, concrete, ref_locus);
+    item->accept_vis (compiler);
+
+    if (is_query_mode && compiler.reference == error_mark_node)
+      rust_internal_error_at (ref_locus, "failed to compile trait item: %s",
+			      item->as_string ().c_str ());
+
+    return compiler.reference;
+  }
+
+  void visit (HIR::TraitItemConst &constant) override;
+  void visit (HIR::TraitItemFunc &func) override;
+
+  void visit (HIR::TraitItemType &typ) override {}
+
+private:
+  CompileTraitItem (Context *ctx, TyTy::BaseType *concrete, Location ref_locus)
+    : HIRCompileBase (ctx), concrete (concrete), reference (error_mark_node),
+      ref_locus (ref_locus)
+  {}
+
+  TyTy::BaseType *concrete;
+  tree reference;
+  Location ref_locus;
+};
+
+} // namespace Compile
+} // namespace Rust
+
+#endif // RUST_COMPILE_IMPLITEM_H
diff --git a/gcc/rust/backend/rust-compile-intrinsic.cc b/gcc/rust/backend/rust-compile-intrinsic.cc
new file mode 100644
index 00000000000..61084b90f33
--- /dev/null
+++ b/gcc/rust/backend/rust-compile-intrinsic.cc
@@ -0,0 +1,515 @@
+// 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/>.
+
+#include "rust-compile-intrinsic.h"
+#include "fold-const.h"
+#include "langhooks.h"
+#include "rust-compile-context.h"
+#include "rust-compile-type.h"
+#include "rust-compile-fnparam.h"
+#include "rust-builtins.h"
+#include "rust-diagnostics.h"
+#include "rust-location.h"
+#include "rust-tree.h"
+#include "tree-core.h"
+#include "print-tree.h"
+
+namespace Rust {
+namespace Compile {
+
+static tree
+offset_handler (Context *ctx, TyTy::FnType *fntype);
+static tree
+sizeof_handler (Context *ctx, TyTy::FnType *fntype);
+static tree
+transmute_handler (Context *ctx, TyTy::FnType *fntype);
+static tree
+rotate_handler (Context *ctx, TyTy::FnType *fntype, tree_code op);
+static tree
+wrapping_op_handler (Context *ctx, TyTy::FnType *fntype, tree_code op);
+static tree
+copy_nonoverlapping_handler (Context *ctx, TyTy::FnType *fntype);
+
+static inline tree
+rotate_left_handler (Context *ctx, TyTy::FnType *fntype)
+{
+  return rotate_handler (ctx, fntype, LROTATE_EXPR);
+}
+static inline tree
+rotate_right_handler (Context *ctx, TyTy::FnType *fntype)
+{
+  return rotate_handler (ctx, fntype, RROTATE_EXPR);
+}
+
+static inline tree
+wrapping_add_handler (Context *ctx, TyTy::FnType *fntype)
+{
+  return wrapping_op_handler (ctx, fntype, PLUS_EXPR);
+}
+static inline tree
+wrapping_sub_handler (Context *ctx, TyTy::FnType *fntype)
+{
+  return wrapping_op_handler (ctx, fntype, MINUS_EXPR);
+}
+static inline tree
+wrapping_mul_handler (Context *ctx, TyTy::FnType *fntype)
+{
+  return wrapping_op_handler (ctx, fntype, MULT_EXPR);
+}
+
+static const std::map<std::string,
+		      std::function<tree (Context *, TyTy::FnType *)>>
+  generic_intrinsics = {{"offset", &offset_handler},
+			{"size_of", &sizeof_handler},
+			{"transmute", &transmute_handler},
+			{"rotate_left", &rotate_left_handler},
+			{"rotate_right", &rotate_right_handler},
+			{"wrapping_add", &wrapping_add_handler},
+			{"wrapping_sub", &wrapping_sub_handler},
+			{"wrapping_mul", &wrapping_mul_handler},
+			{"copy_nonoverlapping", &copy_nonoverlapping_handler}};
+
+Intrinsics::Intrinsics (Context *ctx) : ctx (ctx) {}
+
+tree
+Intrinsics::compile (TyTy::FnType *fntype)
+{
+  rust_assert (fntype->get_abi () == ABI::INTRINSIC);
+
+  tree builtin = error_mark_node;
+  BuiltinsContext &builtin_ctx = BuiltinsContext::get ();
+  if (builtin_ctx.lookup_simple_builtin (fntype->get_identifier (), &builtin))
+    return builtin;
+
+  // is it an generic builtin?
+  auto it = generic_intrinsics.find (fntype->get_identifier ());
+  if (it != generic_intrinsics.end ())
+    return it->second (ctx, fntype);
+
+  Location locus = ctx->get_mappings ()->lookup_location (fntype->get_ref ());
+  rust_error_at (locus, "unknown builtin intrinsic: %s",
+		 fntype->get_identifier ().c_str ());
+
+  return error_mark_node;
+}
+
+/**
+ * Items can be forward compiled which means we may not need to invoke this
+ * code. We might also have already compiled this generic function as well.
+ */
+static bool
+check_for_cached_intrinsic (Context *ctx, TyTy::FnType *fntype, tree *lookup)
+{
+  if (ctx->lookup_function_decl (fntype->get_ty_ref (), lookup,
+				 fntype->get_id (), fntype))
+    {
+      // Has this been added to the list? Then it must be finished
+      if (ctx->function_completed (*lookup))
+	{
+	  tree dummy = NULL_TREE;
+	  if (!ctx->lookup_function_decl (fntype->get_ty_ref (), &dummy))
+	    ctx->insert_function_decl (fntype, *lookup);
+	  return true;
+	}
+    }
+
+  return false;
+}
+
+/**
+ * Maybe override the Hir Lookups for the substituions in this context
+ */
+static void
+maybe_override_ctx (TyTy::FnType *fntype)
+{
+  if (fntype->has_subsititions_defined ())
+    fntype->override_context ();
+}
+
+/**
+ * Compile and setup a function's parameters
+ */
+static void
+compile_fn_params (Context *ctx, TyTy::FnType *fntype, tree fndecl,
+		   std::vector<Bvariable *> *compiled_param_variables,
+		   std::vector<tree_node *> *compiled_param_types = nullptr)
+{
+  for (auto &parm : fntype->get_params ())
+    {
+      auto &referenced_param = parm.first;
+      auto &param_tyty = parm.second;
+      auto compiled_param_type = TyTyResolveCompile::compile (ctx, param_tyty);
+
+      Location param_locus = referenced_param->get_locus ();
+      Bvariable *compiled_param_var
+	= CompileFnParam::compile (ctx, fndecl, referenced_param,
+				   compiled_param_type, param_locus);
+
+      compiled_param_variables->push_back (compiled_param_var);
+      if (compiled_param_types)
+	compiled_param_types->push_back (compiled_param_type);
+    }
+}
+
+static tree
+compile_intrinsic_function (Context *ctx, TyTy::FnType *fntype)
+{
+  maybe_override_ctx (fntype);
+
+  const Resolver::CanonicalPath &canonical_path = fntype->get_ident ().path;
+
+  tree compiled_fn_type = TyTyResolveCompile::compile (ctx, fntype);
+  std::string ir_symbol_name
+    = canonical_path.get () + fntype->subst_as_string ();
+  std::string asm_name = ctx->mangle_item (fntype, canonical_path);
+
+  unsigned int flags = 0;
+  tree fndecl
+    = ctx->get_backend ()->function (compiled_fn_type, ir_symbol_name, asm_name,
+				     flags, fntype->get_ident ().locus);
+
+  TREE_PUBLIC (fndecl) = 0;
+  TREE_READONLY (fndecl) = 1;
+  DECL_ARTIFICIAL (fndecl) = 1;
+  DECL_EXTERNAL (fndecl) = 0;
+  DECL_DECLARED_INLINE_P (fndecl) = 1;
+
+  return fndecl;
+}
+
+static void
+enter_intrinsic_block (Context *ctx, tree fndecl)
+{
+  tree enclosing_scope = NULL_TREE;
+  Location start_location = Location ();
+  Location end_location = Location ();
+
+  auto block = ctx->get_backend ()->block (fndecl, enclosing_scope, {},
+					   start_location, end_location);
+
+  ctx->push_block (block);
+}
+
+static void
+finalize_intrinsic_block (Context *ctx, tree fndecl)
+{
+  tree bind_tree = ctx->pop_block ();
+
+  gcc_assert (TREE_CODE (bind_tree) == BIND_EXPR);
+
+  DECL_SAVED_TREE (fndecl) = bind_tree;
+
+  ctx->push_function (fndecl);
+}
+
+static tree
+offset_handler (Context *ctx, TyTy::FnType *fntype)
+{
+  // offset intrinsic has two params dst pointer and offset isize
+  rust_assert (fntype->get_params ().size () == 2);
+
+  auto fndecl = compile_intrinsic_function (ctx, fntype);
+
+  std::vector<Bvariable *> param_vars;
+  compile_fn_params (ctx, fntype, fndecl, &param_vars);
+
+  auto &dst_param = param_vars.at (0);
+  auto &size_param = param_vars.at (1);
+  rust_assert (param_vars.size () == 2);
+  if (!ctx->get_backend ()->function_set_parameters (fndecl, param_vars))
+    return error_mark_node;
+
+  enter_intrinsic_block (ctx, fndecl);
+
+  // BUILTIN offset FN BODY BEGIN
+  tree dst = ctx->get_backend ()->var_expression (dst_param, Location ());
+  tree size = ctx->get_backend ()->var_expression (size_param, Location ());
+  tree pointer_offset_expr
+    = pointer_offset_expression (dst, size, BUILTINS_LOCATION);
+  auto return_statement
+    = ctx->get_backend ()->return_statement (fndecl, {pointer_offset_expr},
+					     Location ());
+  ctx->add_statement (return_statement);
+  // BUILTIN offset FN BODY END
+
+  finalize_intrinsic_block (ctx, fndecl);
+
+  return fndecl;
+}
+
+static tree
+sizeof_handler (Context *ctx, TyTy::FnType *fntype)
+{
+  // size_of has _zero_ parameters its parameter is the generic one
+  rust_assert (fntype->get_params ().size () == 0);
+
+  tree lookup = NULL_TREE;
+  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
+    return lookup;
+
+  auto fndecl = compile_intrinsic_function (ctx, fntype);
+
+  // get the template parameter type tree fn size_of<T>();
+  rust_assert (fntype->get_num_substitutions () == 1);
+  auto &param_mapping = fntype->get_substs ().at (0);
+  const TyTy::ParamType *param_tyty = param_mapping.get_param_ty ();
+  TyTy::BaseType *resolved_tyty = param_tyty->resolve ();
+  tree template_parameter_type
+    = TyTyResolveCompile::compile (ctx, resolved_tyty);
+
+  enter_intrinsic_block (ctx, fndecl);
+
+  // BUILTIN size_of FN BODY BEGIN
+  tree size_expr = TYPE_SIZE_UNIT (template_parameter_type);
+  auto return_statement
+    = ctx->get_backend ()->return_statement (fndecl, {size_expr}, Location ());
+  ctx->add_statement (return_statement);
+  // BUILTIN size_of FN BODY END
+
+  finalize_intrinsic_block (ctx, fndecl);
+
+  return fndecl;
+}
+
+static tree
+transmute_handler (Context *ctx, TyTy::FnType *fntype)
+{
+  // transmute intrinsic has one parameter
+  rust_assert (fntype->get_params ().size () == 1);
+
+  tree lookup = NULL_TREE;
+  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
+    return lookup;
+
+  auto fndecl = compile_intrinsic_function (ctx, fntype);
+
+  std::vector<Bvariable *> param_vars;
+  std::vector<tree_node *> compiled_types;
+  compile_fn_params (ctx, fntype, fndecl, &param_vars, &compiled_types);
+
+  if (!ctx->get_backend ()->function_set_parameters (fndecl, param_vars))
+    return error_mark_node;
+
+  // param to convert
+  Bvariable *convert_me_param = param_vars.at (0);
+  tree convert_me_expr
+    = ctx->get_backend ()->var_expression (convert_me_param, Location ());
+
+  // check for transmute pre-conditions
+  tree target_type_expr = TREE_TYPE (DECL_RESULT (fndecl));
+  tree source_type_expr = compiled_types.at (0);
+  tree target_size_expr = TYPE_SIZE (target_type_expr);
+  tree source_size_expr = TYPE_SIZE (source_type_expr);
+  // for some reason, unit types and other zero-sized types return NULL for the
+  // size expressions
+  unsigned HOST_WIDE_INT target_size
+    = target_size_expr ? TREE_INT_CST_LOW (target_size_expr) : 0;
+  unsigned HOST_WIDE_INT source_size
+    = source_size_expr ? TREE_INT_CST_LOW (source_size_expr) : 0;
+
+  // size check for concrete types
+  // TODO(liushuyu): check alignment for pointers; check for dependently-sized
+  // types
+  if (target_size != source_size)
+    {
+      rust_error_at (fntype->get_locus (),
+		     "cannot transmute between types of different sizes, or "
+		     "dependently-sized types");
+      rust_inform (fntype->get_ident ().locus, "source type: %qs (%lu bits)",
+		   fntype->get_params ().at (0).second->as_string ().c_str (),
+		   (unsigned long) source_size);
+      rust_inform (fntype->get_ident ().locus, "target type: %qs (%lu bits)",
+		   fntype->get_return_type ()->as_string ().c_str (),
+		   (unsigned long) target_size);
+    }
+
+  enter_intrinsic_block (ctx, fndecl);
+
+  // BUILTIN transmute FN BODY BEGIN
+
+  // Return *((orig_type*)&decl)  */
+
+  tree t
+    = build_fold_addr_expr_loc (Location ().gcc_location (), convert_me_expr);
+  t = fold_build1_loc (Location ().gcc_location (), NOP_EXPR,
+		       build_pointer_type (target_type_expr), t);
+  tree result_expr
+    = build_fold_indirect_ref_loc (Location ().gcc_location (), t);
+
+  auto return_statement
+    = ctx->get_backend ()->return_statement (fndecl, {result_expr},
+					     Location ());
+  ctx->add_statement (return_statement);
+  // BUILTIN transmute FN BODY END
+
+  finalize_intrinsic_block (ctx, fndecl);
+
+  return fndecl;
+}
+
+static tree
+rotate_handler (Context *ctx, TyTy::FnType *fntype, tree_code op)
+{
+  // rotate intrinsic has two parameter
+  rust_assert (fntype->get_params ().size () == 2);
+
+  tree lookup = NULL_TREE;
+  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
+    return lookup;
+
+  auto fndecl = compile_intrinsic_function (ctx, fntype);
+
+  // setup the params
+  std::vector<Bvariable *> param_vars;
+  compile_fn_params (ctx, fntype, fndecl, &param_vars);
+
+  auto &x_param = param_vars.at (0);
+  auto &y_param = param_vars.at (1);
+  rust_assert (param_vars.size () == 2);
+  if (!ctx->get_backend ()->function_set_parameters (fndecl, param_vars))
+    return error_mark_node;
+
+  enter_intrinsic_block (ctx, fndecl);
+
+  // BUILTIN rotate FN BODY BEGIN
+  tree x = ctx->get_backend ()->var_expression (x_param, Location ());
+  tree y = ctx->get_backend ()->var_expression (y_param, Location ());
+  tree rotate_expr
+    = fold_build2_loc (BUILTINS_LOCATION, op, TREE_TYPE (x), x, y);
+  auto return_statement
+    = ctx->get_backend ()->return_statement (fndecl, {rotate_expr},
+					     Location ());
+  ctx->add_statement (return_statement);
+  // BUILTIN rotate FN BODY END
+
+  finalize_intrinsic_block (ctx, fndecl);
+
+  return fndecl;
+}
+
+/**
+ * pub fn wrapping_{add, sub, mul}<T>(lhs: T, rhs: T) -> T;
+ */
+static tree
+wrapping_op_handler (Context *ctx, TyTy::FnType *fntype, tree_code op)
+{
+  // wrapping_<op> intrinsics have two parameter
+  rust_assert (fntype->get_params ().size () == 2);
+
+  tree lookup = NULL_TREE;
+  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
+    return lookup;
+
+  auto fndecl = compile_intrinsic_function (ctx, fntype);
+
+  // setup the params
+  std::vector<Bvariable *> param_vars;
+  compile_fn_params (ctx, fntype, fndecl, &param_vars);
+
+  auto &lhs_param = param_vars.at (0);
+  auto &rhs_param = param_vars.at (1);
+
+  if (!ctx->get_backend ()->function_set_parameters (fndecl, param_vars))
+    return error_mark_node;
+
+  enter_intrinsic_block (ctx, fndecl);
+
+  // BUILTIN wrapping_<op> FN BODY BEGIN
+  auto lhs = ctx->get_backend ()->var_expression (lhs_param, Location ());
+  auto rhs = ctx->get_backend ()->var_expression (rhs_param, Location ());
+
+  // Operations are always wrapping in Rust, as we have -fwrapv enabled by
+  // default. The difference between a wrapping_{add, sub, mul} and a regular
+  // arithmetic operation is that these intrinsics do not panic - they always
+  // carry over.
+  auto wrap_expr = build2 (op, TREE_TYPE (lhs), lhs, rhs);
+
+  auto return_statement
+    = ctx->get_backend ()->return_statement (fndecl, {wrap_expr}, Location ());
+  ctx->add_statement (return_statement);
+  // BUILTIN wrapping_<op> FN BODY END
+
+  finalize_intrinsic_block (ctx, fndecl);
+
+  return fndecl;
+}
+
+/**
+ * fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize);
+ */
+static tree
+copy_nonoverlapping_handler (Context *ctx, TyTy::FnType *fntype)
+{
+  rust_assert (fntype->get_params ().size () == 3);
+  rust_assert (fntype->get_num_substitutions () == 1);
+
+  tree lookup = NULL_TREE;
+  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
+    return lookup;
+
+  auto fndecl = compile_intrinsic_function (ctx, fntype);
+
+  // Most intrinsic functions are pure - not `copy_nonoverlapping`
+  TREE_READONLY (fndecl) = 0;
+  TREE_SIDE_EFFECTS (fndecl) = 1;
+
+  // setup the params
+  std::vector<Bvariable *> param_vars;
+  compile_fn_params (ctx, fntype, fndecl, &param_vars);
+
+  if (!ctx->get_backend ()->function_set_parameters (fndecl, param_vars))
+    return error_mark_node;
+
+  enter_intrinsic_block (ctx, fndecl);
+
+  // BUILTIN copy_nonoverlapping BODY BEGIN
+
+  auto src = ctx->get_backend ()->var_expression (param_vars[0], Location ());
+  auto dst = ctx->get_backend ()->var_expression (param_vars[1], Location ());
+  auto count = ctx->get_backend ()->var_expression (param_vars[2], Location ());
+
+  // We want to create the following statement
+  // memcpy(dst, src, size_of::<T>());
+  // so
+  // memcpy(dst, src, size_expr);
+
+  auto *resolved_ty = fntype->get_substs ().at (0).get_param_ty ()->resolve ();
+  auto param_type = TyTyResolveCompile::compile (ctx, resolved_ty);
+
+  tree size_expr
+    = build2 (MULT_EXPR, size_type_node, TYPE_SIZE_UNIT (param_type), count);
+
+  tree memcpy_raw = nullptr;
+  BuiltinsContext::get ().lookup_simple_builtin ("memcpy", &memcpy_raw);
+  rust_assert (memcpy_raw);
+  auto memcpy
+    = build_fold_addr_expr_loc (Location ().gcc_location (), memcpy_raw);
+
+  auto copy_call
+    = ctx->get_backend ()->call_expression (memcpy, {dst, src, size_expr},
+					    nullptr, Location ());
+
+  ctx->add_statement (copy_call);
+
+  // BUILTIN copy_nonoverlapping BODY END
+
+  finalize_intrinsic_block (ctx, fndecl);
+
+  return fndecl;
+}
+
+} // namespace Compile
+} // namespace Rust
diff --git a/gcc/rust/backend/rust-compile-intrinsic.h b/gcc/rust/backend/rust-compile-intrinsic.h
new file mode 100644
index 00000000000..dceb0864fd4
--- /dev/null
+++ b/gcc/rust/backend/rust-compile-intrinsic.h
@@ -0,0 +1,40 @@
+// 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_COMPILE_INTRINSIC
+#define RUST_COMPILE_INTRINSIC
+
+#include "rust-compile-context.h"
+#include "langhooks.h"
+
+namespace Rust {
+namespace Compile {
+
+class Intrinsics
+{
+public:
+  Intrinsics (Context *ctx);
+
+  tree compile (TyTy::FnType *fntype);
+
+private:
+  Context *ctx;
+};
+
+} // namespace Compile
+} // namespace Rust
+
+#endif // RUST_COMPILE_INTRINSIC
diff --git a/gcc/rust/backend/rust-compile-item.cc b/gcc/rust/backend/rust-compile-item.cc
new file mode 100644
index 00000000000..ceba51c2d27
--- /dev/null
+++ b/gcc/rust/backend/rust-compile-item.cc
@@ -0,0 +1,206 @@
+// 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/>.
+
+#include "rust-compile-item.h"
+#include "rust-compile-implitem.h"
+#include "rust-compile-expr.h"
+#include "rust-compile-extern.h"
+#include "rust-constexpr.h"
+
+namespace Rust {
+namespace Compile {
+
+void
+CompileItem::visit (HIR::StaticItem &var)
+{
+  // have we already compiled this?
+  Bvariable *static_decl_ref = nullptr;
+  if (ctx->lookup_var_decl (var.get_mappings ().get_hirid (), &static_decl_ref))
+    {
+      reference
+	= ctx->get_backend ()->var_expression (static_decl_ref, ref_locus);
+      return;
+    }
+
+  TyTy::BaseType *resolved_type = nullptr;
+  bool ok = ctx->get_tyctx ()->lookup_type (var.get_mappings ().get_hirid (),
+					    &resolved_type);
+  rust_assert (ok);
+
+  tree type = TyTyResolveCompile::compile (ctx, resolved_type);
+  tree value = CompileExpr::Compile (var.get_expr (), ctx);
+
+  const Resolver::CanonicalPath *canonical_path = nullptr;
+  ok = ctx->get_mappings ()->lookup_canonical_path (
+    var.get_mappings ().get_nodeid (), &canonical_path);
+  rust_assert (ok);
+
+  std::string name = canonical_path->get ();
+  std::string asm_name = ctx->mangle_item (resolved_type, *canonical_path);
+
+  bool is_external = false;
+  bool is_hidden = false;
+  bool in_unique_section = true;
+
+  Bvariable *static_global
+    = ctx->get_backend ()->global_variable (name, asm_name, type, is_external,
+					    is_hidden, in_unique_section,
+					    var.get_locus ());
+  ctx->get_backend ()->global_variable_set_init (static_global, value);
+
+  ctx->insert_var_decl (var.get_mappings ().get_hirid (), static_global);
+  ctx->push_var (static_global);
+
+  reference = ctx->get_backend ()->var_expression (static_global, ref_locus);
+}
+
+void
+CompileItem::visit (HIR::ConstantItem &constant)
+{
+  if (ctx->lookup_const_decl (constant.get_mappings ().get_hirid (),
+			      &reference))
+    return;
+
+  // resolve the type
+  TyTy::BaseType *resolved_type = nullptr;
+  bool ok
+    = ctx->get_tyctx ()->lookup_type (constant.get_mappings ().get_hirid (),
+				      &resolved_type);
+  rust_assert (ok);
+
+  // canonical path
+  const Resolver::CanonicalPath *canonical_path = nullptr;
+  ok = ctx->get_mappings ()->lookup_canonical_path (
+    constant.get_mappings ().get_nodeid (), &canonical_path);
+  rust_assert (ok);
+
+  HIR::Expr *const_value_expr = constant.get_expr ();
+  ctx->push_const_context ();
+  tree const_expr
+    = compile_constant_item (ctx, resolved_type, canonical_path,
+			     const_value_expr, constant.get_locus ());
+  ctx->pop_const_context ();
+
+  ctx->push_const (const_expr);
+  ctx->insert_const_decl (constant.get_mappings ().get_hirid (), const_expr);
+  reference = const_expr;
+}
+
+void
+CompileItem::visit (HIR::Function &function)
+{
+  TyTy::BaseType *fntype_tyty;
+  if (!ctx->get_tyctx ()->lookup_type (function.get_mappings ().get_hirid (),
+				       &fntype_tyty))
+    {
+      rust_fatal_error (function.get_locus (),
+			"failed to lookup function type");
+      return;
+    }
+
+  rust_assert (fntype_tyty->get_kind () == TyTy::TypeKind::FNDEF);
+  TyTy::FnType *fntype = static_cast<TyTy::FnType *> (fntype_tyty);
+  if (fntype->has_subsititions_defined ())
+    {
+      // we cant do anything for this only when it is used and a concrete type
+      // is given
+      if (concrete == nullptr)
+	return;
+      else
+	{
+	  rust_assert (concrete->get_kind () == TyTy::TypeKind::FNDEF);
+	  fntype = static_cast<TyTy::FnType *> (concrete);
+	  fntype->monomorphize ();
+	}
+    }
+
+  // items can be forward compiled which means we may not need to invoke this
+  // code. We might also have already compiled this generic function as well.
+  tree lookup = NULL_TREE;
+  if (ctx->lookup_function_decl (fntype->get_ty_ref (), &lookup,
+				 fntype->get_id (), fntype))
+    {
+      // has this been added to the list then it must be finished
+      if (ctx->function_completed (lookup))
+	{
+	  tree dummy = NULL_TREE;
+	  if (!ctx->lookup_function_decl (fntype->get_ty_ref (), &dummy))
+	    {
+	      ctx->insert_function_decl (fntype, lookup);
+	    }
+
+	  reference = address_expression (lookup, ref_locus);
+	  return;
+	}
+    }
+
+  if (fntype->has_subsititions_defined ())
+    {
+      // override the Hir Lookups for the substituions in this context
+      fntype->override_context ();
+    }
+
+  const Resolver::CanonicalPath *canonical_path = nullptr;
+  bool ok = ctx->get_mappings ()->lookup_canonical_path (
+    function.get_mappings ().get_nodeid (), &canonical_path);
+  rust_assert (ok);
+
+  tree fndecl
+    = compile_function (ctx, function.get_function_name (),
+			function.get_self_param (),
+			function.get_function_params (),
+			function.get_qualifiers (), function.get_visibility (),
+			function.get_outer_attrs (), function.get_locus (),
+			function.get_definition ().get (), canonical_path,
+			fntype, function.has_function_return_type ());
+  reference = address_expression (fndecl, ref_locus);
+}
+
+void
+CompileItem::visit (HIR::ImplBlock &impl_block)
+{
+  TyTy::BaseType *self_lookup = nullptr;
+  if (!ctx->get_tyctx ()->lookup_type (
+	impl_block.get_type ()->get_mappings ().get_hirid (), &self_lookup))
+    {
+      rust_error_at (impl_block.get_locus (), "failed to resolve type of impl");
+      return;
+    }
+
+  for (auto &impl_item : impl_block.get_impl_items ())
+    CompileInherentImplItem::Compile (impl_item.get (), ctx);
+}
+
+void
+CompileItem::visit (HIR::ExternBlock &extern_block)
+{
+  for (auto &item : extern_block.get_extern_items ())
+    {
+      CompileExternItem::compile (item.get (), ctx, concrete);
+    }
+}
+
+void
+CompileItem::visit (HIR::Module &module)
+{
+  for (auto &item : module.get_items ())
+    CompileItem::compile (item.get (), ctx);
+}
+
+} // namespace Compile
+} // namespace Rust
diff --git a/gcc/rust/backend/rust-compile-item.h b/gcc/rust/backend/rust-compile-item.h
new file mode 100644
index 00000000000..3c12f1040fc
--- /dev/null
+++ b/gcc/rust/backend/rust-compile-item.h
@@ -0,0 +1,88 @@
+// 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_COMPILE_ITEM
+#define RUST_COMPILE_ITEM
+
+#include "rust-compile-base.h"
+
+namespace Rust {
+namespace Compile {
+
+class CompileItem : private HIRCompileBase, protected HIR::HIRStmtVisitor
+{
+protected:
+public:
+  static tree compile (HIR::Item *item, Context *ctx,
+		       TyTy::BaseType *concrete = nullptr,
+		       bool is_query_mode = false,
+		       Location ref_locus = Location ())
+  {
+    CompileItem compiler (ctx, concrete, ref_locus);
+    item->accept_vis (compiler);
+
+    if (is_query_mode && compiler.reference == error_mark_node)
+      rust_internal_error_at (ref_locus, "failed to compile item: %s",
+			      item->as_string ().c_str ());
+
+    return compiler.reference;
+  }
+
+  void visit (HIR::StaticItem &var) override;
+  void visit (HIR::ConstantItem &constant) override;
+  void visit (HIR::Function &function) override;
+  void visit (HIR::ImplBlock &impl_block) override;
+  void visit (HIR::ExternBlock &extern_block) override;
+  void visit (HIR::Module &module) override;
+
+  // Empty visit for unused Stmt HIR nodes.
+  void visit (HIR::TupleStruct &) override {}
+  void visit (HIR::EnumItem &) override {}
+  void visit (HIR::EnumItemTuple &) override {}
+  void visit (HIR::EnumItemStruct &) override {}
+  void visit (HIR::EnumItemDiscriminant &) override {}
+  void visit (HIR::TypePathSegmentFunction &) override {}
+  void visit (HIR::TypePath &) override {}
+  void visit (HIR::QualifiedPathInType &) override {}
+  void visit (HIR::ExternCrate &) override {}
+  void visit (HIR::UseDeclaration &) override {}
+  void visit (HIR::TypeAlias &) override {}
+  void visit (HIR::StructStruct &) override {}
+  void visit (HIR::Enum &) override {}
+  void visit (HIR::Union &) override {}
+  void visit (HIR::Trait &) override {}
+  void visit (HIR::EmptyStmt &) override {}
+  void visit (HIR::LetStmt &) override {}
+  void visit (HIR::ExprStmtWithoutBlock &) override {}
+  void visit (HIR::ExprStmtWithBlock &) override {}
+
+protected:
+  CompileItem (Context *ctx, TyTy::BaseType *concrete, Location ref_locus)
+    : HIRCompileBase (ctx), concrete (concrete), reference (error_mark_node),
+      ref_locus (ref_locus)
+  {}
+
+  TyTy::BaseType *concrete;
+  tree reference;
+  Location ref_locus;
+};
+
+} // namespace Compile
+} // namespace Rust
+
+#endif // RUST_COMPILE_ITEM
diff --git a/gcc/rust/backend/rust-compile-pattern.cc b/gcc/rust/backend/rust-compile-pattern.cc
new file mode 100644
index 00000000000..1d8eda1a577
--- /dev/null
+++ b/gcc/rust/backend/rust-compile-pattern.cc
@@ -0,0 +1,333 @@
+// 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/>.
+
+#include "rust-compile-pattern.h"
+#include "rust-compile-expr.h"
+#include "rust-compile-resolve-path.h"
+#include "rust-constexpr.h"
+
+namespace Rust {
+namespace Compile {
+
+void
+CompilePatternCaseLabelExpr::visit (HIR::PathInExpression &pattern)
+{
+  // lookup the type
+  TyTy::BaseType *lookup = nullptr;
+  bool ok
+    = ctx->get_tyctx ()->lookup_type (pattern.get_mappings ().get_hirid (),
+				      &lookup);
+  rust_assert (ok);
+
+  // this must be an enum
+  rust_assert (lookup->get_kind () == TyTy::TypeKind::ADT);
+  TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (lookup);
+  rust_assert (adt->is_enum ());
+
+  // lookup the variant
+  HirId variant_id;
+  ok = ctx->get_tyctx ()->lookup_variant_definition (
+    pattern.get_mappings ().get_hirid (), &variant_id);
+  rust_assert (ok);
+
+  TyTy::VariantDef *variant = nullptr;
+  ok = adt->lookup_variant_by_id (variant_id, &variant);
+  rust_assert (ok);
+
+  HIR::Expr *discrim_expr = variant->get_discriminant ();
+  tree discrim_expr_node = CompileExpr::Compile (discrim_expr, ctx);
+  tree folded_discrim_expr = fold_expr (discrim_expr_node);
+  tree case_low = folded_discrim_expr;
+
+  case_label_expr
+    = build_case_label (case_low, NULL_TREE, associated_case_label);
+}
+
+void
+CompilePatternCaseLabelExpr::visit (HIR::StructPattern &pattern)
+{
+  CompilePatternCaseLabelExpr::visit (pattern.get_path ());
+}
+
+void
+CompilePatternCaseLabelExpr::visit (HIR::TupleStructPattern &pattern)
+{
+  CompilePatternCaseLabelExpr::visit (pattern.get_path ());
+}
+
+void
+CompilePatternCaseLabelExpr::visit (HIR::WildcardPattern &pattern)
+{
+  // operand 0 being NULL_TREE signifies this is the default case label see:
+  // tree.def for documentation for CASE_LABEL_EXPR
+  case_label_expr
+    = build_case_label (NULL_TREE, NULL_TREE, associated_case_label);
+}
+
+void
+CompilePatternCaseLabelExpr::visit (HIR::LiteralPattern &pattern)
+{
+  // Compile the literal
+  HIR::LiteralExpr *litexpr
+    = new HIR::LiteralExpr (pattern.get_pattern_mappings (),
+			    pattern.get_literal (), pattern.get_locus (),
+			    std::vector<AST::Attribute> ());
+
+  // Note: Floating point literals are currently accepted but will likely be
+  // forbidden in LiteralPatterns in a future version of Rust.
+  // See: https://github.com/rust-lang/rust/issues/41620
+  // For now, we cannot compile them anyway as CASE_LABEL_EXPR does not support
+  // floating point types.
+  if (pattern.get_literal ().get_lit_type () == HIR::Literal::LitType::FLOAT)
+    {
+      rust_sorry_at (pattern.get_locus (), "floating-point literal in pattern");
+    }
+
+  tree lit = CompileExpr::Compile (litexpr, ctx);
+
+  case_label_expr = build_case_label (lit, NULL_TREE, associated_case_label);
+}
+
+static tree
+compile_range_pattern_bound (HIR::RangePatternBound *bound,
+			     Analysis::NodeMapping mappings, Location locus,
+			     Context *ctx)
+{
+  tree result = NULL_TREE;
+  switch (bound->get_bound_type ())
+    {
+      case HIR::RangePatternBound::RangePatternBoundType::LITERAL: {
+	HIR::RangePatternBoundLiteral &ref
+	  = *static_cast<HIR::RangePatternBoundLiteral *> (bound);
+
+	HIR::LiteralExpr *litexpr
+	  = new HIR::LiteralExpr (mappings, ref.get_literal (), locus,
+				  std::vector<AST::Attribute> ());
+
+	result = CompileExpr::Compile (litexpr, ctx);
+      }
+      break;
+
+      case HIR::RangePatternBound::RangePatternBoundType::PATH: {
+	HIR::RangePatternBoundPath &ref
+	  = *static_cast<HIR::RangePatternBoundPath *> (bound);
+
+	result = ResolvePathRef::Compile (ref.get_path (), ctx);
+
+	// If the path resolves to a const expression, fold it.
+	result = fold_expr (result);
+      }
+      break;
+
+      case HIR::RangePatternBound::RangePatternBoundType::QUALPATH: {
+	HIR::RangePatternBoundQualPath &ref
+	  = *static_cast<HIR::RangePatternBoundQualPath *> (bound);
+
+	result = ResolvePathRef::Compile (ref.get_qualified_path (), ctx);
+
+	// If the path resolves to a const expression, fold it.
+	result = fold_expr (result);
+      }
+    }
+
+  return result;
+}
+
+void
+CompilePatternCaseLabelExpr::visit (HIR::RangePattern &pattern)
+{
+  tree upper = compile_range_pattern_bound (pattern.get_upper_bound ().get (),
+					    pattern.get_pattern_mappings (),
+					    pattern.get_locus (), ctx);
+  tree lower = compile_range_pattern_bound (pattern.get_lower_bound ().get (),
+					    pattern.get_pattern_mappings (),
+					    pattern.get_locus (), ctx);
+
+  case_label_expr = build_case_label (lower, upper, associated_case_label);
+}
+
+// setup the bindings
+
+void
+CompilePatternBindings::visit (HIR::TupleStructPattern &pattern)
+{
+  // lookup the type
+  TyTy::BaseType *lookup = nullptr;
+  bool ok = ctx->get_tyctx ()->lookup_type (
+    pattern.get_path ().get_mappings ().get_hirid (), &lookup);
+  rust_assert (ok);
+
+  // this must be an enum
+  rust_assert (lookup->get_kind () == TyTy::TypeKind::ADT);
+  TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (lookup);
+  rust_assert (adt->number_of_variants () > 0);
+
+  int variant_index = 0;
+  TyTy::VariantDef *variant = adt->get_variants ().at (0);
+  if (adt->is_enum ())
+    {
+      HirId variant_id = UNKNOWN_HIRID;
+      bool ok = ctx->get_tyctx ()->lookup_variant_definition (
+	pattern.get_path ().get_mappings ().get_hirid (), &variant_id);
+      rust_assert (ok);
+
+      ok = adt->lookup_variant_by_id (variant_id, &variant, &variant_index);
+      rust_assert (ok);
+    }
+
+  rust_assert (variant->get_variant_type ()
+	       == TyTy::VariantDef::VariantType::TUPLE);
+
+  std::unique_ptr<HIR::TupleStructItems> &items = pattern.get_items ();
+  switch (items->get_item_type ())
+    {
+      case HIR::TupleStructItems::RANGE: {
+	// TODO
+	gcc_unreachable ();
+      }
+      break;
+
+      case HIR::TupleStructItems::NO_RANGE: {
+	HIR::TupleStructItemsNoRange &items_no_range
+	  = static_cast<HIR::TupleStructItemsNoRange &> (*items.get ());
+
+	rust_assert (items_no_range.get_patterns ().size ()
+		     == variant->num_fields ());
+
+	if (adt->is_enum ())
+	  {
+	    // we are offsetting by + 1 here since the first field in the record
+	    // is always the discriminator
+	    size_t tuple_field_index = 1;
+	    for (auto &pattern : items_no_range.get_patterns ())
+	      {
+		tree variant_accessor
+		  = ctx->get_backend ()->struct_field_expression (
+		    match_scrutinee_expr, variant_index, pattern->get_locus ());
+
+		tree binding = ctx->get_backend ()->struct_field_expression (
+		  variant_accessor, tuple_field_index++, pattern->get_locus ());
+
+		ctx->insert_pattern_binding (
+		  pattern->get_pattern_mappings ().get_hirid (), binding);
+	      }
+	  }
+	else
+	  {
+	    size_t tuple_field_index = 0;
+	    for (auto &pattern : items_no_range.get_patterns ())
+	      {
+		tree variant_accessor = match_scrutinee_expr;
+
+		tree binding = ctx->get_backend ()->struct_field_expression (
+		  variant_accessor, tuple_field_index++, pattern->get_locus ());
+
+		ctx->insert_pattern_binding (
+		  pattern->get_pattern_mappings ().get_hirid (), binding);
+	      }
+	  }
+      }
+      break;
+    }
+}
+
+void
+CompilePatternBindings::visit (HIR::StructPattern &pattern)
+{
+  // lookup the type
+  TyTy::BaseType *lookup = nullptr;
+  bool ok = ctx->get_tyctx ()->lookup_type (
+    pattern.get_path ().get_mappings ().get_hirid (), &lookup);
+  rust_assert (ok);
+
+  // this must be an enum
+  rust_assert (lookup->get_kind () == TyTy::TypeKind::ADT);
+  TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (lookup);
+  rust_assert (adt->number_of_variants () > 0);
+
+  int variant_index = 0;
+  TyTy::VariantDef *variant = adt->get_variants ().at (0);
+  if (adt->is_enum ())
+    {
+      HirId variant_id = UNKNOWN_HIRID;
+      bool ok = ctx->get_tyctx ()->lookup_variant_definition (
+	pattern.get_path ().get_mappings ().get_hirid (), &variant_id);
+      rust_assert (ok);
+
+      ok = adt->lookup_variant_by_id (variant_id, &variant, &variant_index);
+      rust_assert (ok);
+    }
+
+  rust_assert (variant->get_variant_type ()
+	       == TyTy::VariantDef::VariantType::STRUCT);
+
+  auto &struct_pattern_elems = pattern.get_struct_pattern_elems ();
+  for (auto &field : struct_pattern_elems.get_struct_pattern_fields ())
+    {
+      switch (field->get_item_type ())
+	{
+	  case HIR::StructPatternField::ItemType::TUPLE_PAT: {
+	    // TODO
+	    gcc_unreachable ();
+	  }
+	  break;
+
+	  case HIR::StructPatternField::ItemType::IDENT_PAT: {
+	    // TODO
+	    gcc_unreachable ();
+	  }
+	  break;
+
+	  case HIR::StructPatternField::ItemType::IDENT: {
+	    HIR::StructPatternFieldIdent &ident
+	      = static_cast<HIR::StructPatternFieldIdent &> (*field.get ());
+
+	    size_t offs = 0;
+	    ok
+	      = variant->lookup_field (ident.get_identifier (), nullptr, &offs);
+	    rust_assert (ok);
+
+	    tree binding = error_mark_node;
+	    if (adt->is_enum ())
+	      {
+		tree variant_accessor
+		  = ctx->get_backend ()->struct_field_expression (
+		    match_scrutinee_expr, variant_index, ident.get_locus ());
+
+		// we are offsetting by + 1 here since the first field in the
+		// record is always the discriminator
+		binding = ctx->get_backend ()->struct_field_expression (
+		  variant_accessor, offs + 1, ident.get_locus ());
+	      }
+	    else
+	      {
+		tree variant_accessor = match_scrutinee_expr;
+		binding = ctx->get_backend ()->struct_field_expression (
+		  variant_accessor, offs, ident.get_locus ());
+	      }
+
+	    ctx->insert_pattern_binding (ident.get_mappings ().get_hirid (),
+					 binding);
+	  }
+	  break;
+	}
+    }
+}
+
+} // namespace Compile
+} // namespace Rust
diff --git a/gcc/rust/backend/rust-compile-pattern.h b/gcc/rust/backend/rust-compile-pattern.h
new file mode 100644
index 00000000000..0eb5d61249b
--- /dev/null
+++ b/gcc/rust/backend/rust-compile-pattern.h
@@ -0,0 +1,95 @@
+// 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/>.
+
+#include "rust-compile-base.h"
+
+namespace Rust {
+namespace Compile {
+
+class CompilePatternCaseLabelExpr : public HIRCompileBase,
+				    public HIR::HIRPatternVisitor
+{
+public:
+  static tree Compile (HIR::Pattern *pattern, tree associated_case_label,
+		       Context *ctx)
+  {
+    CompilePatternCaseLabelExpr compiler (ctx, associated_case_label);
+    pattern->accept_vis (compiler);
+    return compiler.case_label_expr;
+  }
+
+  void visit (HIR::PathInExpression &pattern) override;
+  void visit (HIR::StructPattern &pattern) override;
+  void visit (HIR::TupleStructPattern &pattern) override;
+  void visit (HIR::WildcardPattern &pattern) override;
+  void visit (HIR::RangePattern &pattern) override;
+
+  // Empty visit for unused Pattern HIR nodes.
+  void visit (HIR::GroupedPattern &) override {}
+  void visit (HIR::IdentifierPattern &) override {}
+  void visit (HIR::LiteralPattern &) override;
+  void visit (HIR::QualifiedPathInExpression &) override {}
+  void visit (HIR::ReferencePattern &) override {}
+  void visit (HIR::SlicePattern &) override {}
+  void visit (HIR::TuplePattern &) override {}
+
+  CompilePatternCaseLabelExpr (Context *ctx, tree associated_case_label)
+    : HIRCompileBase (ctx), case_label_expr (error_mark_node),
+      associated_case_label (associated_case_label)
+  {}
+
+  tree case_label_expr;
+  tree associated_case_label;
+};
+
+class CompilePatternBindings : public HIRCompileBase,
+			       public HIR::HIRPatternVisitor
+{
+public:
+  static void Compile (HIR::Pattern *pattern, tree match_scrutinee_expr,
+		       Context *ctx)
+  {
+    CompilePatternBindings compiler (ctx, match_scrutinee_expr);
+    pattern->accept_vis (compiler);
+  }
+
+  void visit (HIR::StructPattern &pattern) override;
+  void visit (HIR::TupleStructPattern &pattern) override;
+
+  // Empty visit for unused Pattern HIR nodes.
+  void visit (HIR::GroupedPattern &) override {}
+  void visit (HIR::IdentifierPattern &) override {}
+  void visit (HIR::LiteralPattern &) override {}
+  void visit (HIR::PathInExpression &) override {}
+  void visit (HIR::QualifiedPathInExpression &) override {}
+  void visit (HIR::RangePattern &) override {}
+  void visit (HIR::ReferencePattern &) override {}
+  void visit (HIR::SlicePattern &) override {}
+  void visit (HIR::TuplePattern &) override {}
+  void visit (HIR::WildcardPattern &) override {}
+
+protected:
+  CompilePatternBindings (Context *ctx, tree match_scrutinee_expr)
+    : HIRCompileBase (ctx), match_scrutinee_expr (match_scrutinee_expr)
+  {}
+
+  tree match_scrutinee_expr;
+};
+
+} // namespace Compile
+} // namespace Rust
diff --git a/gcc/rust/backend/rust-compile-resolve-path.cc b/gcc/rust/backend/rust-compile-resolve-path.cc
new file mode 100644
index 00000000000..4fb3d540257
--- /dev/null
+++ b/gcc/rust/backend/rust-compile-resolve-path.cc
@@ -0,0 +1,301 @@
+// 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/>.
+
+#include "rust-compile-resolve-path.h"
+#include "rust-compile-intrinsic.h"
+#include "rust-compile-item.h"
+#include "rust-compile-implitem.h"
+#include "rust-compile-expr.h"
+#include "rust-hir-trait-resolve.h"
+#include "rust-hir-path-probe.h"
+#include "rust-compile-extern.h"
+#include "rust-constexpr.h"
+
+namespace Rust {
+namespace Compile {
+
+void
+ResolvePathRef::visit (HIR::QualifiedPathInExpression &expr)
+{
+  resolved = resolve (expr.get_final_segment ().get_segment (),
+		      expr.get_mappings (), expr.get_locus (), true);
+}
+
+void
+ResolvePathRef::visit (HIR::PathInExpression &expr)
+{
+  resolved = resolve (expr.get_final_segment ().get_segment (),
+		      expr.get_mappings (), expr.get_locus (), false);
+}
+
+tree
+ResolvePathRef::resolve (const HIR::PathIdentSegment &final_segment,
+			 const Analysis::NodeMapping &mappings,
+			 Location expr_locus, bool is_qualified_path)
+{
+  TyTy::BaseType *lookup = nullptr;
+  bool ok = ctx->get_tyctx ()->lookup_type (mappings.get_hirid (), &lookup);
+  rust_assert (ok);
+
+  // need to look up the reference for this identifier
+  NodeId ref_node_id = UNKNOWN_NODEID;
+  if (!ctx->get_resolver ()->lookup_resolved_name (mappings.get_nodeid (),
+						   &ref_node_id))
+    {
+      // this can fail because it might be a Constructor for something
+      // in that case the caller should attempt ResolvePathType::Compile
+
+      // it might be an enum data-less enum variant
+      if (lookup->get_kind () != TyTy::TypeKind::ADT)
+	return error_mark_node;
+
+      TyTy::ADTType *adt = static_cast<TyTy::ADTType *> (lookup);
+
+      // it might be a unit-struct
+      if (adt->is_unit ())
+	{
+	  return ctx->get_backend ()->unit_expression ();
+	}
+
+      if (!adt->is_enum ())
+	return error_mark_node;
+
+      HirId variant_id;
+      if (!ctx->get_tyctx ()->lookup_variant_definition (mappings.get_hirid (),
+							 &variant_id))
+	return error_mark_node;
+
+      int union_disriminator = -1;
+      TyTy::VariantDef *variant = nullptr;
+      if (!adt->lookup_variant_by_id (variant_id, &variant,
+				      &union_disriminator))
+	return error_mark_node;
+
+      // this can only be for discriminant variants the others are built up
+      // using call-expr or struct-init
+      rust_assert (variant->get_variant_type ()
+		   == TyTy::VariantDef::VariantType::NUM);
+
+      // we need the actual gcc type
+      tree compiled_adt_type = TyTyResolveCompile::compile (ctx, adt);
+
+      // make the ctor for the union
+      HIR::Expr *discrim_expr = variant->get_discriminant ();
+      tree discrim_expr_node = CompileExpr::Compile (discrim_expr, ctx);
+      tree folded_discrim_expr = fold_expr (discrim_expr_node);
+      tree qualifier = folded_discrim_expr;
+
+      return ctx->get_backend ()->constructor_expression (compiled_adt_type,
+							  true, {qualifier},
+							  union_disriminator,
+							  expr_locus);
+    }
+
+  HirId ref;
+  if (!ctx->get_mappings ()->lookup_node_to_hir (ref_node_id, &ref))
+    {
+      rust_error_at (expr_locus, "reverse call path lookup failure");
+      return error_mark_node;
+    }
+
+  // might be a constant
+  tree constant_expr;
+  if (ctx->lookup_const_decl (ref, &constant_expr))
+    {
+      TREE_USED (constant_expr) = 1;
+      return constant_expr;
+    }
+
+  // this might be a variable reference or a function reference
+  Bvariable *var = nullptr;
+  if (ctx->lookup_var_decl (ref, &var))
+    {
+      // TREE_USED is setup in the gcc abstraction here
+      return ctx->get_backend ()->var_expression (var, expr_locus);
+    }
+
+  // might be a match pattern binding
+  tree binding = error_mark_node;
+  if (ctx->lookup_pattern_binding (ref, &binding))
+    {
+      TREE_USED (binding) = 1;
+      return binding;
+    }
+
+  // it might be a function call
+  if (lookup->get_kind () == TyTy::TypeKind::FNDEF)
+    {
+      TyTy::FnType *fntype = static_cast<TyTy::FnType *> (lookup);
+      tree fn = NULL_TREE;
+      if (ctx->lookup_function_decl (fntype->get_ty_ref (), &fn))
+	{
+	  TREE_USED (fn) = 1;
+	  return address_expression (fn, expr_locus);
+	}
+      else if (fntype->get_abi () == ABI::INTRINSIC)
+	{
+	  Intrinsics compile (ctx);
+	  fn = compile.compile (fntype);
+	  TREE_USED (fn) = 1;
+	  return address_expression (fn, expr_locus);
+	}
+    }
+
+  // let the query system figure it out
+  tree resolved_item = query_compile (ref, lookup, final_segment, mappings,
+				      expr_locus, is_qualified_path);
+  if (resolved_item != error_mark_node)
+    {
+      TREE_USED (resolved_item) = 1;
+    }
+  return resolved_item;
+}
+
+tree
+HIRCompileBase::query_compile (HirId ref, TyTy::BaseType *lookup,
+			       const HIR::PathIdentSegment &final_segment,
+			       const Analysis::NodeMapping &mappings,
+			       Location expr_locus, bool is_qualified_path)
+{
+  HIR::Item *resolved_item = ctx->get_mappings ()->lookup_hir_item (ref);
+  HirId parent_block;
+  HIR::ExternalItem *resolved_extern_item
+    = ctx->get_mappings ()->lookup_hir_extern_item (ref, &parent_block);
+  bool is_hir_item = resolved_item != nullptr;
+  bool is_hir_extern_item = resolved_extern_item != nullptr;
+  if (is_hir_item)
+    {
+      if (!lookup->has_subsititions_defined ())
+	return CompileItem::compile (resolved_item, ctx, nullptr, true,
+				     expr_locus);
+      else
+	return CompileItem::compile (resolved_item, ctx, lookup, true,
+				     expr_locus);
+    }
+  else if (is_hir_extern_item)
+    {
+      if (!lookup->has_subsititions_defined ())
+	return CompileExternItem::compile (resolved_extern_item, ctx, nullptr,
+					   true, expr_locus);
+      else
+	return CompileExternItem::compile (resolved_extern_item, ctx, lookup,
+					   true, expr_locus);
+    }
+  else
+    {
+      HirId parent_impl_id = UNKNOWN_HIRID;
+      HIR::ImplItem *resolved_item
+	= ctx->get_mappings ()->lookup_hir_implitem (ref, &parent_impl_id);
+      bool is_impl_item = resolved_item != nullptr;
+      if (is_impl_item)
+	{
+	  rust_assert (parent_impl_id != UNKNOWN_HIRID);
+	  HIR::Item *impl_ref
+	    = ctx->get_mappings ()->lookup_hir_item (parent_impl_id);
+	  rust_assert (impl_ref != nullptr);
+	  HIR::ImplBlock *impl = static_cast<HIR::ImplBlock *> (impl_ref);
+
+	  TyTy::BaseType *self = nullptr;
+	  bool ok = ctx->get_tyctx ()->lookup_type (
+	    impl->get_type ()->get_mappings ().get_hirid (), &self);
+	  rust_assert (ok);
+
+	  if (!lookup->has_subsititions_defined ())
+	    return CompileInherentImplItem::Compile (resolved_item, ctx,
+						     nullptr, true, expr_locus);
+	  else
+	    return CompileInherentImplItem::Compile (resolved_item, ctx, lookup,
+						     true, expr_locus);
+	}
+      else
+	{
+	  // it might be resolved to a trait item
+	  HIR::TraitItem *trait_item
+	    = ctx->get_mappings ()->lookup_hir_trait_item (ref);
+	  HIR::Trait *trait = ctx->get_mappings ()->lookup_trait_item_mapping (
+	    trait_item->get_mappings ().get_hirid ());
+
+	  Resolver::TraitReference *trait_ref
+	    = &Resolver::TraitReference::error_node ();
+	  bool ok = ctx->get_tyctx ()->lookup_trait_reference (
+	    trait->get_mappings ().get_defid (), &trait_ref);
+	  rust_assert (ok);
+
+	  TyTy::BaseType *receiver = nullptr;
+	  ok = ctx->get_tyctx ()->lookup_receiver (mappings.get_hirid (),
+						   &receiver);
+	  rust_assert (ok);
+
+	  if (receiver->get_kind () == TyTy::TypeKind::PARAM)
+	    {
+	      TyTy::ParamType *p = static_cast<TyTy::ParamType *> (receiver);
+	      receiver = p->resolve ();
+	    }
+
+	  // the type resolver can only resolve type bounds to their trait
+	  // item so its up to us to figure out if this path should resolve
+	  // to an trait-impl-block-item or if it can be defaulted to the
+	  // trait-impl-item's definition
+	  std::vector<Resolver::PathProbeCandidate> candidates
+	    = Resolver::PathProbeImplTrait::Probe (receiver, final_segment,
+						   trait_ref);
+	  if (candidates.size () == 0)
+	    {
+	      // this means we are defaulting back to the trait_item if
+	      // possible
+	      Resolver::TraitItemReference *trait_item_ref = nullptr;
+	      bool ok = trait_ref->lookup_hir_trait_item (*trait_item,
+							  &trait_item_ref);
+	      rust_assert (ok);				    // found
+	      rust_assert (trait_item_ref->is_optional ()); // has definition
+
+	      return CompileTraitItem::Compile (
+		trait_item_ref->get_hir_trait_item (), ctx, lookup, true,
+		expr_locus);
+	    }
+	  else
+	    {
+	      Resolver::PathProbeCandidate &candidate = candidates.at (0);
+	      rust_assert (candidate.is_impl_candidate ());
+
+	      HIR::ImplBlock *impl = candidate.item.impl.parent;
+	      HIR::ImplItem *impl_item = candidate.item.impl.impl_item;
+
+	      TyTy::BaseType *self = nullptr;
+	      bool ok = ctx->get_tyctx ()->lookup_type (
+		impl->get_type ()->get_mappings ().get_hirid (), &self);
+	      rust_assert (ok);
+
+	      if (!lookup->has_subsititions_defined ())
+		return CompileInherentImplItem::Compile (impl_item, ctx,
+							 nullptr, true,
+							 expr_locus);
+	      else
+		return CompileInherentImplItem::Compile (impl_item, ctx, lookup,
+							 true, expr_locus);
+
+	      lookup->set_ty_ref (impl_item->get_impl_mappings ().get_hirid ());
+	    }
+	}
+    }
+
+  return error_mark_node;
+}
+
+} // namespace Compile
+} // namespace Rust
diff --git a/gcc/rust/backend/rust-compile-resolve-path.h b/gcc/rust/backend/rust-compile-resolve-path.h
new file mode 100644
index 00000000000..f0360bdc739
--- /dev/null
+++ b/gcc/rust/backend/rust-compile-resolve-path.h
@@ -0,0 +1,73 @@
+// 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_COMPILE_RESOLVE_PATH
+#define RUST_COMPILE_RESOLVE_PATH
+
+#include "rust-compile-base.h"
+
+namespace Rust {
+namespace Compile {
+
+class ResolvePathRef : public HIRCompileBase, public HIR::HIRPatternVisitor
+{
+public:
+  static tree Compile (HIR::QualifiedPathInExpression &expr, Context *ctx)
+  {
+    ResolvePathRef resolver (ctx);
+    expr.accept_vis (resolver);
+    return resolver.resolved;
+  }
+
+  static tree Compile (HIR::PathInExpression &expr, Context *ctx)
+  {
+    ResolvePathRef resolver (ctx);
+    expr.accept_vis (resolver);
+    return resolver.resolved;
+  }
+
+  void visit (HIR::PathInExpression &expr) override;
+  void visit (HIR::QualifiedPathInExpression &expr) override;
+
+  // Empty visit for unused Pattern HIR nodes.
+  void visit (HIR::GroupedPattern &) override {}
+  void visit (HIR::IdentifierPattern &) override {}
+  void visit (HIR::LiteralPattern &) override {}
+  void visit (HIR::RangePattern &) override {}
+  void visit (HIR::ReferencePattern &) override {}
+  void visit (HIR::SlicePattern &) override {}
+  void visit (HIR::StructPattern &) override {}
+  void visit (HIR::TuplePattern &) override {}
+  void visit (HIR::TupleStructPattern &) override {}
+  void visit (HIR::WildcardPattern &) override {}
+
+  ResolvePathRef (Context *ctx)
+    : HIRCompileBase (ctx), resolved (error_mark_node)
+  {}
+
+  tree resolve (const HIR::PathIdentSegment &final_segment,
+		const Analysis::NodeMapping &mappings, Location locus,
+		bool is_qualified_path);
+
+  tree resolved;
+};
+
+} // namespace Compile
+} // namespace Rust
+
+#endif // RUST_COMPILE_RESOLVE_PATH
diff --git a/gcc/rust/backend/rust-compile-stmt.cc b/gcc/rust/backend/rust-compile-stmt.cc
new file mode 100644
index 00000000000..bfb25f12980
--- /dev/null
+++ b/gcc/rust/backend/rust-compile-stmt.cc
@@ -0,0 +1,115 @@
+// 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/>.
+
+#include "rust-compile-stmt.h"
+#include "rust-compile-expr.h"
+
+namespace Rust {
+namespace Compile {
+
+CompileStmt::CompileStmt (Context *ctx)
+  : HIRCompileBase (ctx), translated (nullptr)
+{}
+
+tree
+CompileStmt::Compile (HIR::Stmt *stmt, Context *ctx)
+{
+  CompileStmt compiler (ctx);
+  stmt->accept_vis (compiler);
+  return compiler.translated;
+}
+
+void
+CompileStmt::visit (HIR::ExprStmtWithBlock &stmt)
+{
+  translated = CompileExpr::Compile (stmt.get_expr (), ctx);
+}
+
+void
+CompileStmt::visit (HIR::ExprStmtWithoutBlock &stmt)
+{
+  translated = CompileExpr::Compile (stmt.get_expr (), ctx);
+}
+
+void
+CompileStmt::visit (HIR::LetStmt &stmt)
+{
+  // nothing to do
+  if (!stmt.has_init_expr ())
+    return;
+
+  const HIR::Pattern &stmt_pattern = *stmt.get_pattern ();
+  HirId stmt_id = stmt_pattern.get_pattern_mappings ().get_hirid ();
+
+  TyTy::BaseType *ty = nullptr;
+  if (!ctx->get_tyctx ()->lookup_type (stmt_id, &ty))
+    {
+      // FIXME this should be an assertion instead
+      rust_fatal_error (stmt.get_locus (),
+			"failed to lookup variable declaration type");
+      return;
+    }
+
+  Bvariable *var = nullptr;
+  if (!ctx->lookup_var_decl (stmt_id, &var))
+    {
+      // FIXME this should be an assertion instead and use error mark node
+      rust_fatal_error (stmt.get_locus (),
+			"failed to lookup compiled variable declaration");
+      return;
+    }
+
+  tree init = CompileExpr::Compile (stmt.get_init_expr (), ctx);
+  // FIXME use error_mark_node, check that CompileExpr returns error_mark_node
+  // on failure and make this an assertion
+  if (init == nullptr)
+    return;
+
+  TyTy::BaseType *actual = nullptr;
+  bool ok = ctx->get_tyctx ()->lookup_type (
+    stmt.get_init_expr ()->get_mappings ().get_hirid (), &actual);
+  rust_assert (ok);
+  tree stmt_type = TyTyResolveCompile::compile (ctx, ty);
+
+  Location lvalue_locus = stmt.get_pattern ()->get_locus ();
+  Location rvalue_locus = stmt.get_init_expr ()->get_locus ();
+  TyTy::BaseType *expected = ty;
+  init = coercion_site (stmt.get_mappings ().get_hirid (), init, actual,
+			expected, lvalue_locus, rvalue_locus);
+
+  auto fnctx = ctx->peek_fn ();
+  if (ty->is_unit ())
+    {
+      ctx->add_statement (init);
+
+      auto unit_type_init_expr
+	= ctx->get_backend ()->constructor_expression (stmt_type, false, {}, -1,
+						       rvalue_locus);
+      auto s = ctx->get_backend ()->init_statement (fnctx.fndecl, var,
+						    unit_type_init_expr);
+      ctx->add_statement (s);
+    }
+  else
+    {
+      auto s = ctx->get_backend ()->init_statement (fnctx.fndecl, var, init);
+      ctx->add_statement (s);
+    }
+}
+
+} // namespace Compile
+} // namespace Rust
diff --git a/gcc/rust/backend/rust-compile-stmt.h b/gcc/rust/backend/rust-compile-stmt.h
new file mode 100644
index 00000000000..a0ec8b26667
--- /dev/null
+++ b/gcc/rust/backend/rust-compile-stmt.h
@@ -0,0 +1,69 @@
+// 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_COMPILE_STMT
+#define RUST_COMPILE_STMT
+
+#include "rust-compile-base.h"
+
+namespace Rust {
+namespace Compile {
+
+class CompileStmt : private HIRCompileBase, protected HIR::HIRStmtVisitor
+{
+public:
+  static tree Compile (HIR::Stmt *stmt, Context *ctx);
+
+  void visit (HIR::ExprStmtWithBlock &stmt) override;
+  void visit (HIR::ExprStmtWithoutBlock &stmt) override;
+  void visit (HIR::LetStmt &stmt) override;
+
+  // Empty visit for unused Stmt HIR nodes.
+  void visit (HIR::TupleStruct &) override {}
+  void visit (HIR::EnumItem &) override {}
+  void visit (HIR::EnumItemTuple &) override {}
+  void visit (HIR::EnumItemStruct &) override {}
+  void visit (HIR::EnumItemDiscriminant &) override {}
+  void visit (HIR::TypePathSegmentFunction &) override {}
+  void visit (HIR::TypePath &) override {}
+  void visit (HIR::QualifiedPathInType &) override {}
+  void visit (HIR::Module &) override {}
+  void visit (HIR::ExternCrate &) override {}
+  void visit (HIR::UseDeclaration &) override {}
+  void visit (HIR::Function &) override {}
+  void visit (HIR::TypeAlias &) override {}
+  void visit (HIR::StructStruct &) override {}
+  void visit (HIR::Enum &) override {}
+  void visit (HIR::Union &) override {}
+  void visit (HIR::ConstantItem &) override {}
+  void visit (HIR::StaticItem &) override {}
+  void visit (HIR::Trait &) override {}
+  void visit (HIR::ImplBlock &) override {}
+  void visit (HIR::ExternBlock &) override {}
+  void visit (HIR::EmptyStmt &) override {}
+
+private:
+  CompileStmt (Context *ctx);
+
+  tree translated;
+};
+
+} // namespace Compile
+} // namespace Rust
+
+#endif // RUST_COMPILE_STMT
diff --git a/gcc/rust/backend/rust-compile-struct-field-expr.cc b/gcc/rust/backend/rust-compile-struct-field-expr.cc
new file mode 100644
index 00000000000..c9a2811f611
--- /dev/null
+++ b/gcc/rust/backend/rust-compile-struct-field-expr.cc
@@ -0,0 +1,81 @@
+// 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/>.
+
+#include "rust-compile-struct-field-expr.h"
+#include "rust-compile-expr.h"
+
+namespace Rust {
+namespace Compile {
+
+CompileStructExprField::CompileStructExprField (Context *ctx)
+  : HIRCompileBase (ctx), translated (error_mark_node)
+{}
+
+tree
+CompileStructExprField::Compile (HIR::StructExprField *field, Context *ctx)
+{
+  CompileStructExprField compiler (ctx);
+  switch (field->get_kind ())
+    {
+    case HIR::StructExprField::StructExprFieldKind::IDENTIFIER:
+      compiler.visit (static_cast<HIR::StructExprFieldIdentifier &> (*field));
+      break;
+
+    case HIR::StructExprField::StructExprFieldKind::IDENTIFIER_VALUE:
+      compiler.visit (
+	static_cast<HIR::StructExprFieldIdentifierValue &> (*field));
+      break;
+
+    case HIR::StructExprField::StructExprFieldKind::INDEX_VALUE:
+      compiler.visit (static_cast<HIR::StructExprFieldIndexValue &> (*field));
+      break;
+    }
+  return compiler.translated;
+}
+
+void
+CompileStructExprField::visit (HIR::StructExprFieldIdentifierValue &field)
+{
+  translated = CompileExpr::Compile (field.get_value (), ctx);
+}
+
+void
+CompileStructExprField::visit (HIR::StructExprFieldIndexValue &field)
+{
+  translated = CompileExpr::Compile (field.get_value (), ctx);
+}
+
+void
+CompileStructExprField::visit (HIR::StructExprFieldIdentifier &field)
+{
+  // we can make the field look like a path expr to take advantage of existing
+  // code
+
+  Analysis::NodeMapping mappings_copy1 = field.get_mappings ();
+  Analysis::NodeMapping mappings_copy2 = field.get_mappings ();
+
+  HIR::PathIdentSegment ident_seg (field.get_field_name ());
+  HIR::PathExprSegment seg (mappings_copy1, ident_seg, field.get_locus (),
+			    HIR::GenericArgs::create_empty ());
+  HIR::PathInExpression expr (mappings_copy2, {seg}, field.get_locus (), false,
+			      {});
+  translated = CompileExpr::Compile (&expr, ctx);
+}
+
+} // namespace Compile
+} // namespace Rust
diff --git a/gcc/rust/backend/rust-compile-struct-field-expr.h b/gcc/rust/backend/rust-compile-struct-field-expr.h
new file mode 100644
index 00000000000..bc5da080dfe
--- /dev/null
+++ b/gcc/rust/backend/rust-compile-struct-field-expr.h
@@ -0,0 +1,46 @@
+// 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_COMPILE_STRUCT_FIELD_EXPR
+#define RUST_COMPILE_STRUCT_FIELD_EXPR
+
+#include "rust-compile-base.h"
+
+namespace Rust {
+namespace Compile {
+
+class CompileStructExprField : private HIRCompileBase
+{
+public:
+  static tree Compile (HIR::StructExprField *field, Context *ctx);
+
+protected:
+  void visit (HIR::StructExprFieldIdentifierValue &field);
+  void visit (HIR::StructExprFieldIndexValue &field);
+  void visit (HIR::StructExprFieldIdentifier &field);
+
+private:
+  CompileStructExprField (Context *ctx);
+
+  tree translated;
+};
+
+} // namespace Compile
+} // namespace Rust
+
+#endif // RUST_COMPILE_STRUCT_FIELD_EXPR
diff --git a/gcc/rust/backend/rust-compile-type.cc b/gcc/rust/backend/rust-compile-type.cc
new file mode 100644
index 00000000000..eced909673e
--- /dev/null
+++ b/gcc/rust/backend/rust-compile-type.cc
@@ -0,0 +1,713 @@
+// 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/>.
+
+#include "rust-compile-type.h"
+#include "rust-compile-expr.h"
+#include "rust-constexpr.h"
+
+#include "tree.h"
+
+namespace Rust {
+namespace Compile {
+
+static const std::string RUST_ENUM_DISR_FIELD_NAME = "RUST$ENUM$DISR";
+
+TyTyResolveCompile::TyTyResolveCompile (Context *ctx, bool trait_object_mode)
+  : ctx (ctx), trait_object_mode (trait_object_mode),
+    translated (error_mark_node), recurisve_ops (0)
+{}
+
+tree
+TyTyResolveCompile::compile (Context *ctx, const TyTy::BaseType *ty,
+			     bool trait_object_mode)
+{
+  TyTyResolveCompile compiler (ctx, trait_object_mode);
+  ty->accept_vis (compiler);
+
+  if (compiler.translated != error_mark_node
+      && TYPE_NAME (compiler.translated) != NULL)
+    {
+      // canonicalize the type
+      compiler.translated = ctx->insert_compiled_type (compiler.translated);
+    }
+
+  return compiler.translated;
+}
+
+// see: gcc/c/c-decl.cc:8230-8241
+// https://github.com/Rust-GCC/gccrs/blob/0024bc2f028369b871a65ceb11b2fddfb0f9c3aa/gcc/c/c-decl.c#L8229-L8241
+tree
+TyTyResolveCompile::get_implicit_enumeral_node_type (Context *ctx)
+{
+  // static tree enum_node = NULL_TREE;
+  // if (enum_node == NULL_TREE)
+  //   {
+  //     enum_node = make_node (ENUMERAL_TYPE);
+  //     SET_TYPE_MODE (enum_node, TYPE_MODE (unsigned_type_node));
+  //     SET_TYPE_ALIGN (enum_node, TYPE_ALIGN (unsigned_type_node));
+  //     TYPE_USER_ALIGN (enum_node) = 0;
+  //     TYPE_UNSIGNED (enum_node) = 1;
+  //     TYPE_PRECISION (enum_node) = TYPE_PRECISION (unsigned_type_node);
+  //     TYPE_MIN_VALUE (enum_node) = TYPE_MIN_VALUE (unsigned_type_node);
+  //     TYPE_MAX_VALUE (enum_node) = TYPE_MAX_VALUE (unsigned_type_node);
+
+  //     // tree identifier = ctx->get_backend ()->get_identifier_node
+  //     // ("enumeral"); tree enum_decl
+  //     //   = build_decl (BUILTINS_LOCATION, TYPE_DECL, identifier,
+  //     enum_node);
+  //     // TYPE_NAME (enum_node) = enum_decl;
+  //   }
+  // return enum_node;
+
+  static tree enum_node = NULL_TREE;
+  if (enum_node == NULL_TREE)
+    {
+      enum_node = ctx->get_backend ()->named_type (
+	"enumeral", ctx->get_backend ()->integer_type (false, 64),
+	Linemap::predeclared_location ());
+    }
+  return enum_node;
+}
+
+void
+TyTyResolveCompile::visit (const TyTy::ErrorType &)
+{
+  translated = error_mark_node;
+}
+
+void
+TyTyResolveCompile::visit (const TyTy::InferType &)
+{
+  translated = error_mark_node;
+}
+
+void
+TyTyResolveCompile::visit (const TyTy::ClosureType &)
+{
+  gcc_unreachable ();
+}
+
+void
+TyTyResolveCompile::visit (const TyTy::ProjectionType &type)
+{
+  type.get ()->accept_vis (*this);
+}
+
+void
+TyTyResolveCompile::visit (const TyTy::PlaceholderType &type)
+{
+  type.resolve ()->accept_vis (*this);
+}
+
+void
+TyTyResolveCompile::visit (const TyTy::ParamType &param)
+{
+  if (recurisve_ops++ >= rust_max_recursion_depth)
+    {
+      rust_error_at (Location (),
+		     "%<recursion depth%> count exceeds limit of %i (use "
+		     "%<frust-max-recursion-depth=%> to increase the limit)",
+		     rust_max_recursion_depth);
+      translated = error_mark_node;
+      return;
+    }
+
+  param.resolve ()->accept_vis (*this);
+}
+
+void
+TyTyResolveCompile::visit (const TyTy::FnType &type)
+{
+  Backend::typed_identifier receiver;
+  std::vector<Backend::typed_identifier> parameters;
+  std::vector<Backend::typed_identifier> results;
+
+  if (!type.get_return_type ()->is_unit ())
+    {
+      auto hir_type = type.get_return_type ();
+      auto ret = TyTyResolveCompile::compile (ctx, hir_type, trait_object_mode);
+      results.push_back (Backend::typed_identifier (
+	"_", ret,
+	ctx->get_mappings ()->lookup_location (hir_type->get_ref ())));
+    }
+
+  for (auto &param_pair : type.get_params ())
+    {
+      auto param_tyty = param_pair.second;
+      auto compiled_param_type
+	= TyTyResolveCompile::compile (ctx, param_tyty, trait_object_mode);
+
+      auto compiled_param = Backend::typed_identifier (
+	param_pair.first->as_string (), compiled_param_type,
+	ctx->get_mappings ()->lookup_location (param_tyty->get_ref ()));
+
+      parameters.push_back (compiled_param);
+    }
+
+  if (!type.is_varadic ())
+    translated
+      = ctx->get_backend ()->function_type (receiver, parameters, results, NULL,
+					    type.get_ident ().locus);
+  else
+    translated
+      = ctx->get_backend ()->function_type_varadic (receiver, parameters,
+						    results, NULL,
+						    type.get_ident ().locus);
+}
+
+void
+TyTyResolveCompile::visit (const TyTy::FnPtr &type)
+{
+  tree result_type = TyTyResolveCompile::compile (ctx, type.get_return_type ());
+
+  std::vector<tree> parameters;
+
+  auto &params = type.get_params ();
+  for (auto &p : params)
+    {
+      tree pty = TyTyResolveCompile::compile (ctx, p.get_tyty ());
+      parameters.push_back (pty);
+    }
+
+  translated = ctx->get_backend ()->function_ptr_type (result_type, parameters,
+						       type.get_ident ().locus);
+}
+
+void
+TyTyResolveCompile::visit (const TyTy::ADTType &type)
+{
+  tree type_record = error_mark_node;
+  if (!type.is_enum ())
+    {
+      rust_assert (type.number_of_variants () == 1);
+
+      TyTy::VariantDef &variant = *type.get_variants ().at (0);
+      std::vector<Backend::typed_identifier> fields;
+      for (size_t i = 0; i < variant.num_fields (); i++)
+	{
+	  const TyTy::StructFieldType *field = variant.get_field_at_index (i);
+	  tree compiled_field_ty
+	    = TyTyResolveCompile::compile (ctx, field->get_field_type ());
+
+	  Backend::typed_identifier f (field->get_name (), compiled_field_ty,
+				       ctx->get_mappings ()->lookup_location (
+					 type.get_ty_ref ()));
+	  fields.push_back (std::move (f));
+	}
+
+      type_record = type.is_union ()
+		      ? ctx->get_backend ()->union_type (fields)
+		      : ctx->get_backend ()->struct_type (fields);
+    }
+  else
+    {
+      // see:
+      // https://github.com/bminor/binutils-gdb/blob/527b8861cd472385fa9160a91dd6d65a25c41987/gdb/dwarf2/read.c#L9010-L9241
+      //
+      // enums are actually a big union so for example the rust enum:
+      //
+      // enum AnEnum {
+      //   A,
+      //   B,
+      //   C (char),
+      //   D { x: i64, y: i64 },
+      // }
+      //
+      // we actually turn this into
+      //
+      // union {
+      //   struct A { int RUST$ENUM$DISR; }; <- this is a data-less variant
+      //   struct B { int RUST$ENUM$DISR; }; <- this is a data-less variant
+      //   struct C { int RUST$ENUM$DISR; char __0; };
+      //   struct D { int RUST$ENUM$DISR; i64 x; i64 y; };
+      // }
+      //
+      // Ada, qual_union_types might still work for this but I am not 100% sure.
+      // I ran into some issues lets reuse our normal union and ask Ada people
+      // about it.
+
+      std::vector<tree> variant_records;
+      for (auto &variant : type.get_variants ())
+	{
+	  std::vector<Backend::typed_identifier> fields;
+
+	  // add in the qualifier field for the variant
+	  tree enumeral_type
+	    = TyTyResolveCompile::get_implicit_enumeral_node_type (ctx);
+	  Backend::typed_identifier f (RUST_ENUM_DISR_FIELD_NAME, enumeral_type,
+				       ctx->get_mappings ()->lookup_location (
+					 variant->get_id ()));
+	  fields.push_back (std::move (f));
+
+	  // compile the rest of the fields
+	  for (size_t i = 0; i < variant->num_fields (); i++)
+	    {
+	      const TyTy::StructFieldType *field
+		= variant->get_field_at_index (i);
+	      tree compiled_field_ty
+		= TyTyResolveCompile::compile (ctx, field->get_field_type ());
+
+	      std::string field_name = field->get_name ();
+	      if (variant->get_variant_type ()
+		  == TyTy::VariantDef::VariantType::TUPLE)
+		field_name = "__" + field->get_name ();
+
+	      Backend::typed_identifier f (
+		field_name, compiled_field_ty,
+		ctx->get_mappings ()->lookup_location (type.get_ty_ref ()));
+	      fields.push_back (std::move (f));
+	    }
+
+	  tree variant_record = ctx->get_backend ()->struct_type (fields);
+	  tree named_variant_record = ctx->get_backend ()->named_type (
+	    variant->get_ident ().path.get (), variant_record,
+	    variant->get_ident ().locus);
+
+	  // set the qualifier to be a builtin
+	  DECL_ARTIFICIAL (TYPE_FIELDS (variant_record)) = 1;
+
+	  // add them to the list
+	  variant_records.push_back (named_variant_record);
+	}
+
+      // now we need to make the actual union, but first we need to make
+      // named_type TYPE_DECL's out of the variants
+
+      size_t i = 0;
+      std::vector<Backend::typed_identifier> enum_fields;
+      for (auto &variant_record : variant_records)
+	{
+	  TyTy::VariantDef *variant = type.get_variants ().at (i++);
+	  std::string implicit_variant_name = variant->get_identifier ();
+
+	  Backend::typed_identifier f (implicit_variant_name, variant_record,
+				       ctx->get_mappings ()->lookup_location (
+					 type.get_ty_ref ()));
+	  enum_fields.push_back (std::move (f));
+	}
+
+      // finally make the union or the enum
+      type_record = ctx->get_backend ()->union_type (enum_fields);
+    }
+
+  // Handle repr options
+  // TODO: "packed" should only narrow type alignment and "align" should only
+  // widen it. Do we need to check and enforce this here, or is it taken care of
+  // later on in the gcc middle-end?
+  TyTy::ADTType::ReprOptions repr = type.get_repr_options ();
+  if (repr.pack)
+    {
+      TYPE_PACKED (type_record) = 1;
+      if (repr.pack > 1)
+	{
+	  SET_TYPE_ALIGN (type_record, repr.pack * 8);
+	  TYPE_USER_ALIGN (type_record) = 1;
+	}
+    }
+  else if (repr.align)
+    {
+      SET_TYPE_ALIGN (type_record, repr.align * 8);
+      TYPE_USER_ALIGN (type_record) = 1;
+    }
+
+  std::string named_struct_str
+    = type.get_ident ().path.get () + type.subst_as_string ();
+  translated = ctx->get_backend ()->named_type (named_struct_str, type_record,
+						type.get_ident ().locus);
+}
+
+void
+TyTyResolveCompile::visit (const TyTy::TupleType &type)
+{
+  if (type.num_fields () == 0)
+    {
+      translated = ctx->get_backend ()->unit_type ();
+      return;
+    }
+
+  // create implicit struct
+  std::vector<Backend::typed_identifier> fields;
+  for (size_t i = 0; i < type.num_fields (); i++)
+    {
+      TyTy::BaseType *field = type.get_field (i);
+      tree compiled_field_ty = TyTyResolveCompile::compile (ctx, field);
+
+      // rustc uses the convention __N, where N is an integer, to
+      // name the fields of a tuple.  We follow this as well,
+      // because this is used by GDB.  One further reason to prefer
+      // this, rather than simply emitting the integer, is that this
+      // approach makes it simpler to use a C-only debugger, or
+      // GDB's C mode, when debugging Rust.
+      Backend::typed_identifier f ("__" + std::to_string (i), compiled_field_ty,
+				   ctx->get_mappings ()->lookup_location (
+				     type.get_ty_ref ()));
+      fields.push_back (std::move (f));
+    }
+
+  tree struct_type_record = ctx->get_backend ()->struct_type (fields);
+  translated
+    = ctx->get_backend ()->named_type (type.as_string (), struct_type_record,
+				       type.get_ident ().locus);
+}
+
+void
+TyTyResolveCompile::visit (const TyTy::ArrayType &type)
+{
+  tree element_type
+    = TyTyResolveCompile::compile (ctx, type.get_element_type ());
+  tree capacity_expr = CompileExpr::Compile (&type.get_capacity_expr (), ctx);
+  tree folded_capacity_expr = fold_expr (capacity_expr);
+
+  translated
+    = ctx->get_backend ()->array_type (element_type, folded_capacity_expr);
+}
+
+void
+TyTyResolveCompile::visit (const TyTy::SliceType &type)
+{
+  tree type_record = create_slice_type_record (type);
+
+  std::string named_struct_str
+    = std::string ("[") + type.get_element_type ()->get_name () + "]";
+  translated = ctx->get_backend ()->named_type (named_struct_str, type_record,
+						type.get_ident ().locus);
+}
+
+void
+TyTyResolveCompile::visit (const TyTy::BoolType &type)
+{
+  translated
+    = ctx->get_backend ()->named_type ("bool",
+				       ctx->get_backend ()->bool_type (),
+				       Linemap::predeclared_location ());
+}
+
+void
+TyTyResolveCompile::visit (const TyTy::IntType &type)
+{
+  switch (type.get_int_kind ())
+    {
+    case TyTy::IntType::I8:
+      translated = ctx->get_backend ()->named_type (
+	"i8", ctx->get_backend ()->integer_type (false, 8),
+	Linemap::predeclared_location ());
+      return;
+
+    case TyTy::IntType::I16:
+      translated = ctx->get_backend ()->named_type (
+	"i16", ctx->get_backend ()->integer_type (false, 16),
+	Linemap::predeclared_location ());
+      return;
+
+    case TyTy::IntType::I32:
+      translated = ctx->get_backend ()->named_type (
+	"i32", ctx->get_backend ()->integer_type (false, 32),
+	Linemap::predeclared_location ());
+      return;
+
+    case TyTy::IntType::I64:
+      translated = ctx->get_backend ()->named_type (
+	"i64", ctx->get_backend ()->integer_type (false, 64),
+	Linemap::predeclared_location ());
+      return;
+
+    case TyTy::IntType::I128:
+      translated = ctx->get_backend ()->named_type (
+	"i128", ctx->get_backend ()->integer_type (false, 128),
+	Linemap::predeclared_location ());
+      return;
+    }
+}
+
+void
+TyTyResolveCompile::visit (const TyTy::UintType &type)
+{
+  switch (type.get_uint_kind ())
+    {
+    case TyTy::UintType::U8:
+      translated = ctx->get_backend ()->named_type (
+	"u8", ctx->get_backend ()->integer_type (true, 8),
+	Linemap::predeclared_location ());
+      return;
+
+    case TyTy::UintType::U16:
+      translated = ctx->get_backend ()->named_type (
+	"u16", ctx->get_backend ()->integer_type (true, 16),
+	Linemap::predeclared_location ());
+      return;
+
+    case TyTy::UintType::U32:
+      translated = ctx->get_backend ()->named_type (
+	"u32", ctx->get_backend ()->integer_type (true, 32),
+	Linemap::predeclared_location ());
+      return;
+
+    case TyTy::UintType::U64:
+      translated = ctx->get_backend ()->named_type (
+	"u64", ctx->get_backend ()->integer_type (true, 64),
+	Linemap::predeclared_location ());
+      return;
+
+    case TyTy::UintType::U128:
+      translated = ctx->get_backend ()->named_type (
+	"u128", ctx->get_backend ()->integer_type (true, 128),
+	Linemap::predeclared_location ());
+      return;
+    }
+}
+
+void
+TyTyResolveCompile::visit (const TyTy::FloatType &type)
+{
+  switch (type.get_float_kind ())
+    {
+    case TyTy::FloatType::F32:
+      translated
+	= ctx->get_backend ()->named_type ("f32",
+					   ctx->get_backend ()->float_type (32),
+					   Linemap::predeclared_location ());
+      return;
+
+    case TyTy::FloatType::F64:
+      translated
+	= ctx->get_backend ()->named_type ("f64",
+					   ctx->get_backend ()->float_type (64),
+					   Linemap::predeclared_location ());
+      return;
+    }
+}
+
+void
+TyTyResolveCompile::visit (const TyTy::USizeType &type)
+{
+  translated = ctx->get_backend ()->named_type (
+    "usize",
+    ctx->get_backend ()->integer_type (
+      true, ctx->get_backend ()->get_pointer_size ()),
+    Linemap::predeclared_location ());
+}
+
+void
+TyTyResolveCompile::visit (const TyTy::ISizeType &type)
+{
+  translated = ctx->get_backend ()->named_type (
+    "isize",
+    ctx->get_backend ()->integer_type (
+      false, ctx->get_backend ()->get_pointer_size ()),
+    Linemap::predeclared_location ());
+}
+
+void
+TyTyResolveCompile::visit (const TyTy::CharType &type)
+{
+  translated
+    = ctx->get_backend ()->named_type ("char",
+				       ctx->get_backend ()->wchar_type (),
+				       Linemap::predeclared_location ());
+}
+
+void
+TyTyResolveCompile::visit (const TyTy::ReferenceType &type)
+{
+  const TyTy::SliceType *slice = nullptr;
+  const TyTy::StrType *str = nullptr;
+  if (type.is_dyn_slice_type (&slice))
+    {
+      tree type_record = create_slice_type_record (*slice);
+      std::string dyn_slice_type_str
+	= std::string (type.is_mutable () ? "&mut " : "&") + "["
+	  + slice->get_element_type ()->get_name () + "]";
+
+      translated
+	= ctx->get_backend ()->named_type (dyn_slice_type_str, type_record,
+					   slice->get_locus ());
+
+      return;
+    }
+  else if (type.is_dyn_str_type (&str))
+    {
+      tree type_record = create_str_type_record (*str);
+      std::string dyn_str_type_str
+	= std::string (type.is_mutable () ? "&mut " : "&") + "str";
+
+      translated
+	= ctx->get_backend ()->named_type (dyn_str_type_str, type_record,
+					   str->get_locus ());
+
+      return;
+    }
+
+  tree base_compiled_type
+    = TyTyResolveCompile::compile (ctx, type.get_base (), trait_object_mode);
+  if (type.is_mutable ())
+    {
+      translated = ctx->get_backend ()->reference_type (base_compiled_type);
+    }
+  else
+    {
+      auto base = ctx->get_backend ()->immutable_type (base_compiled_type);
+      translated = ctx->get_backend ()->reference_type (base);
+    }
+}
+
+void
+TyTyResolveCompile::visit (const TyTy::PointerType &type)
+{
+  const TyTy::SliceType *slice = nullptr;
+  const TyTy::StrType *str = nullptr;
+  if (type.is_dyn_slice_type (&slice))
+    {
+      tree type_record = create_slice_type_record (*slice);
+      std::string dyn_slice_type_str
+	= std::string (type.is_mutable () ? "*mut " : "*const ") + "["
+	  + slice->get_element_type ()->get_name () + "]";
+
+      translated
+	= ctx->get_backend ()->named_type (dyn_slice_type_str, type_record,
+					   slice->get_locus ());
+
+      return;
+    }
+  else if (type.is_dyn_str_type (&str))
+    {
+      tree type_record = create_str_type_record (*str);
+      std::string dyn_str_type_str
+	= std::string (type.is_mutable () ? "*mut " : "*const ") + "str";
+
+      translated
+	= ctx->get_backend ()->named_type (dyn_str_type_str, type_record,
+					   str->get_locus ());
+
+      return;
+    }
+
+  tree base_compiled_type
+    = TyTyResolveCompile::compile (ctx, type.get_base (), trait_object_mode);
+  if (type.is_mutable ())
+    {
+      translated = ctx->get_backend ()->pointer_type (base_compiled_type);
+    }
+  else
+    {
+      auto base = ctx->get_backend ()->immutable_type (base_compiled_type);
+      translated = ctx->get_backend ()->pointer_type (base);
+    }
+}
+
+void
+TyTyResolveCompile::visit (const TyTy::StrType &type)
+{
+  tree raw_str = create_str_type_record (type);
+  translated
+    = ctx->get_backend ()->named_type ("str", raw_str,
+				       Linemap::predeclared_location ());
+}
+
+void
+TyTyResolveCompile::visit (const TyTy::NeverType &)
+{
+  translated = ctx->get_backend ()->unit_type ();
+}
+
+void
+TyTyResolveCompile::visit (const TyTy::DynamicObjectType &type)
+{
+  if (trait_object_mode)
+    {
+      translated = ctx->get_backend ()->integer_type (
+	true, ctx->get_backend ()->get_pointer_size ());
+      return;
+    }
+
+  // create implicit struct
+  auto items = type.get_object_items ();
+  std::vector<Backend::typed_identifier> fields;
+
+  tree uint = ctx->get_backend ()->integer_type (
+    true, ctx->get_backend ()->get_pointer_size ());
+  tree uintptr_ty = build_pointer_type (uint);
+
+  Backend::typed_identifier f ("pointer", uintptr_ty,
+			       ctx->get_mappings ()->lookup_location (
+				 type.get_ty_ref ()));
+  fields.push_back (std::move (f));
+
+  tree vtable_size = build_int_cst (size_type_node, items.size ());
+  tree vtable_type = ctx->get_backend ()->array_type (uintptr_ty, vtable_size);
+  Backend::typed_identifier vtf ("vtable", vtable_type,
+				 ctx->get_mappings ()->lookup_location (
+				   type.get_ty_ref ()));
+  fields.push_back (std::move (vtf));
+
+  tree type_record = ctx->get_backend ()->struct_type (fields);
+  translated = ctx->get_backend ()->named_type (type.get_name (), type_record,
+						type.get_ident ().locus);
+}
+
+tree
+TyTyResolveCompile::create_slice_type_record (const TyTy::SliceType &type)
+{
+  // lookup usize
+  TyTy::BaseType *usize = nullptr;
+  bool ok = ctx->get_tyctx ()->lookup_builtin ("usize", &usize);
+  rust_assert (ok);
+
+  tree element_type
+    = TyTyResolveCompile::compile (ctx, type.get_element_type ());
+  tree data_field_ty = build_pointer_type (element_type);
+  Backend::typed_identifier data_field ("data", data_field_ty,
+					type.get_locus ());
+
+  tree len_field_ty = TyTyResolveCompile::compile (ctx, usize);
+  Backend::typed_identifier len_field ("len", len_field_ty, type.get_locus ());
+
+  tree record = ctx->get_backend ()->struct_type ({data_field, len_field});
+  SLICE_FLAG (record) = 1;
+  TYPE_MAIN_VARIANT (record) = ctx->insert_main_variant (record);
+
+  return record;
+}
+
+tree
+TyTyResolveCompile::create_str_type_record (const TyTy::StrType &type)
+{
+  // lookup usize
+  TyTy::BaseType *usize = nullptr;
+  bool ok = ctx->get_tyctx ()->lookup_builtin ("usize", &usize);
+  rust_assert (ok);
+
+  tree char_ptr = build_pointer_type (char_type_node);
+  tree const_char_type = build_qualified_type (char_ptr, TYPE_QUAL_CONST);
+
+  tree element_type = const_char_type;
+  tree data_field_ty = build_pointer_type (element_type);
+  Backend::typed_identifier data_field ("data", data_field_ty,
+					type.get_locus ());
+
+  tree len_field_ty = TyTyResolveCompile::compile (ctx, usize);
+  Backend::typed_identifier len_field ("len", len_field_ty, type.get_locus ());
+
+  tree record = ctx->get_backend ()->struct_type ({data_field, len_field});
+  SLICE_FLAG (record) = 1;
+  TYPE_MAIN_VARIANT (record) = ctx->insert_main_variant (record);
+
+  return record;
+}
+
+} // namespace Compile
+} // namespace Rust
diff --git a/gcc/rust/backend/rust-compile-type.h b/gcc/rust/backend/rust-compile-type.h
new file mode 100644
index 00000000000..b52fd71bf6b
--- /dev/null
+++ b/gcc/rust/backend/rust-compile-type.h
@@ -0,0 +1,79 @@
+// 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_COMPILE_TYPE
+#define RUST_COMPILE_TYPE
+
+#include "rust-compile-context.h"
+
+namespace Rust {
+namespace Compile {
+
+class TyTyResolveCompile : protected TyTy::TyConstVisitor
+{
+public:
+  static tree compile (Context *ctx, const TyTy::BaseType *ty,
+		       bool trait_object_mode = false);
+
+  static tree get_implicit_enumeral_node_type (Context *ctx);
+
+  void visit (const TyTy::InferType &) override;
+  void visit (const TyTy::ADTType &) override;
+  void visit (const TyTy::TupleType &) override;
+  void visit (const TyTy::FnType &) override;
+  void visit (const TyTy::FnPtr &) override;
+  void visit (const TyTy::ArrayType &) override;
+  void visit (const TyTy::SliceType &) override;
+  void visit (const TyTy::BoolType &) override;
+  void visit (const TyTy::IntType &) override;
+  void visit (const TyTy::UintType &) override;
+  void visit (const TyTy::FloatType &) override;
+  void visit (const TyTy::USizeType &) override;
+  void visit (const TyTy::ISizeType &) override;
+  void visit (const TyTy::ErrorType &) override;
+  void visit (const TyTy::CharType &) override;
+  void visit (const TyTy::ReferenceType &) override;
+  void visit (const TyTy::PointerType &) override;
+  void visit (const TyTy::ParamType &) override;
+  void visit (const TyTy::StrType &) override;
+  void visit (const TyTy::NeverType &) override;
+  void visit (const TyTy::PlaceholderType &) override;
+  void visit (const TyTy::ProjectionType &) override;
+  void visit (const TyTy::DynamicObjectType &) override;
+  void visit (const TyTy::ClosureType &) override;
+
+public:
+  static hashval_t type_hasher (tree type);
+
+protected:
+  tree create_slice_type_record (const TyTy::SliceType &type);
+  tree create_str_type_record (const TyTy::StrType &type);
+
+private:
+  TyTyResolveCompile (Context *ctx, bool trait_object_mode);
+
+  Context *ctx;
+  bool trait_object_mode;
+  tree translated;
+  int recurisve_ops;
+};
+
+} // namespace Compile
+} // namespace Rust
+
+#endif // RUST_COMPILE_TYPE
diff --git a/gcc/rust/backend/rust-compile-var-decl.h b/gcc/rust/backend/rust-compile-var-decl.h
new file mode 100644
index 00000000000..e2ee05b8163
--- /dev/null
+++ b/gcc/rust/backend/rust-compile-var-decl.h
@@ -0,0 +1,95 @@
+// 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_COMPILE_VAR_DECL
+#define RUST_COMPILE_VAR_DECL
+
+#include "rust-compile-base.h"
+#include "rust-hir-visitor.h"
+
+namespace Rust {
+namespace Compile {
+
+class CompileVarDecl : public HIRCompileBase, public HIR::HIRPatternVisitor
+{
+  using HIR::HIRPatternVisitor::visit;
+
+public:
+  static ::Bvariable *compile (tree fndecl, tree translated_type,
+			       HIR::Pattern *pattern, Context *ctx)
+  {
+    CompileVarDecl compiler (ctx, fndecl, translated_type);
+    pattern->accept_vis (compiler);
+    return compiler.compiled_variable;
+  }
+
+  void visit (HIR::IdentifierPattern &pattern) override
+  {
+    if (!pattern.is_mut ())
+      translated_type = ctx->get_backend ()->immutable_type (translated_type);
+
+    compiled_variable
+      = ctx->get_backend ()->local_variable (fndecl, pattern.get_identifier (),
+					     translated_type, NULL /*decl_var*/,
+					     pattern.get_locus ());
+
+    HirId stmt_id = pattern.get_pattern_mappings ().get_hirid ();
+    ctx->insert_var_decl (stmt_id, compiled_variable);
+  }
+
+  void visit (HIR::WildcardPattern &pattern) override
+  {
+    translated_type = ctx->get_backend ()->immutable_type (translated_type);
+
+    compiled_variable
+      = ctx->get_backend ()->local_variable (fndecl, "_", translated_type,
+					     NULL /*decl_var*/,
+					     pattern.get_locus ());
+
+    HirId stmt_id = pattern.get_pattern_mappings ().get_hirid ();
+    ctx->insert_var_decl (stmt_id, compiled_variable);
+  }
+
+  // Empty visit for unused Pattern HIR nodes.
+  void visit (HIR::GroupedPattern &) override {}
+  void visit (HIR::LiteralPattern &) override {}
+  void visit (HIR::PathInExpression &) override {}
+  void visit (HIR::QualifiedPathInExpression &) override {}
+  void visit (HIR::RangePattern &) override {}
+  void visit (HIR::ReferencePattern &) override {}
+  void visit (HIR::SlicePattern &) override {}
+  void visit (HIR::StructPattern &) override {}
+  void visit (HIR::TuplePattern &) override {}
+  void visit (HIR::TupleStructPattern &) override {}
+
+private:
+  CompileVarDecl (Context *ctx, tree fndecl, tree translated_type)
+    : HIRCompileBase (ctx), fndecl (fndecl), translated_type (translated_type),
+      compiled_variable (ctx->get_backend ()->error_variable ())
+  {}
+
+  tree fndecl;
+  tree translated_type;
+
+  Bvariable *compiled_variable;
+};
+
+} // namespace Compile
+} // namespace Rust
+
+#endif // RUST_COMPILE_VAR_DECL
diff --git a/gcc/rust/backend/rust-compile.cc b/gcc/rust/backend/rust-compile.cc
new file mode 100644
index 00000000000..0ccb98d9e12
--- /dev/null
+++ b/gcc/rust/backend/rust-compile.cc
@@ -0,0 +1,414 @@
+// 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/>.
+
+#include "rust-compile.h"
+#include "rust-compile-item.h"
+#include "rust-compile-implitem.h"
+#include "rust-compile-expr.h"
+#include "rust-compile-struct-field-expr.h"
+#include "rust-compile-stmt.h"
+#include "rust-hir-trait-resolve.h"
+#include "rust-hir-path-probe.h"
+#include "rust-hir-type-bounds.h"
+#include "rust-hir-dot-operator.h"
+#include "rust-compile-block.h"
+
+namespace Rust {
+namespace Compile {
+
+CompileCrate::CompileCrate (HIR::Crate &crate, Context *ctx)
+  : crate (crate), ctx (ctx)
+{}
+
+CompileCrate::~CompileCrate () {}
+
+void
+CompileCrate::Compile (HIR::Crate &crate, Context *ctx)
+{
+  CompileCrate c (crate, ctx);
+  c.go ();
+}
+
+void
+CompileCrate::go ()
+{
+  for (auto &item : crate.items)
+    CompileItem::compile (item.get (), ctx);
+}
+
+// Shared methods in compilation
+
+tree
+HIRCompileBase::coercion_site (HirId id, tree rvalue,
+			       const TyTy::BaseType *rval,
+			       const TyTy::BaseType *lval,
+			       Location lvalue_locus, Location rvalue_locus)
+{
+  std::vector<Resolver::Adjustment> *adjustments = nullptr;
+  bool ok = ctx->get_tyctx ()->lookup_autoderef_mappings (id, &adjustments);
+  if (ok)
+    {
+      rvalue = resolve_adjustements (*adjustments, rvalue, rvalue_locus);
+    }
+
+  return coercion_site1 (rvalue, rval, lval, lvalue_locus, rvalue_locus);
+}
+
+tree
+HIRCompileBase::coercion_site1 (tree rvalue, const TyTy::BaseType *rval,
+				const TyTy::BaseType *lval,
+				Location lvalue_locus, Location rvalue_locus)
+{
+  if (rvalue == error_mark_node)
+    return error_mark_node;
+
+  const TyTy::BaseType *actual = rval->destructure ();
+  const TyTy::BaseType *expected = lval->destructure ();
+
+  if (expected->get_kind () == TyTy::TypeKind::REF)
+    {
+      // this is a dyn object
+      if (SLICE_TYPE_P (TREE_TYPE (rvalue)))
+	{
+	  return rvalue;
+	}
+
+      // bad coercion... of something to a reference
+      if (actual->get_kind () != TyTy::TypeKind::REF)
+	return error_mark_node;
+
+      const TyTy::ReferenceType *exp
+	= static_cast<const TyTy::ReferenceType *> (expected);
+      const TyTy::ReferenceType *act
+	= static_cast<const TyTy::ReferenceType *> (actual);
+
+      tree deref_rvalue = indirect_expression (rvalue, rvalue_locus);
+      tree coerced
+	= coercion_site1 (deref_rvalue, act->get_base (), exp->get_base (),
+			  lvalue_locus, rvalue_locus);
+      if (exp->is_dyn_object () && SLICE_TYPE_P (TREE_TYPE (coerced)))
+	return coerced;
+
+      return address_expression (coerced, rvalue_locus);
+    }
+  else if (expected->get_kind () == TyTy::TypeKind::POINTER)
+    {
+      // this is a dyn object
+      if (SLICE_TYPE_P (TREE_TYPE (rvalue)))
+	{
+	  return rvalue;
+	}
+
+      // bad coercion... of something to a reference
+      bool valid_coercion = actual->get_kind () == TyTy::TypeKind::REF
+			    || actual->get_kind () == TyTy::TypeKind::POINTER;
+      if (!valid_coercion)
+	return error_mark_node;
+
+      const TyTy::ReferenceType *exp
+	= static_cast<const TyTy::ReferenceType *> (expected);
+
+      TyTy::BaseType *actual_base = nullptr;
+      if (actual->get_kind () == TyTy::TypeKind::REF)
+	{
+	  const TyTy::ReferenceType *act
+	    = static_cast<const TyTy::ReferenceType *> (actual);
+
+	  actual_base = act->get_base ();
+	}
+      else if (actual->get_kind () == TyTy::TypeKind::POINTER)
+	{
+	  const TyTy::PointerType *act
+	    = static_cast<const TyTy::PointerType *> (actual);
+
+	  actual_base = act->get_base ();
+	}
+      rust_assert (actual_base != nullptr);
+
+      tree deref_rvalue = indirect_expression (rvalue, rvalue_locus);
+      tree coerced
+	= coercion_site1 (deref_rvalue, actual_base, exp->get_base (),
+			  lvalue_locus, rvalue_locus);
+
+      if (exp->is_dyn_object () && SLICE_TYPE_P (TREE_TYPE (coerced)))
+	return coerced;
+
+      return address_expression (coerced, rvalue_locus);
+    }
+  else if (expected->get_kind () == TyTy::TypeKind::ARRAY)
+    {
+      if (actual->get_kind () != TyTy::TypeKind::ARRAY)
+	return error_mark_node;
+
+      tree tree_rval_type = TyTyResolveCompile::compile (ctx, actual);
+      tree tree_lval_type = TyTyResolveCompile::compile (ctx, expected);
+      if (!verify_array_capacities (tree_lval_type, tree_rval_type,
+				    lvalue_locus, rvalue_locus))
+	return error_mark_node;
+    }
+  else if (expected->get_kind () == TyTy::TypeKind::SLICE)
+    {
+      // bad coercion
+      bool valid_coercion = actual->get_kind () == TyTy::TypeKind::SLICE
+			    || actual->get_kind () == TyTy::TypeKind::ARRAY;
+      if (!valid_coercion)
+	return error_mark_node;
+
+      // nothing to do here
+      if (actual->get_kind () == TyTy::TypeKind::SLICE)
+	return rvalue;
+
+      // return an unsized coercion
+      Resolver::Adjustment unsize_adj (
+	Resolver::Adjustment::AdjustmentType::UNSIZE, actual, expected);
+      return resolve_unsized_adjustment (unsize_adj, rvalue, rvalue_locus);
+    }
+
+  return rvalue;
+}
+
+tree
+HIRCompileBase::coerce_to_dyn_object (tree compiled_ref,
+				      const TyTy::BaseType *actual,
+				      const TyTy::DynamicObjectType *ty,
+				      Location locus)
+{
+  tree dynamic_object = TyTyResolveCompile::compile (ctx, ty);
+  tree dynamic_object_fields = TYPE_FIELDS (dynamic_object);
+  tree vtable_field = DECL_CHAIN (dynamic_object_fields);
+  rust_assert (TREE_CODE (TREE_TYPE (vtable_field)) == ARRAY_TYPE);
+
+  //' this assumes ordering and current the structure is
+  // __trait_object_ptr
+  // [list of function ptrs]
+
+  std::vector<std::pair<Resolver::TraitReference *, HIR::ImplBlock *>>
+    probed_bounds_for_receiver = Resolver::TypeBoundsProbe::Probe (actual);
+
+  tree address_of_compiled_ref = null_pointer_node;
+  if (!actual->is_unit ())
+    address_of_compiled_ref = address_expression (compiled_ref, locus);
+
+  std::vector<tree> vtable_ctor_elems;
+  std::vector<unsigned long> vtable_ctor_idx;
+  unsigned long i = 0;
+  for (auto &bound : ty->get_object_items ())
+    {
+      const Resolver::TraitItemReference *item = bound.first;
+      const TyTy::TypeBoundPredicate *predicate = bound.second;
+
+      auto address = compute_address_for_trait_item (item, predicate,
+						     probed_bounds_for_receiver,
+						     actual, actual, locus);
+      vtable_ctor_elems.push_back (address);
+      vtable_ctor_idx.push_back (i++);
+    }
+
+  tree vtable_ctor = ctx->get_backend ()->array_constructor_expression (
+    TREE_TYPE (vtable_field), vtable_ctor_idx, vtable_ctor_elems, locus);
+
+  std::vector<tree> dyn_ctor = {address_of_compiled_ref, vtable_ctor};
+  return ctx->get_backend ()->constructor_expression (dynamic_object, false,
+						      dyn_ctor, -1, locus);
+}
+
+tree
+HIRCompileBase::compute_address_for_trait_item (
+  const Resolver::TraitItemReference *ref,
+  const TyTy::TypeBoundPredicate *predicate,
+  std::vector<std::pair<Resolver::TraitReference *, HIR::ImplBlock *>>
+    &receiver_bounds,
+  const TyTy::BaseType *receiver, const TyTy::BaseType *root, Location locus)
+{
+  // There are two cases here one where its an item which has an implementation
+  // within a trait-impl-block. Then there is the case where there is a default
+  // implementation for this within the trait.
+  //
+  // The awkward part here is that this might be a generic trait and we need to
+  // figure out the correct monomorphized type for this so we can resolve the
+  // address of the function , this is stored as part of the
+  // type-bound-predicate
+  //
+  // Algo:
+  // check if there is an impl-item for this trait-item-ref first
+  // else assert that the trait-item-ref has an implementation
+
+  TyTy::TypeBoundPredicateItem predicate_item
+    = predicate->lookup_associated_item (ref->get_identifier ());
+  rust_assert (!predicate_item.is_error ());
+
+  // this is the expected end type
+  TyTy::BaseType *trait_item_type = predicate_item.get_tyty_for_receiver (root);
+  rust_assert (trait_item_type->get_kind () == TyTy::TypeKind::FNDEF);
+  TyTy::FnType *trait_item_fntype
+    = static_cast<TyTy::FnType *> (trait_item_type);
+
+  // find impl-block for this trait-item-ref
+  HIR::ImplBlock *associated_impl_block = nullptr;
+  const Resolver::TraitReference *predicate_trait_ref = predicate->get ();
+  for (auto &item : receiver_bounds)
+    {
+      Resolver::TraitReference *trait_ref = item.first;
+      HIR::ImplBlock *impl_block = item.second;
+      if (predicate_trait_ref->is_equal (*trait_ref))
+	{
+	  associated_impl_block = impl_block;
+	  break;
+	}
+    }
+
+  // FIXME this probably should just return error_mark_node but this helps
+  // debug for now since we are wrongly returning early on type-resolution
+  // failures, until we take advantage of more error types and error_mark_node
+  rust_assert (associated_impl_block != nullptr);
+
+  // lookup self for the associated impl
+  std::unique_ptr<HIR::Type> &self_type_path
+    = associated_impl_block->get_type ();
+  TyTy::BaseType *self = nullptr;
+  bool ok = ctx->get_tyctx ()->lookup_type (
+    self_type_path->get_mappings ().get_hirid (), &self);
+  rust_assert (ok);
+
+  // lookup the predicate item from the self
+  TyTy::TypeBoundPredicate *self_bound = nullptr;
+  for (auto &bound : self->get_specified_bounds ())
+    {
+      const Resolver::TraitReference *bound_ref = bound.get ();
+      const Resolver::TraitReference *specified_ref = predicate->get ();
+      if (bound_ref->is_equal (*specified_ref))
+	{
+	  self_bound = &bound;
+	  break;
+	}
+    }
+  rust_assert (self_bound != nullptr);
+
+  // lookup the associated item from the associated impl block
+  TyTy::TypeBoundPredicateItem associated_self_item
+    = self_bound->lookup_associated_item (ref->get_identifier ());
+  rust_assert (!associated_self_item.is_error ());
+
+  TyTy::BaseType *mono1 = associated_self_item.get_tyty_for_receiver (self);
+  rust_assert (mono1 != nullptr);
+  rust_assert (mono1->get_kind () == TyTy::TypeKind::FNDEF);
+  TyTy::FnType *assocated_item_ty1 = static_cast<TyTy::FnType *> (mono1);
+
+  // Lookup the impl-block for the associated impl_item if it exists
+  HIR::Function *associated_function = nullptr;
+  for (auto &impl_item : associated_impl_block->get_impl_items ())
+    {
+      bool is_function = impl_item->get_impl_item_type ()
+			 == HIR::ImplItem::ImplItemType::FUNCTION;
+      if (!is_function)
+	continue;
+
+      HIR::Function *fn = static_cast<HIR::Function *> (impl_item.get ());
+      bool found_associated_item
+	= fn->get_function_name ().compare (ref->get_identifier ()) == 0;
+      if (found_associated_item)
+	associated_function = fn;
+    }
+
+  // we found an impl_item for this
+  if (associated_function != nullptr)
+    {
+      // lookup the associated type for this item
+      TyTy::BaseType *lookup = nullptr;
+      bool ok = ctx->get_tyctx ()->lookup_type (
+	associated_function->get_mappings ().get_hirid (), &lookup);
+      rust_assert (ok);
+      rust_assert (lookup->get_kind () == TyTy::TypeKind::FNDEF);
+      TyTy::FnType *lookup_fntype = static_cast<TyTy::FnType *> (lookup);
+
+      if (lookup_fntype->needs_substitution ())
+	{
+	  TyTy::SubstitutionArgumentMappings mappings
+	    = assocated_item_ty1->solve_missing_mappings_from_this (
+	      *trait_item_fntype, *lookup_fntype);
+	  lookup_fntype = lookup_fntype->handle_substitions (mappings);
+	}
+
+      return CompileInherentImplItem::Compile (associated_function, ctx,
+					       lookup_fntype, true, locus);
+    }
+
+  // we can only compile trait-items with a body
+  bool trait_item_has_definition = ref->is_optional ();
+  rust_assert (trait_item_has_definition);
+
+  HIR::TraitItem *trait_item = ref->get_hir_trait_item ();
+  return CompileTraitItem::Compile (trait_item, ctx, trait_item_fntype, true,
+				    locus);
+}
+
+bool
+HIRCompileBase::verify_array_capacities (tree ltype, tree rtype,
+					 Location lvalue_locus,
+					 Location rvalue_locus)
+{
+  rust_assert (ltype != NULL_TREE);
+  rust_assert (rtype != NULL_TREE);
+
+  // lets just return ok as other errors have already occurred
+  if (ltype == error_mark_node || rtype == error_mark_node)
+    return true;
+
+  tree ltype_domain = TYPE_DOMAIN (ltype);
+  if (!ltype_domain)
+    return false;
+
+  if (!TREE_CONSTANT (TYPE_MAX_VALUE (ltype_domain)))
+    return false;
+
+  unsigned HOST_WIDE_INT ltype_length
+    = wi::ext (wi::to_offset (TYPE_MAX_VALUE (ltype_domain))
+		 - wi::to_offset (TYPE_MIN_VALUE (ltype_domain)) + 1,
+	       TYPE_PRECISION (TREE_TYPE (ltype_domain)),
+	       TYPE_SIGN (TREE_TYPE (ltype_domain)))
+	.to_uhwi ();
+
+  tree rtype_domain = TYPE_DOMAIN (rtype);
+  if (!rtype_domain)
+    return false;
+
+  if (!TREE_CONSTANT (TYPE_MAX_VALUE (rtype_domain)))
+    return false;
+
+  unsigned HOST_WIDE_INT rtype_length
+    = wi::ext (wi::to_offset (TYPE_MAX_VALUE (rtype_domain))
+		 - wi::to_offset (TYPE_MIN_VALUE (rtype_domain)) + 1,
+	       TYPE_PRECISION (TREE_TYPE (rtype_domain)),
+	       TYPE_SIGN (TREE_TYPE (rtype_domain)))
+	.to_uhwi ();
+
+  if (ltype_length != rtype_length)
+    {
+      rust_error_at (
+	rvalue_locus,
+	"expected an array with a fixed size of " HOST_WIDE_INT_PRINT_UNSIGNED
+	" elements, found one with " HOST_WIDE_INT_PRINT_UNSIGNED " elements",
+	ltype_length, rtype_length);
+      return false;
+    }
+
+  return true;
+}
+
+} // namespace Compile
+} // namespace Rust
diff --git a/gcc/rust/backend/rust-compile.h b/gcc/rust/backend/rust-compile.h
new file mode 100644
index 00000000000..62ebac69cc1
--- /dev/null
+++ b/gcc/rust/backend/rust-compile.h
@@ -0,0 +1,47 @@
+// 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_COMPILE_H
+#define RUST_COMPILE_H
+
+#include "rust-system.h"
+#include "rust-hir-full.h"
+#include "rust-compile-context.h"
+
+namespace Rust {
+namespace Compile {
+
+class CompileCrate
+{
+public:
+  static void Compile (HIR::Crate &crate, Context *ctx);
+
+  ~CompileCrate ();
+
+private:
+  CompileCrate (HIR::Crate &crate, Context *ctx);
+  void go ();
+
+  HIR::Crate &crate;
+  Context *ctx;
+};
+
+} // namespace Compile
+} // namespace Rust
+
+#endif // RUST_COMPILE_H
diff --git a/gcc/rust/backend/rust-constexpr.cc b/gcc/rust/backend/rust-constexpr.cc
new file mode 100644
index 00000000000..53c6ef6a668
--- /dev/null
+++ b/gcc/rust/backend/rust-constexpr.cc
@@ -0,0 +1,441 @@
+// 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/>.
+
+#include "rust-constexpr.h"
+#include "rust-location.h"
+#include "rust-diagnostics.h"
+#include "rust-tree.h"
+
+#include "fold-const.h"
+#include "realmpfr.h"
+#include "convert.h"
+#include "print-tree.h"
+#include "gimplify.h"
+#include "tree-iterator.h"
+
+namespace Rust {
+namespace Compile {
+
+struct constexpr_global_ctx
+{
+  HOST_WIDE_INT constexpr_ops_count;
+
+  constexpr_global_ctx () : constexpr_ops_count (0) {}
+};
+
+struct constexpr_ctx
+{
+  constexpr_global_ctx *global;
+};
+
+static tree
+constant_value_1 (tree decl, bool strict_p, bool return_aggregate_cst_ok_p,
+		  bool unshare_p);
+tree
+decl_constant_value (tree decl, bool unshare_p);
+
+static void
+non_const_var_error (location_t loc, tree r);
+
+static tree
+constexpr_expression (const constexpr_ctx *ctx, tree);
+
+static tree
+constexpr_fn_retval (const constexpr_ctx *ctx, tree r);
+
+static tree
+eval_store_expression (const constexpr_ctx *ctx, tree r);
+
+static tree
+eval_call_expression (const constexpr_ctx *ctx, tree r);
+
+static tree
+eval_binary_expression (const constexpr_ctx *ctx, tree r);
+
+static tree
+get_function_named_in_call (tree t);
+
+tree
+fold_expr (tree expr)
+{
+  constexpr_global_ctx global_ctx;
+  constexpr_ctx ctx = {&global_ctx};
+
+  tree folded = constexpr_expression (&ctx, expr);
+  rust_assert (folded != NULL_TREE);
+  return folded;
+}
+
+static tree
+constexpr_expression (const constexpr_ctx *ctx, tree t)
+{
+  location_t loc = EXPR_LOCATION (t);
+
+  if (CONSTANT_CLASS_P (t))
+    {
+      if (TREE_OVERFLOW (t))
+	{
+	  error_at (loc, "overflow in constant expression");
+	  return t;
+	}
+
+      return t;
+    }
+
+  // Avoid excessively long constexpr evaluations
+  if (++ctx->global->constexpr_ops_count >= constexpr_ops_limit)
+    {
+      rust_error_at (
+	Location (loc),
+	"%<constexpr%> evaluation operation count exceeds limit of "
+	"%wd (use %<-fconstexpr-ops-limit=%> to increase the limit)",
+	constexpr_ops_limit);
+
+      return t;
+    }
+
+  tree r = t;
+  tree_code tcode = TREE_CODE (t);
+  switch (tcode)
+    {
+      case CONST_DECL: {
+	r = decl_constant_value (t, /*unshare_p=*/false);
+	if (TREE_CODE (r) == TARGET_EXPR
+	    && TREE_CODE (TARGET_EXPR_INITIAL (r)) == CONSTRUCTOR)
+	  r = TARGET_EXPR_INITIAL (r);
+	if (DECL_P (r))
+	  {
+	    non_const_var_error (loc, r);
+	    return r;
+	  }
+      }
+      break;
+
+    case POINTER_PLUS_EXPR:
+    case POINTER_DIFF_EXPR:
+    case PLUS_EXPR:
+    case MINUS_EXPR:
+    case MULT_EXPR:
+    case TRUNC_DIV_EXPR:
+    case CEIL_DIV_EXPR:
+    case FLOOR_DIV_EXPR:
+    case ROUND_DIV_EXPR:
+    case TRUNC_MOD_EXPR:
+    case CEIL_MOD_EXPR:
+    case ROUND_MOD_EXPR:
+    case RDIV_EXPR:
+    case EXACT_DIV_EXPR:
+    case MIN_EXPR:
+    case MAX_EXPR:
+    case LSHIFT_EXPR:
+    case RSHIFT_EXPR:
+    case LROTATE_EXPR:
+    case RROTATE_EXPR:
+    case BIT_IOR_EXPR:
+    case BIT_XOR_EXPR:
+    case BIT_AND_EXPR:
+    case TRUTH_XOR_EXPR:
+    case LT_EXPR:
+    case LE_EXPR:
+    case GT_EXPR:
+    case GE_EXPR:
+    case EQ_EXPR:
+    case NE_EXPR:
+    case SPACESHIP_EXPR:
+    case UNORDERED_EXPR:
+    case ORDERED_EXPR:
+    case UNLT_EXPR:
+    case UNLE_EXPR:
+    case UNGT_EXPR:
+    case UNGE_EXPR:
+    case UNEQ_EXPR:
+    case LTGT_EXPR:
+    case RANGE_EXPR:
+    case COMPLEX_EXPR:
+      r = eval_binary_expression (ctx, t);
+      break;
+
+    case CALL_EXPR:
+      r = eval_call_expression (ctx, t);
+      break;
+
+    case RETURN_EXPR:
+      rust_assert (TREE_OPERAND (t, 0) != NULL_TREE);
+      r = constexpr_expression (ctx, TREE_OPERAND (t, 0));
+      break;
+
+    case MODIFY_EXPR:
+      r = eval_store_expression (ctx, t);
+      break;
+
+    default:
+      break;
+    }
+
+  return r;
+}
+
+static tree
+eval_store_expression (const constexpr_ctx *ctx, tree t)
+{
+  tree init = TREE_OPERAND (t, 1);
+  if (TREE_CLOBBER_P (init))
+    /* Just ignore clobbers.  */
+    return void_node;
+
+  /* First we figure out where we're storing to.  */
+  tree target = TREE_OPERAND (t, 0);
+
+  tree type = TREE_TYPE (target);
+  bool preeval = SCALAR_TYPE_P (type) || TREE_CODE (t) == MODIFY_EXPR;
+  if (preeval)
+    {
+      /* Evaluate the value to be stored without knowing what object it will be
+	 stored in, so that any side-effects happen first.  */
+      init = fold_expr (init);
+    }
+
+  bool evaluated = false;
+  tree object = NULL_TREE;
+  for (tree probe = target; object == NULL_TREE;)
+    {
+      switch (TREE_CODE (probe))
+	{
+	default:
+	  if (evaluated)
+	    object = probe;
+	  else
+	    {
+	      probe = constexpr_expression (ctx, probe);
+	      evaluated = true;
+	    }
+	  break;
+	}
+    }
+
+  return init;
+}
+
+/* Subroutine of cxx_eval_constant_expression.
+ Like cxx_eval_unary_expression, except for binary expressions.  */
+static tree
+eval_binary_expression (const constexpr_ctx *ctx, tree t)
+{
+  tree orig_lhs = TREE_OPERAND (t, 0);
+  tree orig_rhs = TREE_OPERAND (t, 1);
+  tree lhs, rhs;
+
+  lhs = constexpr_expression (ctx, orig_lhs);
+  rhs = constexpr_expression (ctx, orig_rhs);
+
+  location_t loc = EXPR_LOCATION (t);
+  enum tree_code code = TREE_CODE (t);
+  tree type = TREE_TYPE (t);
+
+  return fold_binary_loc (loc, code, type, lhs, rhs);
+}
+
+// Subroutine of cxx_eval_constant_expression.
+// Evaluate the call expression tree T in the context of OLD_CALL expression
+// evaluation.
+static tree
+eval_call_expression (const constexpr_ctx *ctx, tree t)
+{
+  tree fun = get_function_named_in_call (t);
+  return constexpr_fn_retval (ctx, DECL_SAVED_TREE (fun));
+}
+
+// Subroutine of check_constexpr_fundef.  BODY is the body of a function
+// declared to be constexpr, or a sub-statement thereof.  Returns the
+// return value if suitable, error_mark_node for a statement not allowed in
+// a constexpr function, or NULL_TREE if no return value was found.
+static tree
+constexpr_fn_retval (const constexpr_ctx *ctx, tree body)
+{
+  switch (TREE_CODE (body))
+    {
+      case STATEMENT_LIST: {
+	tree expr = NULL_TREE;
+	for (tree stmt : tsi_range (body))
+	  {
+	    tree s = constexpr_fn_retval (ctx, stmt);
+	    if (s == error_mark_node)
+	      return error_mark_node;
+	    else if (s == NULL_TREE)
+	      /* Keep iterating.  */;
+	    else if (expr)
+	      /* Multiple return statements.  */
+	      return error_mark_node;
+	    else
+	      expr = s;
+	  }
+	return expr;
+      }
+
+    case RETURN_EXPR:
+      return constexpr_expression (ctx, body);
+
+      case DECL_EXPR: {
+	tree decl = DECL_EXPR_DECL (body);
+	if (TREE_CODE (decl) == USING_DECL
+	    /* Accept __func__, __FUNCTION__, and __PRETTY_FUNCTION__.  */
+	    || DECL_ARTIFICIAL (decl))
+	  return NULL_TREE;
+	return error_mark_node;
+      }
+
+    case CLEANUP_POINT_EXPR:
+      return constexpr_fn_retval (ctx, TREE_OPERAND (body, 0));
+
+      case BIND_EXPR: {
+	tree b = BIND_EXPR_BODY (body);
+	return constexpr_fn_retval (ctx, b);
+      }
+      break;
+
+    default:
+      return error_mark_node;
+    }
+  return error_mark_node;
+}
+
+// Taken from cp/constexpr.cc
+//
+// If DECL is a scalar enumeration constant or variable with a
+// constant initializer, return the initializer (or, its initializers,
+// recursively); otherwise, return DECL.  If STRICT_P, the
+// initializer is only returned if DECL is a
+// constant-expression.  If RETURN_AGGREGATE_CST_OK_P, it is ok to
+// return an aggregate constant.  If UNSHARE_P, return an unshared
+// copy of the initializer.
+static tree
+constant_value_1 (tree decl, bool strict_p, bool return_aggregate_cst_ok_p,
+		  bool unshare_p)
+{
+  while (TREE_CODE (decl) == CONST_DECL)
+    {
+      tree init;
+      /* If DECL is a static data member in a template
+	 specialization, we must instantiate it here.  The
+	 initializer for the static data member is not processed
+	 until needed; we need it now.  */
+
+      init = DECL_INITIAL (decl);
+      if (init == error_mark_node)
+	{
+	  if (TREE_CODE (decl) == CONST_DECL)
+	    /* Treat the error as a constant to avoid cascading errors on
+	       excessively recursive template instantiation (c++/9335).  */
+	    return init;
+	  else
+	    return decl;
+	}
+
+      decl = init;
+    }
+  return unshare_p ? unshare_expr (decl) : decl;
+}
+
+// A more relaxed version of decl_really_constant_value, used by the
+// common C/C++ code.
+tree
+decl_constant_value (tree decl, bool unshare_p)
+{
+  return constant_value_1 (decl, /*strict_p=*/false,
+			   /*return_aggregate_cst_ok_p=*/true,
+			   /*unshare_p=*/unshare_p);
+}
+
+static void
+non_const_var_error (location_t loc, tree r)
+{
+  error_at (loc,
+	    "the value of %qD is not usable in a constant "
+	    "expression",
+	    r);
+  /* Avoid error cascade.  */
+  if (DECL_INITIAL (r) == error_mark_node)
+    return;
+
+  // more in cp/constexpr.cc
+}
+
+static tree
+get_callee (tree call)
+{
+  if (call == NULL_TREE)
+    return call;
+  else if (TREE_CODE (call) == CALL_EXPR)
+    return CALL_EXPR_FN (call);
+
+  return NULL_TREE;
+}
+
+// We have an expression tree T that represents a call, either CALL_EXPR
+// or AGGR_INIT_EXPR. If the call is lexically to a named function,
+// return the _DECL for that function.
+static tree
+get_function_named_in_call (tree t)
+{
+  tree fun = get_callee (t);
+  if (fun && TREE_CODE (fun) == ADDR_EXPR
+      && TREE_CODE (TREE_OPERAND (fun, 0)) == FUNCTION_DECL)
+    fun = TREE_OPERAND (fun, 0);
+  return fun;
+}
+
+// forked from gcc/cp/constexpr.cc maybe_constexpr_fn
+
+/* True if a function might be declared constexpr  */
+
+bool
+maybe_constexpr_fn (tree t)
+{
+  return (DECL_DECLARED_CONSTEXPR_P (t));
+}
+
+// forked from gcc/cp/constexpr.cc get_nth_callarg
+
+/* We have an expression tree T that represents a call, either CALL_EXPR.
+  Return the Nth argument.  */
+
+inline tree
+get_nth_callarg (tree t, int n)
+{
+  switch (TREE_CODE (t))
+    {
+    case CALL_EXPR:
+      return CALL_EXPR_ARG (t, n);
+
+    default:
+      gcc_unreachable ();
+      return NULL;
+    }
+}
+
+// forked from gcc/cp/constexpr.cc var_in_maybe_constexpr_fn
+
+/* True if T was declared in a function that might be constexpr: either a
+   function that was declared constexpr.  */
+
+bool
+var_in_maybe_constexpr_fn (tree t)
+{
+  return (DECL_FUNCTION_SCOPE_P (t) && maybe_constexpr_fn (DECL_CONTEXT (t)));
+}
+
+} // namespace Compile
+} // namespace Rust
diff --git a/gcc/rust/backend/rust-constexpr.h b/gcc/rust/backend/rust-constexpr.h
new file mode 100644
index 00000000000..3cfcec817a9
--- /dev/null
+++ b/gcc/rust/backend/rust-constexpr.h
@@ -0,0 +1,31 @@
+// 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_CONSTEXPR
+#define RUST_CONSTEXPR
+
+#include "rust-system.h"
+#include "tree.h"
+
+namespace Rust {
+namespace Compile {
+
+extern tree fold_expr (tree);
+
+} // namespace Compile
+} // namespace Rust
+
+#endif // RUST_CONSTEXPR
diff --git a/gcc/rust/backend/rust-mangle.cc b/gcc/rust/backend/rust-mangle.cc
new file mode 100644
index 00000000000..4d202078a70
--- /dev/null
+++ b/gcc/rust/backend/rust-mangle.cc
@@ -0,0 +1,307 @@
+#include "rust-mangle.h"
+#include "fnv-hash.h"
+#include "rust-base62.h"
+
+// FIXME: Rename those to legacy_*
+static const std::string kMangledSymbolPrefix = "_ZN";
+static const std::string kMangledSymbolDelim = "E";
+static const std::string kMangledGenericDelim = "$C$";
+static const std::string kMangledSubstBegin = "$LT$";
+static const std::string kMangledSubstEnd = "$GT$";
+static const std::string kMangledSpace = "$u20$";
+static const std::string kMangledRef = "$RF$";
+static const std::string kMangledPtr = "$BP$";
+static const std::string kMangledLeftSqParen = "$u5b$";	 // [
+static const std::string kMangledRightSqParen = "$u5d$"; // ]
+static const std::string kQualPathBegin = "_" + kMangledSubstBegin;
+static const std::string kMangledComma = "$C$";
+
+namespace Rust {
+namespace Compile {
+
+Mangler::MangleVersion Mangler::version = MangleVersion::LEGACY;
+
+static std::string
+legacy_mangle_name (const std::string &name)
+{
+  // example
+  //  <&T as core::fmt::Debug>::fmt:
+  //  _ZN42_$LT$$RF$T$u20$as$u20$core..fmt..Debug$GT$3fmt17h6dac924c0051eef7E
+  // replace all white space with $ and & with RF
+  //
+  // <example::Bar as example::A>::fooA:
+  // _ZN43_$LT$example..Bar$u20$as$u20$example..A$GT$4fooA17hfc615fa76c7db7a0E:
+  //
+  // core::ptr::const_ptr::<impl *const T>::cast:
+  // _ZN4core3ptr9const_ptr33_$LT$impl$u20$$BP$const$u20$T$GT$4cast17hb79f4617226f1d55E:
+  //
+  // core::ptr::const_ptr::<impl *const [T]>::as_ptr:
+  // _ZN4core3ptr9const_ptr43_$LT$impl$u20$$BP$const$u20$$u5b$T$u5d$$GT$6as_ptr17he16e0dcd9473b04fE:
+  //
+  // example::Foo<T>::new:
+  // _ZN7example12Foo$LT$T$GT$3new17h9a2aacb7fd783515E:
+  //
+  // <example::Identity as example::FnLike<&T,&T>>::call
+  // _ZN74_$LT$example..Identity$u20$as$u20$example..FnLike$LT$$RF$T$C$$RF$T$GT$$GT$4call17ha9ee58935895acb3E
+
+  std::string buffer;
+  for (size_t i = 0; i < name.size (); i++)
+    {
+      std::string m;
+      char c = name.at (i);
+
+      if (c == ' ')
+	m = kMangledSpace;
+      else if (c == '&')
+	m = kMangledRef;
+      else if (i == 0 && c == '<')
+	m = kQualPathBegin;
+      else if (c == '<')
+	m = kMangledSubstBegin;
+      else if (c == '>')
+	m = kMangledSubstEnd;
+      else if (c == '*')
+	m = kMangledPtr;
+      else if (c == '[')
+	m = kMangledLeftSqParen;
+      else if (c == ']')
+	m = kMangledRightSqParen;
+      else if (c == ',')
+	m = kMangledComma;
+      else if (c == ':')
+	{
+	  rust_assert (i + 1 < name.size ());
+	  rust_assert (name.at (i + 1) == ':');
+	  i++;
+	  m = "..";
+	}
+      else
+	m.push_back (c);
+
+      buffer += m;
+    }
+
+  return std::to_string (buffer.size ()) + buffer;
+}
+
+static std::string
+legacy_mangle_canonical_path (const Resolver::CanonicalPath &path)
+{
+  std::string buffer;
+  for (size_t i = 0; i < path.size (); i++)
+    {
+      auto &seg = path.get_seg_at (i);
+      buffer += legacy_mangle_name (seg.second);
+    }
+  return buffer;
+}
+
+// rustc uses a sip128 hash for legacy mangling, but an fnv 128 was quicker to
+// implement for now
+static std::string
+legacy_hash (const std::string &fingerprint)
+{
+  Hash::FNV128 hasher;
+  hasher.write ((const unsigned char *) fingerprint.c_str (),
+		fingerprint.size ());
+
+  uint64_t hi, lo;
+  hasher.sum (&hi, &lo);
+
+  char hex[16 + 1];
+  memset (hex, 0, sizeof hex);
+  snprintf (hex, sizeof hex, "%08" PRIx64 "%08" PRIx64, lo, hi);
+
+  return "h" + std::string (hex, sizeof (hex) - 1);
+}
+
+static std::string
+v0_tuple_prefix (const TyTy::BaseType *ty)
+{
+  if (ty->is_unit ())
+    return "u";
+
+  // FIXME: ARTHUR: Add rest of algorithm
+  return "";
+}
+
+static std::string
+v0_numeric_prefix (const TyTy::BaseType *ty)
+{
+  static const std::map<std::string, std::string> num_prefixes = {
+    {"[i8]", "a"},    {"[u8]", "h"},	{"[i16]", "s"}, {"[u16]", "t"},
+    {"[i32]", "l"},   {"[u32]", "m"},	{"[i64]", "x"}, {"[u64]", "y"},
+    {"[isize]", "i"}, {"[usize]", "j"}, {"[f32]", "f"}, {"[f64]", "d"},
+  };
+
+  auto ty_kind = ty->get_kind ();
+  auto ty_str = ty->as_string ();
+  auto numeric_iter = num_prefixes.end ();
+
+  // Special numeric types
+  if (ty_kind == TyTy::TypeKind::ISIZE)
+    return "i";
+  else if (ty_kind == TyTy::TypeKind::USIZE)
+    return "j";
+
+  numeric_iter = num_prefixes.find (ty_str);
+  if (numeric_iter != num_prefixes.end ())
+    return numeric_iter->second;
+
+  return "";
+}
+
+static std::string
+v0_simple_type_prefix (const TyTy::BaseType *ty)
+{
+  switch (ty->get_kind ())
+    {
+    case TyTy::TypeKind::BOOL:
+      return "b";
+    case TyTy::TypeKind::CHAR:
+      return "c";
+    case TyTy::TypeKind::STR:
+      return "e";
+    case TyTy::TypeKind::NEVER:
+      return "z";
+
+      // Placeholder types
+    case TyTy::TypeKind::ERROR:	      // Fallthrough
+    case TyTy::TypeKind::INFER:	      // Fallthrough
+    case TyTy::TypeKind::PLACEHOLDER: // Fallthrough
+    case TyTy::TypeKind::PARAM:
+      // FIXME: TyTy::TypeKind::BOUND is also a valid variant in rustc
+      return "p";
+
+    case TyTy::TypeKind::TUPLE:
+      return v0_tuple_prefix (ty);
+
+    case TyTy::TypeKind::UINT:	// Fallthrough
+    case TyTy::TypeKind::INT:	// Fallthrough
+    case TyTy::TypeKind::FLOAT: // Fallthrough
+    case TyTy::TypeKind::ISIZE: // Fallthrough
+    case TyTy::TypeKind::USIZE: // Fallthrough
+      return v0_numeric_prefix (ty);
+
+    default:
+      return "";
+    }
+
+  gcc_unreachable ();
+}
+
+// Add an underscore-terminated base62 integer to the mangling string.
+// This corresponds to the `<base-62-number>` grammar in the v0 mangling RFC:
+//  - 0 is encoded as "_"
+//  - any other value is encoded as itself minus one in base 62, followed by
+//  "_"
+static void
+v0_add_integer_62 (std::string &mangled, uint64_t x)
+{
+  if (x > 0)
+    mangled.append (base62_integer (x - 1));
+
+  mangled.append ("_");
+}
+
+// Add a tag-prefixed base62 integer to the mangling string when the
+// integer is greater than 0:
+//  - 0 is encoded as "" (nothing)
+//  - any other value is encoded as <tag> + v0_add_integer_62(itself), that is
+//  <tag> + base62(itself - 1) + '_'
+static void
+v0_add_opt_integer_62 (std::string &mangled, std::string tag, uint64_t x)
+{
+  if (x > 0)
+    {
+      mangled.append (tag);
+      v0_add_integer_62 (mangled, x);
+    }
+}
+
+static void
+v0_add_disambiguator (std::string &mangled, uint64_t dis)
+{
+  v0_add_opt_integer_62 (mangled, "s", dis);
+}
+
+// Add an identifier to the mangled string. This corresponds to the
+// `<identifier>` grammar in the v0 mangling RFC.
+static void
+v0_add_identifier (std::string &mangled, const std::string &identifier)
+{
+  // FIXME: gccrs cannot handle unicode identifiers yet, so we never have to
+  // create mangling for unicode values for now. However, this is handled
+  // by the v0 mangling scheme. The grammar for unicode identifier is
+  // contained in <undisambiguated-identifier>, right under the <identifier>
+  // one. If the identifier contains unicode values, then an extra "u" needs
+  // to be added to the mangling string and `punycode` must be used to encode
+  // the characters.
+
+  mangled += std::to_string (identifier.size ());
+
+  // If the first character of the identifier is a digit or an underscore, we
+  // add an extra underscore
+  if (identifier[0] == '_')
+    mangled.append ("_");
+
+  mangled.append (identifier);
+}
+
+static std::string
+v0_type_prefix (const TyTy::BaseType *ty)
+{
+  auto ty_prefix = v0_simple_type_prefix (ty);
+  if (!ty_prefix.empty ())
+    return ty_prefix;
+
+  // FIXME: We need to fetch more type prefixes
+  gcc_unreachable ();
+}
+
+static std::string
+legacy_mangle_item (const TyTy::BaseType *ty,
+		    const Resolver::CanonicalPath &path)
+{
+  const std::string hash = legacy_hash (ty->as_string ());
+  const std::string hash_sig = legacy_mangle_name (hash);
+
+  return kMangledSymbolPrefix + legacy_mangle_canonical_path (path) + hash_sig
+	 + kMangledSymbolDelim;
+}
+
+static std::string
+v0_mangle_item (const TyTy::BaseType *ty, const Resolver::CanonicalPath &path)
+{
+  // we can get this from the canonical_path
+  auto mappings = Analysis::Mappings::get ();
+  std::string crate_name;
+  bool ok = mappings->get_crate_name (path.get_crate_num (), crate_name);
+  rust_assert (ok);
+
+  std::string mangled;
+  // FIXME: Add real algorithm once all pieces are implemented
+  auto ty_prefix = v0_type_prefix (ty);
+  v0_add_identifier (mangled, crate_name);
+  v0_add_disambiguator (mangled, 62);
+
+  gcc_unreachable ();
+}
+
+std::string
+Mangler::mangle_item (const TyTy::BaseType *ty,
+		      const Resolver::CanonicalPath &path) const
+{
+  switch (version)
+    {
+    case Mangler::MangleVersion::LEGACY:
+      return legacy_mangle_item (ty, path);
+    case Mangler::MangleVersion::V0:
+      return v0_mangle_item (ty, path);
+    default:
+      gcc_unreachable ();
+    }
+}
+
+} // namespace Compile
+} // namespace Rust
diff --git a/gcc/rust/backend/rust-mangle.h b/gcc/rust/backend/rust-mangle.h
new file mode 100644
index 00000000000..6d5a64f8bce
--- /dev/null
+++ b/gcc/rust/backend/rust-mangle.h
@@ -0,0 +1,52 @@
+// 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_MANGLE_H
+#define RUST_MANGLE_H
+
+#include "rust-system.h"
+#include "rust-tyty.h"
+
+namespace Rust {
+namespace Compile {
+
+class Mangler
+{
+public:
+  enum MangleVersion
+  {
+    // Values defined in rust/lang.opt
+    LEGACY = 0,
+    V0 = 1,
+  };
+
+  // this needs to support Legacy and V0 see github #429 or #305
+  std::string mangle_item (const TyTy::BaseType *ty,
+			   const Resolver::CanonicalPath &path) const;
+
+  static void set_mangling (int frust_mangling_value)
+  {
+    version = static_cast<MangleVersion> (frust_mangling_value);
+  }
+
+private:
+  static enum MangleVersion version;
+};
+
+} // namespace Compile
+} // namespace Rust
+
+#endif // RUST_MANGLE_H
diff --git a/gcc/rust/backend/rust-tree.cc b/gcc/rust/backend/rust-tree.cc
new file mode 100644
index 00000000000..3d71e19fe82
--- /dev/null
+++ b/gcc/rust/backend/rust-tree.cc
@@ -0,0 +1,958 @@
+// 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/>.
+
+#include "rust-tree.h"
+#include "fold-const.h"
+#include "stringpool.h"
+#include "attribs.h"
+#include "escaped_string.h"
+
+namespace Rust {
+
+void
+mark_exp_read (tree exp)
+{
+  if (exp == NULL)
+    return;
+
+  switch (TREE_CODE (exp))
+    {
+    case VAR_DECL:
+      gcc_fallthrough ();
+    case PARM_DECL:
+      DECL_READ_P (exp) = 1;
+      break;
+    case ARRAY_REF:
+    case COMPONENT_REF:
+    case MODIFY_EXPR:
+    case REALPART_EXPR:
+    case IMAGPART_EXPR:
+    CASE_CONVERT:
+    case ADDR_EXPR:
+    case INDIRECT_REF:
+    case FLOAT_EXPR:
+    case NON_DEPENDENT_EXPR:
+    case VIEW_CONVERT_EXPR:
+      mark_exp_read (TREE_OPERAND (exp, 0));
+      break;
+    case COMPOUND_EXPR:
+      mark_exp_read (TREE_OPERAND (exp, 1));
+      break;
+    case COND_EXPR:
+      if (TREE_OPERAND (exp, 1))
+	mark_exp_read (TREE_OPERAND (exp, 1));
+      if (TREE_OPERAND (exp, 2))
+	mark_exp_read (TREE_OPERAND (exp, 2));
+      break;
+    default:
+      break;
+    }
+}
+
+tree
+convert_from_reference (tree val)
+{
+  if (TREE_TYPE (val) && TYPE_REF_P (TREE_TYPE (val)))
+    {
+      tree t = TREE_TYPE (TREE_TYPE (val));
+      tree ref = build1 (INDIRECT_REF, t, val);
+
+      mark_exp_read (val);
+
+      TREE_SIDE_EFFECTS (ref)
+	= (TREE_THIS_VOLATILE (ref) || TREE_SIDE_EFFECTS (val));
+      val = ref;
+    }
+
+  return val;
+}
+
+tree
+mark_use (tree expr, bool rvalue_p, bool read_p,
+	  location_t loc /* = UNKNOWN_LOCATION */,
+	  bool reject_builtin /* = true */)
+{
+#define RECUR(t) mark_use ((t), rvalue_p, read_p, loc, reject_builtin)
+
+  if (expr == NULL_TREE || error_operand_p (expr))
+    return expr;
+
+  if (reject_builtin)
+    return error_mark_node;
+
+  if (read_p)
+    mark_exp_read (expr);
+
+  bool recurse_op[3] = {false, false, false};
+  switch (TREE_CODE (expr))
+    {
+    case COMPONENT_REF:
+    case NON_DEPENDENT_EXPR:
+      recurse_op[0] = true;
+      break;
+    case COMPOUND_EXPR:
+      recurse_op[1] = true;
+      break;
+    case COND_EXPR:
+      recurse_op[2] = true;
+      if (TREE_OPERAND (expr, 1))
+	recurse_op[1] = true;
+      break;
+    case INDIRECT_REF:
+      if (REFERENCE_REF_P (expr))
+	{
+	  /* Try to look through the reference.  */
+	  tree ref = TREE_OPERAND (expr, 0);
+	  tree r = mark_rvalue_use (ref, loc, reject_builtin);
+	  if (r != ref)
+	    expr = convert_from_reference (r);
+	}
+      break;
+
+    case VIEW_CONVERT_EXPR:
+      if (location_wrapper_p (expr))
+	{
+	  loc = EXPR_LOCATION (expr);
+	  tree op = TREE_OPERAND (expr, 0);
+	  tree nop = RECUR (op);
+	  if (nop == error_mark_node)
+	    return error_mark_node;
+	  else if (op == nop)
+	    /* No change.  */;
+	  else if (DECL_P (nop) || CONSTANT_CLASS_P (nop))
+	    {
+	      /* Reuse the location wrapper.  */
+	      TREE_OPERAND (expr, 0) = nop;
+	      /* If we're replacing a DECL with a constant, we also need to
+		 change the TREE_CODE of the location wrapper.  */
+	      if (rvalue_p)
+		TREE_SET_CODE (expr, NON_LVALUE_EXPR);
+	    }
+	  else
+	    {
+	      /* Drop the location wrapper.  */
+	      expr = nop;
+	      protected_set_expr_location (expr, loc);
+	    }
+	  return expr;
+	}
+      gcc_fallthrough ();
+    CASE_CONVERT:
+      recurse_op[0] = true;
+      break;
+
+    default:
+      break;
+    }
+
+  for (int i = 0; i < 3; ++i)
+    if (recurse_op[i])
+      {
+	tree op = TREE_OPERAND (expr, i);
+	op = RECUR (op);
+	if (op == error_mark_node)
+	  return error_mark_node;
+	TREE_OPERAND (expr, i) = op;
+      }
+
+  return expr;
+#undef RECUR
+}
+
+tree
+mark_rvalue_use (tree e, location_t loc /* = UNKNOWN_LOCATION */,
+		 bool reject_builtin /* = true */)
+{
+  return mark_use (e, true, true, loc, reject_builtin);
+}
+
+tree
+mark_lvalue_use (tree expr)
+{
+  return mark_use (expr, false, true, input_location, false);
+}
+
+tree
+mark_lvalue_use_nonread (tree expr)
+{
+  return mark_use (expr, false, false, input_location, false);
+}
+
+tree
+mark_discarded_use (tree expr)
+{
+  if (expr == NULL_TREE)
+    return expr;
+
+  STRIP_ANY_LOCATION_WRAPPER (expr);
+
+  switch (TREE_CODE (expr))
+    {
+    case COND_EXPR:
+      TREE_OPERAND (expr, 2) = mark_discarded_use (TREE_OPERAND (expr, 2));
+      gcc_fallthrough ();
+    case COMPOUND_EXPR:
+      TREE_OPERAND (expr, 1) = mark_discarded_use (TREE_OPERAND (expr, 1));
+      return expr;
+
+    case COMPONENT_REF:
+    case ARRAY_REF:
+    case INDIRECT_REF:
+    case MEMBER_REF:
+      break;
+    default:
+      if (DECL_P (expr))
+	break;
+      else
+	return expr;
+    }
+
+  return mark_use (expr, true, true, input_location, false);
+}
+
+tree
+convert_to_void (tree expr, impl_conv_void implicit)
+{
+  location_t loc = expr_loc_or_input_loc (expr);
+  if (expr == error_mark_node || TREE_TYPE (expr) == error_mark_node)
+    return error_mark_node;
+
+  expr = mark_discarded_use (expr);
+  if (implicit == ICV_CAST)
+    /* An explicit cast to void avoids all -Wunused-but-set* warnings.  */
+    mark_exp_read (expr);
+
+  if (!TREE_TYPE (expr))
+    return expr;
+
+  if (VOID_TYPE_P (TREE_TYPE (expr)))
+    return expr;
+  switch (TREE_CODE (expr))
+    {
+      case COND_EXPR: {
+	/* The two parts of a cond expr might be separate lvalues.  */
+	tree op1 = TREE_OPERAND (expr, 1);
+	tree op2 = TREE_OPERAND (expr, 2);
+	bool side_effects
+	  = ((op1 && TREE_SIDE_EFFECTS (op1)) || TREE_SIDE_EFFECTS (op2));
+	tree new_op1, new_op2;
+	new_op1 = NULL_TREE;
+	if (implicit != ICV_CAST && !side_effects)
+	  {
+	    if (op1)
+	      new_op1 = convert_to_void (op1, ICV_SECOND_OF_COND);
+	    new_op2 = convert_to_void (op2, ICV_THIRD_OF_COND);
+	  }
+	else
+	  {
+	    if (op1)
+	      new_op1 = convert_to_void (op1, ICV_CAST);
+	    new_op2 = convert_to_void (op2, ICV_CAST);
+	  }
+
+	expr = build3_loc (loc, COND_EXPR, TREE_TYPE (new_op2),
+			   TREE_OPERAND (expr, 0), new_op1, new_op2);
+	break;
+      }
+
+      case COMPOUND_EXPR: {
+	/* The second part of a compound expr contains the value.  */
+	tree op1 = TREE_OPERAND (expr, 1);
+	tree new_op1;
+	if (implicit != ICV_CAST
+	    && !warning_suppressed_p (expr /* What warning? */))
+	  new_op1 = convert_to_void (op1, ICV_RIGHT_OF_COMMA);
+	else
+	  new_op1 = convert_to_void (op1, ICV_CAST);
+
+	if (new_op1 != op1)
+	  {
+	    tree t = build2_loc (loc, COMPOUND_EXPR, TREE_TYPE (new_op1),
+				 TREE_OPERAND (expr, 0), new_op1);
+	    expr = t;
+	  }
+
+	break;
+      }
+
+    case NON_LVALUE_EXPR:
+    case NOP_EXPR:
+      /* These have already decayed to rvalue.  */
+      break;
+
+    case CALL_EXPR:
+      maybe_warn_nodiscard (expr, implicit);
+      break;
+
+      case INDIRECT_REF: {
+	tree type = TREE_TYPE (expr);
+	int is_reference = TYPE_REF_P (TREE_TYPE (TREE_OPERAND (expr, 0)));
+	int is_volatile = TYPE_VOLATILE (type);
+	int is_complete = COMPLETE_TYPE_P (type);
+
+	/* Can't load the value if we don't know the type.  */
+	if (is_volatile && !is_complete)
+	  {
+	    switch (implicit)
+	      {
+	      case ICV_CAST:
+		warning_at (loc, 0,
+			    "conversion to void will not access "
+			    "object of incomplete type %qT",
+			    type);
+		break;
+	      case ICV_SECOND_OF_COND:
+		warning_at (loc, 0,
+			    "indirection will not access object of "
+			    "incomplete type %qT in second operand "
+			    "of conditional expression",
+			    type);
+		break;
+	      case ICV_THIRD_OF_COND:
+		warning_at (loc, 0,
+			    "indirection will not access object of "
+			    "incomplete type %qT in third operand "
+			    "of conditional expression",
+			    type);
+		break;
+	      case ICV_RIGHT_OF_COMMA:
+		warning_at (loc, 0,
+			    "indirection will not access object of "
+			    "incomplete type %qT in right operand of "
+			    "comma operator",
+			    type);
+		break;
+	      case ICV_LEFT_OF_COMMA:
+		warning_at (loc, 0,
+			    "indirection will not access object of "
+			    "incomplete type %qT in left operand of "
+			    "comma operator",
+			    type);
+		break;
+	      case ICV_STATEMENT:
+		warning_at (loc, 0,
+			    "indirection will not access object of "
+			    "incomplete type %qT in statement",
+			    type);
+		break;
+	      case ICV_THIRD_IN_FOR:
+		warning_at (loc, 0,
+			    "indirection will not access object of "
+			    "incomplete type %qT in for increment "
+			    "expression",
+			    type);
+		break;
+	      default:
+		gcc_unreachable ();
+	      }
+	  }
+	/* Don't load the value if this is an implicit dereference, or if
+	   the type needs to be handled by ctors/dtors.  */
+	else if (is_volatile && is_reference)
+	  {
+	    switch (implicit)
+	      {
+	      case ICV_CAST:
+		warning_at (loc, 0,
+			    "conversion to void will not access "
+			    "object of type %qT",
+			    type);
+		break;
+	      case ICV_SECOND_OF_COND:
+		warning_at (loc, 0,
+			    "implicit dereference will not access "
+			    "object of type %qT in second operand of "
+			    "conditional expression",
+			    type);
+		break;
+	      case ICV_THIRD_OF_COND:
+		warning_at (loc, 0,
+			    "implicit dereference will not access "
+			    "object of type %qT in third operand of "
+			    "conditional expression",
+			    type);
+		break;
+	      case ICV_RIGHT_OF_COMMA:
+		warning_at (loc, 0,
+			    "implicit dereference will not access "
+			    "object of type %qT in right operand of "
+			    "comma operator",
+			    type);
+		break;
+	      case ICV_LEFT_OF_COMMA:
+		warning_at (loc, 0,
+			    "implicit dereference will not access "
+			    "object of type %qT in left operand of comma "
+			    "operator",
+			    type);
+		break;
+	      case ICV_STATEMENT:
+		warning_at (loc, 0,
+			    "implicit dereference will not access "
+			    "object of type %qT in statement",
+			    type);
+		break;
+	      case ICV_THIRD_IN_FOR:
+		warning_at (loc, 0,
+			    "implicit dereference will not access "
+			    "object of type %qT in for increment expression",
+			    type);
+		break;
+	      default:
+		gcc_unreachable ();
+	      }
+	  }
+	else if (is_volatile && TREE_ADDRESSABLE (type))
+	  {
+	    switch (implicit)
+	      {
+	      case ICV_CAST:
+		warning_at (loc, 0,
+			    "conversion to void will not access "
+			    "object of non-trivially-copyable type %qT",
+			    type);
+		break;
+	      case ICV_SECOND_OF_COND:
+		warning_at (loc, 0,
+			    "indirection will not access object of "
+			    "non-trivially-copyable type %qT in second "
+			    "operand of conditional expression",
+			    type);
+		break;
+	      case ICV_THIRD_OF_COND:
+		warning_at (loc, 0,
+			    "indirection will not access object of "
+			    "non-trivially-copyable type %qT in third "
+			    "operand of conditional expression",
+			    type);
+		break;
+	      case ICV_RIGHT_OF_COMMA:
+		warning_at (loc, 0,
+			    "indirection will not access object of "
+			    "non-trivially-copyable type %qT in right "
+			    "operand of comma operator",
+			    type);
+		break;
+	      case ICV_LEFT_OF_COMMA:
+		warning_at (loc, 0,
+			    "indirection will not access object of "
+			    "non-trivially-copyable type %qT in left "
+			    "operand of comma operator",
+			    type);
+		break;
+	      case ICV_STATEMENT:
+		warning_at (loc, 0,
+			    "indirection will not access object of "
+			    "non-trivially-copyable type %qT in statement",
+			    type);
+		break;
+	      case ICV_THIRD_IN_FOR:
+		warning_at (loc, 0,
+			    "indirection will not access object of "
+			    "non-trivially-copyable type %qT in for "
+			    "increment expression",
+			    type);
+		break;
+	      default:
+		gcc_unreachable ();
+	      }
+	  }
+	if (is_reference || !is_volatile || !is_complete
+	    || TREE_ADDRESSABLE (type))
+	  {
+	    /* Emit a warning (if enabled) when the "effect-less" INDIRECT_REF
+	       operation is stripped off. Note that we don't warn about
+	       - an expression with TREE_NO_WARNING set. (For an example of
+		 such expressions, see build_over_call in call.cc.)
+	       - automatic dereferencing of references, since the user cannot
+		 control it. (See also warn_if_unused_value() in c-common.cc.)
+	     */
+	    if (warn_unused_value && implicit != ICV_CAST
+		&& !warning_suppressed_p (expr, OPT_Wunused_value)
+		&& !is_reference)
+	      warning_at (loc, OPT_Wunused_value, "value computed is not used");
+	    expr = TREE_OPERAND (expr, 0);
+	    if (TREE_CODE (expr) == CALL_EXPR)
+	      maybe_warn_nodiscard (expr, implicit);
+	  }
+
+	break;
+      }
+
+      case VAR_DECL: {
+	/* External variables might be incomplete.  */
+	tree type = TREE_TYPE (expr);
+	int is_complete = COMPLETE_TYPE_P (type);
+
+	if (TYPE_VOLATILE (type) && !is_complete)
+	  switch (implicit)
+	    {
+	    case ICV_CAST:
+	      warning_at (loc, 0,
+			  "conversion to void will not access "
+			  "object %qE of incomplete type %qT",
+			  expr, type);
+	      break;
+	    case ICV_SECOND_OF_COND:
+	      warning_at (loc, 0,
+			  "variable %qE of incomplete type %qT will "
+			  "not be accessed in second operand of "
+			  "conditional expression",
+			  expr, type);
+	      break;
+	    case ICV_THIRD_OF_COND:
+	      warning_at (loc, 0,
+			  "variable %qE of incomplete type %qT will "
+			  "not be accessed in third operand of "
+			  "conditional expression",
+			  expr, type);
+	      break;
+	    case ICV_RIGHT_OF_COMMA:
+	      warning_at (loc, 0,
+			  "variable %qE of incomplete type %qT will "
+			  "not be accessed in right operand of comma operator",
+			  expr, type);
+	      break;
+	    case ICV_LEFT_OF_COMMA:
+	      warning_at (loc, 0,
+			  "variable %qE of incomplete type %qT will "
+			  "not be accessed in left operand of comma operator",
+			  expr, type);
+	      break;
+	    case ICV_STATEMENT:
+	      warning_at (loc, 0,
+			  "variable %qE of incomplete type %qT will "
+			  "not be accessed in statement",
+			  expr, type);
+	      break;
+	    case ICV_THIRD_IN_FOR:
+	      warning_at (loc, 0,
+			  "variable %qE of incomplete type %qT will "
+			  "not be accessed in for increment expression",
+			  expr, type);
+	      break;
+	    default:
+	      gcc_unreachable ();
+	    }
+
+	break;
+      }
+
+    default:;
+    }
+
+  if (!TREE_SIDE_EFFECTS (expr))
+    expr = void_node;
+
+  return expr;
+}
+
+void
+maybe_warn_nodiscard (tree expr, impl_conv_void implicit)
+{
+  tree call = expr;
+  if (TREE_CODE (expr) == TARGET_EXPR)
+    call = TARGET_EXPR_INITIAL (expr);
+
+  location_t loc = expr_loc_or_input_loc (call);
+  tree callee = CALL_EXPR_FN (call);
+  if (!callee)
+    return;
+
+  tree type = TREE_TYPE (callee);
+  if (INDIRECT_TYPE_P (type))
+    type = TREE_TYPE (type);
+
+  tree rettype = TREE_TYPE (type);
+  tree fn = get_fndecl_from_callee (callee);
+  tree attr;
+  if (implicit != ICV_CAST && fn
+      && (attr = lookup_attribute ("nodiscard", DECL_ATTRIBUTES (fn))))
+    {
+      escaped_string msg;
+      tree args = TREE_VALUE (attr);
+      if (args)
+	msg.escape (TREE_STRING_POINTER (TREE_VALUE (args)));
+      const char *format
+	= (msg ? G_ ("ignoring return value of %qD, that must be used: %<%s%>")
+	       : G_ ("ignoring return value of %qD, that must be used"));
+      const char *raw_msg = msg ? (const char *) msg : "";
+      auto_diagnostic_group d;
+      if (warning_at (loc, OPT_Wunused_result, format, fn, raw_msg))
+	inform (DECL_SOURCE_LOCATION (fn), "declared here");
+    }
+  else if (implicit != ICV_CAST
+	   && (attr
+	       = lookup_attribute ("nodiscard", TYPE_ATTRIBUTES (rettype))))
+    {
+      escaped_string msg;
+      tree args = TREE_VALUE (attr);
+      if (args)
+	msg.escape (TREE_STRING_POINTER (TREE_VALUE (args)));
+      const char *format
+	= (msg ? G_ (
+	     "ignoring returned value of type %qT, that must be used: %<%s%>")
+	       : G_ ("ignoring returned value of type %qT, that must be used"));
+      const char *raw_msg = msg ? (const char *) msg : "";
+      auto_diagnostic_group d;
+      if (warning_at (loc, OPT_Wunused_result, format, rettype, raw_msg))
+	{
+	  if (fn)
+	    inform (DECL_SOURCE_LOCATION (fn), "in call to %qD, declared here",
+		    fn);
+	  inform (DECL_SOURCE_LOCATION (TYPE_NAME (rettype)),
+		  "%qT declared here", rettype);
+	}
+    }
+}
+
+location_t
+expr_loc_or_loc (const_tree t, location_t or_loc)
+{
+  location_t loc = EXPR_LOCATION (t);
+  if (loc == UNKNOWN_LOCATION)
+    loc = or_loc;
+  return loc;
+}
+
+location_t
+expr_loc_or_input_loc (const_tree t)
+{
+  return expr_loc_or_loc (t, input_location);
+}
+
+// FN is the callee of a CALL_EXPR or AGGR_INIT_EXPR; return the FUNCTION_DECL
+// if we can.
+tree
+get_fndecl_from_callee (tree fn)
+{
+  if (fn == NULL_TREE)
+    return fn;
+  if (TREE_CODE (fn) == FUNCTION_DECL)
+    return fn;
+  tree type = TREE_TYPE (fn);
+  if (type == NULL_TREE || !INDIRECT_TYPE_P (type))
+    return NULL_TREE;
+
+  STRIP_NOPS (fn);
+  if (TREE_CODE (fn) == ADDR_EXPR || TREE_CODE (fn) == FDESC_EXPR)
+    fn = TREE_OPERAND (fn, 0);
+  if (TREE_CODE (fn) == FUNCTION_DECL)
+    return fn;
+  return NULL_TREE;
+}
+
+tree
+pointer_offset_expression (tree base_tree, tree index_tree, location_t location)
+{
+  tree element_type_tree = TREE_TYPE (TREE_TYPE (base_tree));
+  if (base_tree == error_mark_node || TREE_TYPE (base_tree) == error_mark_node
+      || index_tree == error_mark_node || element_type_tree == error_mark_node)
+    return error_mark_node;
+
+  tree element_size = TYPE_SIZE_UNIT (element_type_tree);
+  index_tree = fold_convert_loc (location, sizetype, index_tree);
+  tree offset
+    = fold_build2_loc (location, MULT_EXPR, sizetype, index_tree, element_size);
+
+  return fold_build2_loc (location, POINTER_PLUS_EXPR, TREE_TYPE (base_tree),
+			  base_tree, offset);
+}
+
+// forked from gcc/cp/tree.cc cp_walk_subtrees
+/* Apply FUNC to all language-specific sub-trees of TP in a pre-order
+   traversal.  Called from walk_tree.  */
+
+tree
+rs_walk_subtrees (tree *tp, int *walk_subtrees_p, walk_tree_fn func, void *data,
+		  hash_set<tree> *pset)
+{
+  enum tree_code code = TREE_CODE (*tp);
+  tree result;
+
+#define WALK_SUBTREE(NODE)                                                     \
+  do                                                                           \
+    {                                                                          \
+      result = rs_walk_tree (&(NODE), func, data, pset);                       \
+      if (result)                                                              \
+	goto out;                                                              \
+    }                                                                          \
+  while (0)
+
+  if (TYPE_P (*tp))
+    {
+      /* If *WALK_SUBTREES_P is 1, we're interested in the syntactic form of
+	 the argument, so don't look through typedefs, but do walk into
+	 template arguments for alias templates (and non-typedefed classes).
+
+	 If *WALK_SUBTREES_P > 1, we're interested in type identity or
+	 equivalence, so look through typedefs, ignoring template arguments for
+	 alias templates, and walk into template args of classes.
+
+	 See find_abi_tags_r for an example of setting *WALK_SUBTREES_P to 2
+	 when that's the behavior the walk_tree_fn wants.  */
+      if (*walk_subtrees_p == 1 && typedef_variant_p (*tp))
+	{
+	  *walk_subtrees_p = 0;
+	  return NULL_TREE;
+	}
+    }
+
+  /* Not one of the easy cases.  We must explicitly go through the
+     children.  */
+  result = NULL_TREE;
+  switch (code)
+    {
+    case TREE_LIST:
+      WALK_SUBTREE (TREE_PURPOSE (*tp));
+      break;
+
+    case RECORD_TYPE:
+      if (TYPE_PTRMEMFUNC_P (*tp))
+	WALK_SUBTREE (TYPE_PTRMEMFUNC_FN_TYPE_RAW (*tp));
+      break;
+
+    case CONSTRUCTOR:
+      if (COMPOUND_LITERAL_P (*tp))
+	WALK_SUBTREE (TREE_TYPE (*tp));
+      break;
+
+    case DECL_EXPR:
+      /* User variables should be mentioned in BIND_EXPR_VARS
+	 and their initializers and sizes walked when walking
+	 the containing BIND_EXPR.  Compiler temporaries are
+	 handled here.  And also normal variables in templates,
+	 since do_poplevel doesn't build a BIND_EXPR then.  */
+      if (VAR_P (TREE_OPERAND (*tp, 0))
+	  && (DECL_ARTIFICIAL (TREE_OPERAND (*tp, 0))
+	      && !TREE_STATIC (TREE_OPERAND (*tp, 0))))
+	{
+	  tree decl = TREE_OPERAND (*tp, 0);
+	  WALK_SUBTREE (DECL_INITIAL (decl));
+	  WALK_SUBTREE (DECL_SIZE (decl));
+	  WALK_SUBTREE (DECL_SIZE_UNIT (decl));
+	}
+      break;
+
+    default:
+      return NULL_TREE;
+    }
+
+  /* We didn't find what we were looking for.  */
+out:
+  return result;
+
+#undef WALK_SUBTREE
+}
+
+// forked from gcc/cp/tree.cc cp_expr_location
+
+/* Like EXPR_LOCATION, but also handle some tcc_exceptional that have
+   locations.  */
+
+location_t
+rs_expr_location (const_tree t_)
+{
+  tree t = CONST_CAST_TREE (t_);
+  if (t == NULL_TREE)
+    return UNKNOWN_LOCATION;
+
+  return EXPR_LOCATION (t);
+}
+
+// forked from gcc/cp/class.cc is_really_empty_class
+
+/* Returns true if TYPE contains no actual data, just various
+   possible combinations of empty classes.  If IGNORE_VPTR is true,
+   a vptr doesn't prevent the class from being considered empty.  Typically
+   we want to ignore the vptr on assignment, and not on initialization.  */
+
+bool
+is_really_empty_class (tree type, bool ignore_vptr)
+{
+  if (CLASS_TYPE_P (type))
+    {
+      tree field;
+      tree binfo;
+      tree base_binfo;
+      int i;
+
+      /* CLASSTYPE_EMPTY_P isn't set properly until the class is actually laid
+	 out, but we'd like to be able to check this before then.  */
+      if (COMPLETE_TYPE_P (type) && is_empty_class (type))
+	return true;
+
+      if (!ignore_vptr && TYPE_CONTAINS_VPTR_P (type))
+	return false;
+
+      for (binfo = TYPE_BINFO (type), i = 0;
+	   BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i)
+	if (!is_really_empty_class (BINFO_TYPE (base_binfo), ignore_vptr))
+	  return false;
+      for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
+	if (TREE_CODE (field) == FIELD_DECL
+	    && !DECL_ARTIFICIAL (field)
+	    /* An unnamed bit-field is not a data member.  */
+	    && !DECL_UNNAMED_BIT_FIELD (field)
+	    && !is_really_empty_class (TREE_TYPE (field), ignore_vptr))
+	  return false;
+      return true;
+    }
+  else if (TREE_CODE (type) == ARRAY_TYPE)
+    return (integer_zerop (array_type_nelts_top (type))
+	    || is_really_empty_class (TREE_TYPE (type), ignore_vptr));
+  return false;
+}
+
+// forked from gcc/cp/class.cc is_empty_class
+
+/* Returns 1 if TYPE contains only padding bytes.  */
+
+int
+is_empty_class (tree type)
+{
+  if (type == error_mark_node)
+    return 0;
+
+  if (!CLASS_TYPE_P (type))
+    return 0;
+
+  return CLASSTYPE_EMPTY_P (type);
+}
+
+// forked from gcc/cp/tree.cc array_type_nelts_top
+
+/* Return, as an INTEGER_CST node, the number of elements for TYPE
+   (which is an ARRAY_TYPE).  This counts only elements of the top
+   array.  */
+
+tree
+array_type_nelts_top (tree type)
+{
+  return fold_build2_loc (input_location, PLUS_EXPR, sizetype,
+			  array_type_nelts (type), size_one_node);
+}
+
+// forked from gcc/cp/tree.cc builtin_valid_in_constant_expr_p
+
+/* Test whether DECL is a builtin that may appear in a
+   constant-expression. */
+
+bool
+builtin_valid_in_constant_expr_p (const_tree decl)
+{
+  STRIP_ANY_LOCATION_WRAPPER (decl);
+  if (TREE_CODE (decl) != FUNCTION_DECL)
+    /* Not a function.  */
+    return false;
+  if (DECL_BUILT_IN_CLASS (decl) != BUILT_IN_NORMAL)
+    {
+      if (fndecl_built_in_p (decl, BUILT_IN_FRONTEND))
+	switch (DECL_FE_FUNCTION_CODE (decl))
+	  {
+	  case RS_BUILT_IN_IS_CONSTANT_EVALUATED:
+	  case RS_BUILT_IN_SOURCE_LOCATION:
+	  case RS_BUILT_IN_IS_CORRESPONDING_MEMBER:
+	  case RS_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS:
+	    return true;
+	  default:
+	    break;
+	  }
+      /* Not a built-in.  */
+      return false;
+    }
+  switch (DECL_FUNCTION_CODE (decl))
+    {
+      /* These always have constant results like the corresponding
+	 macros/symbol.  */
+    case BUILT_IN_FILE:
+    case BUILT_IN_FUNCTION:
+    case BUILT_IN_LINE:
+
+      /* The following built-ins are valid in constant expressions
+	 when their arguments are.  */
+    case BUILT_IN_ADD_OVERFLOW_P:
+    case BUILT_IN_SUB_OVERFLOW_P:
+    case BUILT_IN_MUL_OVERFLOW_P:
+
+      /* These have constant results even if their operands are
+	 non-constant.  */
+    case BUILT_IN_CONSTANT_P:
+    case BUILT_IN_ATOMIC_ALWAYS_LOCK_FREE:
+      return true;
+    default:
+      return false;
+    }
+}
+
+// forked from gcc/cp/decl2.cc decl_maybe_constant_var_p
+
+/* Returns true if DECL could be a symbolic constant variable, depending on
+   its initializer.  */
+
+bool
+decl_maybe_constant_var_p (tree decl)
+{
+  tree type = TREE_TYPE (decl);
+  if (!VAR_P (decl))
+    return false;
+  if (DECL_DECLARED_CONSTEXPR_P (decl))
+    return true;
+  if (DECL_HAS_VALUE_EXPR_P (decl))
+    /* A proxy isn't constant.  */
+    return false;
+  if (TYPE_REF_P (type))
+    /* References can be constant.  */;
+  else if (RS_TYPE_CONST_NON_VOLATILE_P (type)
+	   && INTEGRAL_OR_ENUMERATION_TYPE_P (type))
+    /* And const integers.  */;
+  else
+    return false;
+
+  if (DECL_INITIAL (decl) && !DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl))
+    /* We know the initializer, and it isn't constant.  */
+    return false;
+  else
+    return true;
+}
+
+// forked from gcc/cp/typeck.cc cp_type_quals
+
+/* Returns the type qualifiers for this type, including the qualifiers on the
+   elements for an array type.  */
+
+int
+rs_type_quals (const_tree type)
+{
+  int quals;
+  /* This CONST_CAST is okay because strip_array_types returns its
+     argument unmodified and we assign it to a const_tree.  */
+  type = strip_array_types (CONST_CAST_TREE (type));
+  if (type == error_mark_node
+      /* Quals on a FUNCTION_TYPE are memfn quals.  */
+      || TREE_CODE (type) == FUNCTION_TYPE)
+    return TYPE_UNQUALIFIED;
+  quals = TYPE_QUALS (type);
+  /* METHOD and REFERENCE_TYPEs should never have quals.  */
+  gcc_assert (
+    (TREE_CODE (type) != METHOD_TYPE && !TYPE_REF_P (type))
+    || ((quals & (TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE)) == TYPE_UNQUALIFIED));
+  return quals;
+}
+
+} // namespace Rust
diff --git a/gcc/rust/backend/rust-tree.h b/gcc/rust/backend/rust-tree.h
new file mode 100644
index 00000000000..a667cbfc8ad
--- /dev/null
+++ b/gcc/rust/backend/rust-tree.h
@@ -0,0 +1,508 @@
+// 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_TREE
+#define RUST_TREE
+
+#include "rust-system.h"
+#include "coretypes.h"
+#include "tree.h"
+
+/* Returns true if NODE is a pointer.  */
+#define TYPE_PTR_P(NODE) (TREE_CODE (NODE) == POINTER_TYPE)
+
+/* Returns true if NODE is a reference.  */
+#define TYPE_REF_P(NODE) (TREE_CODE (NODE) == REFERENCE_TYPE)
+
+/* Returns true if NODE is a pointer or a reference.  */
+#define INDIRECT_TYPE_P(NODE) (TYPE_PTR_P (NODE) || TYPE_REF_P (NODE))
+
+/* [basic.fundamental]
+
+   Types  bool, char, wchar_t, and the signed and unsigned integer types
+   are collectively called integral types.
+
+   Note that INTEGRAL_TYPE_P, as defined in tree.h, allows enumeration
+   types as well, which is incorrect in C++.  Keep these checks in
+   ascending code order.  */
+#define RS_INTEGRAL_TYPE_P(TYPE)                                               \
+  (TREE_CODE (TYPE) == BOOLEAN_TYPE || TREE_CODE (TYPE) == INTEGER_TYPE)
+
+/* [basic.fundamental]
+
+   Integral and floating types are collectively called arithmetic
+   types.
+
+   As a GNU extension, we also accept complex types.
+
+   Keep these checks in ascending code order.  */
+#define ARITHMETIC_TYPE_P(TYPE)                                                \
+  (RS_INTEGRAL_TYPE_P (TYPE) || TREE_CODE (TYPE) == REAL_TYPE                  \
+   || TREE_CODE (TYPE) == COMPLEX_TYPE)
+
+/* True iff TYPE is cv decltype(nullptr).  */
+#define NULLPTR_TYPE_P(TYPE) (TREE_CODE (TYPE) == NULLPTR_TYPE)
+
+/* [basic.types]
+
+   Arithmetic types, enumeration types, pointer types,
+   pointer-to-member types, and std::nullptr_t are collectively called
+   scalar types.
+
+   Keep these checks in ascending code order.  */
+#define SCALAR_TYPE_P(TYPE)                                                    \
+  (TREE_CODE (TYPE) == ENUMERAL_TYPE || ARITHMETIC_TYPE_P (TYPE)               \
+   || TYPE_PTR_P (TYPE) || NULLPTR_TYPE_P (TYPE))
+
+/* True if NODE is an implicit INDIRECT_REF from convert_from_reference.  */
+#define REFERENCE_REF_P(NODE)                                                  \
+  (INDIRECT_REF_P (NODE) && TREE_TYPE (TREE_OPERAND (NODE, 0))                 \
+   && TYPE_REF_P (TREE_TYPE (TREE_OPERAND ((NODE), 0))))
+
+// this is a helper to differentiate RECORD types between actual records and
+// slices
+#define SLICE_FLAG TREE_LANG_FLAG_0
+#define SLICE_TYPE_P(TYPE)                                                     \
+  (TREE_CODE (TYPE) == RECORD_TYPE && TREE_LANG_FLAG_0 (TYPE))
+
+/* Returns true if NODE is a pointer to member function type.  */
+#define TYPE_PTRMEMFUNC_P(NODE)                                                \
+  (TREE_CODE (NODE) == RECORD_TYPE && TYPE_PTRMEMFUNC_FLAG (NODE))
+
+#define TYPE_PTRMEMFUNC_FLAG(NODE) (TYPE_LANG_FLAG_2 (RECORD_TYPE_CHECK (NODE)))
+
+#define TYPE_PTRMEMFUNC_FN_TYPE_RAW(NODE) (TREE_TYPE (TYPE_FIELDS (NODE)))
+
+/* True if NODE is a compound-literal, i.e., a brace-enclosed
+   initializer cast to a particular type.  This is mostly only set during
+   template parsing; once the initializer has been digested into an actual
+   value of the type, the expression is represented by a TARGET_EXPR.  */
+#define COMPOUND_LITERAL_P(NODE)                                               \
+  (TREE_CODE (NODE) == CONSTRUCTOR && TREE_HAS_CONSTRUCTOR (NODE))
+
+/* When appearing in an INDIRECT_REF, it means that the tree structure
+   underneath is actually a call to a constructor.  This is needed
+   when the constructor must initialize local storage (which can
+   be automatically destroyed), rather than allowing it to allocate
+   space from the heap.
+
+   When appearing in a SAVE_EXPR, it means that underneath
+   is a call to a constructor.
+
+   When appearing in a CONSTRUCTOR, the expression is an unconverted
+   compound literal.
+
+   When appearing in a FIELD_DECL, it means that this field
+   has been duly initialized in its constructor.  */
+#define TREE_HAS_CONSTRUCTOR(NODE) (TREE_LANG_FLAG_4 (NODE))
+
+/* Nonzero if T is a class type.  Zero for template type parameters,
+   typename types, and so forth.  */
+#define CLASS_TYPE_P(T)                                                        \
+  (RECORD_OR_UNION_CODE_P (TREE_CODE (T)) && TYPE_LANG_FLAG_5 (T))
+
+/* [class.virtual]
+
+   A class that declares or inherits a virtual function is called a
+   polymorphic class.  */
+#define TYPE_POLYMORPHIC_P(NODE) (TREE_LANG_FLAG_2 (NODE))
+
+/* Nonzero if this class has a virtual function table pointer.  */
+#define TYPE_CONTAINS_VPTR_P(NODE)                                             \
+  (TYPE_POLYMORPHIC_P (NODE) || CLASSTYPE_VBASECLASSES (NODE))
+
+/* A vector of BINFOs for the direct and indirect virtual base classes
+   that this type uses in a post-order depth-first left-to-right
+   order.  (In other words, these bases appear in the order that they
+   should be initialized.)  */
+#define CLASSTYPE_VBASECLASSES(NODE) (LANG_TYPE_CLASS_CHECK (NODE)->vbases)
+
+/* A vector of BINFOs for the direct and indirect virtual base classes
+   that this type uses in a post-order depth-first left-to-right
+   order.  (In other words, these bases appear in the order that they
+   should be initialized.)  */
+#define CLASSTYPE_VBASECLASSES(NODE) (LANG_TYPE_CLASS_CHECK (NODE)->vbases)
+
+/* We used to have a variant type for lang_type.  Keep the name of the
+   checking accessor for the sole survivor.  */
+#define LANG_TYPE_CLASS_CHECK(NODE) (TYPE_LANG_SPECIFIC (NODE))
+
+/* Keep these checks in ascending code order.  */
+#define RECORD_OR_UNION_CODE_P(T) ((T) == RECORD_TYPE || (T) == UNION_TYPE)
+#define OVERLOAD_TYPE_P(T) (CLASS_TYPE_P (T) || TREE_CODE (T) == ENUMERAL_TYPE)
+
+/* Nonzero if this class is "empty" in the sense of the C++ ABI.  */
+#define CLASSTYPE_EMPTY_P(NODE) (LANG_TYPE_CLASS_CHECK (NODE)->empty_p)
+
+/* True if DECL is declared 'constexpr'.  */
+#define DECL_DECLARED_CONSTEXPR_P(DECL)                                        \
+  DECL_LANG_FLAG_8 (VAR_OR_FUNCTION_DECL_CHECK (DECL))
+
+#define VAR_OR_FUNCTION_DECL_CHECK(NODE)                                       \
+  TREE_CHECK2 (NODE, VAR_DECL, FUNCTION_DECL)
+
+// Below macros are copied from gcc/c-family/c-common.h
+
+/* In a FIELD_DECL, nonzero if the decl was originally a bitfield.  */
+#define DECL_C_BIT_FIELD(NODE) (DECL_LANG_FLAG_4 (FIELD_DECL_CHECK (NODE)) == 1)
+#define SET_DECL_C_BIT_FIELD(NODE)                                             \
+  (DECL_LANG_FLAG_4 (FIELD_DECL_CHECK (NODE)) = 1)
+#define CLEAR_DECL_C_BIT_FIELD(NODE)                                           \
+  (DECL_LANG_FLAG_4 (FIELD_DECL_CHECK (NODE)) = 0)
+
+/* True if the decl was an unnamed bitfield.  */
+#define DECL_UNNAMED_BIT_FIELD(NODE)                                           \
+  (DECL_C_BIT_FIELD (NODE) && !DECL_NAME (NODE))
+
+/* 1 iff NODE is function-local.  */
+#define DECL_FUNCTION_SCOPE_P(NODE)                                            \
+  (DECL_CONTEXT (NODE) && TREE_CODE (DECL_CONTEXT (NODE)) == FUNCTION_DECL)
+
+/* Nonzero if this type is const-qualified, but not
+   volatile-qualified.  Other qualifiers are ignored.  This macro is
+   used to test whether or not it is OK to bind an rvalue to a
+   reference.  */
+#define RS_TYPE_CONST_NON_VOLATILE_P(NODE)                                     \
+  ((rs_type_quals (NODE) & (TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE))             \
+   == TYPE_QUAL_CONST)
+
+/* [basic.fundamental]
+
+   Types  bool, char, wchar_t, and the signed and unsigned integer types
+   are collectively called integral types.
+
+   Note that INTEGRAL_TYPE_P, as defined in tree.h, allows enumeration
+   types as well, which is incorrect in C++.  Keep these checks in
+   ascending code order.  */
+#define RS_INTEGRAL_TYPE_P(TYPE)                                               \
+  (TREE_CODE (TYPE) == BOOLEAN_TYPE || TREE_CODE (TYPE) == INTEGER_TYPE)
+
+/* Returns true if TYPE is an integral or enumeration name.  Keep
+   these checks in ascending code order.  */
+#define INTEGRAL_OR_ENUMERATION_TYPE_P(TYPE)                                   \
+  (TREE_CODE (TYPE) == ENUMERAL_TYPE || RS_INTEGRAL_TYPE_P (TYPE))
+
+/* Nonzero for a VAR_DECL that was initialized with a
+   constant-expression.  */
+#define DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P(NODE)                        \
+  (TREE_LANG_FLAG_2 (VAR_DECL_CHECK (NODE)))
+
+// Above macros are copied from gcc/c-family/c-common.h
+
+// forked from gcc/cp/cp-tree.h treee_pair_s
+
+struct GTY (()) tree_pair_s
+{
+  tree purpose;
+  tree value;
+};
+
+// forked from gcc/cp/cp-tree.h tree_pair_p
+
+typedef tree_pair_s *tree_pair_p;
+
+// forked from gcc/cp/cp-tree.h lang_type
+
+/* This structure provides additional information above and beyond
+   what is provide in the ordinary tree_type.  In the past, we used it
+   for the types of class types, template parameters types, typename
+   types, and so forth.  However, there can be many (tens to hundreds
+   of thousands) of template parameter types in a compilation, and
+   there's no need for this additional information in that case.
+   Therefore, we now use this data structure only for class types.
+
+   In the past, it was thought that there would be relatively few
+   class types.  However, in the presence of heavy use of templates,
+   many (i.e., thousands) of classes can easily be generated.
+   Therefore, we should endeavor to keep the size of this structure to
+   a minimum.  */
+struct GTY (()) lang_type
+{
+  unsigned char align;
+
+  unsigned has_type_conversion : 1;
+  unsigned has_copy_ctor : 1;
+  unsigned has_default_ctor : 1;
+  unsigned const_needs_init : 1;
+  unsigned ref_needs_init : 1;
+  unsigned has_const_copy_assign : 1;
+  unsigned use_template : 2;
+
+  unsigned has_mutable : 1;
+  unsigned com_interface : 1;
+  unsigned non_pod_class : 1;
+  unsigned nearly_empty_p : 1;
+  unsigned user_align : 1;
+  unsigned has_copy_assign : 1;
+  unsigned has_new : 1;
+  unsigned has_array_new : 1;
+
+  unsigned gets_delete : 2;
+  unsigned interface_only : 1;
+  unsigned interface_unknown : 1;
+  unsigned contains_empty_class_p : 1;
+  unsigned anon_aggr : 1;
+  unsigned non_zero_init : 1;
+  unsigned empty_p : 1;
+  /* 32 bits allocated.  */
+
+  unsigned vec_new_uses_cookie : 1;
+  unsigned declared_class : 1;
+  unsigned diamond_shaped : 1;
+  unsigned repeated_base : 1;
+  unsigned being_defined : 1;
+  unsigned debug_requested : 1;
+  unsigned fields_readonly : 1;
+  unsigned ptrmemfunc_flag : 1;
+
+  unsigned lazy_default_ctor : 1;
+  unsigned lazy_copy_ctor : 1;
+  unsigned lazy_copy_assign : 1;
+  unsigned lazy_destructor : 1;
+  unsigned has_const_copy_ctor : 1;
+  unsigned has_complex_copy_ctor : 1;
+  unsigned has_complex_copy_assign : 1;
+  unsigned non_aggregate : 1;
+
+  unsigned has_complex_dflt : 1;
+  unsigned has_list_ctor : 1;
+  unsigned non_std_layout : 1;
+  unsigned is_literal : 1;
+  unsigned lazy_move_ctor : 1;
+  unsigned lazy_move_assign : 1;
+  unsigned has_complex_move_ctor : 1;
+  unsigned has_complex_move_assign : 1;
+
+  unsigned has_constexpr_ctor : 1;
+  unsigned unique_obj_representations : 1;
+  unsigned unique_obj_representations_set : 1;
+  bool erroneous : 1;
+  bool non_pod_aggregate : 1;
+
+  /* When adding a flag here, consider whether or not it ought to
+     apply to a template instance if it applies to the template.  If
+     so, make sure to copy it in instantiate_class_template!  */
+
+  /* There are some bits left to fill out a 32-bit word.  Keep track
+     of this by updating the size of this bitfield whenever you add or
+     remove a flag.  */
+  unsigned dummy : 3;
+
+  tree primary_base;
+  vec<tree_pair_s, va_gc> *vcall_indices;
+  tree vtables;
+  tree typeinfo_var;
+  vec<tree, va_gc> *vbases;
+  tree as_base;
+  vec<tree, va_gc> *pure_virtuals;
+  tree friend_classes;
+  vec<tree, va_gc> *GTY ((reorder ("resort_type_member_vec"))) members;
+  tree key_method;
+  tree decl_list;
+  tree befriending_classes;
+  /* In a RECORD_TYPE, information specific to Objective-C++, such
+     as a list of adopted protocols or a pointer to a corresponding
+     @interface.  See objc/objc-act.h for details.  */
+  tree objc_info;
+  /* FIXME reuse another field?  */
+  tree lambda_expr;
+};
+
+namespace Rust {
+
+// forked from gcc/cp/cp-tree.h tsubst_flags_t
+
+/* This type is used for parameters and variables which hold
+   combinations of the flags in enum tsubst_flags.  */
+typedef int tsubst_flags_t;
+
+// forked from gcc/cp/cvt.cc convert_to_void
+//
+// When an expression is used in a void context, its value is discarded and
+// no lvalue-rvalue and similar conversions happen [expr.static.cast/4,
+// stmt.expr/1, expr.comma/1].  This permits dereferencing an incomplete type
+// in a void context. The C++ standard does not define what an `access' to an
+// object is, but there is reason to believe that it is the lvalue to rvalue
+// conversion -- if it were not, `*&*p = 1' would violate [expr]/4 in that it
+// accesses `*p' not to calculate the value to be stored. But, dcl.type.cv/8
+// indicates that volatile semantics should be the same between C and C++
+// where ever possible. C leaves it implementation defined as to what
+// constitutes an access to a volatile. So, we interpret `*vp' as a read of
+// the volatile object `vp' points to, unless that is an incomplete type. For
+// volatile references we do not do this interpretation, because that would
+// make it impossible to ignore the reference return value from functions. We
+// issue warnings in the confusing cases.
+//
+// The IMPLICIT is ICV_CAST when the user is explicitly converting an
+// expression to void via a cast. If an expression is being implicitly
+// converted, IMPLICIT indicates the context of the implicit conversion.
+
+/* Possible cases of implicit or explicit bad conversions to void. */
+enum impl_conv_void
+{
+  ICV_CAST,	      /* (explicit) conversion to void */
+  ICV_SECOND_OF_COND, /* second operand of conditional expression */
+  ICV_THIRD_OF_COND,  /* third operand of conditional expression */
+  ICV_RIGHT_OF_COMMA, /* right operand of comma operator */
+  ICV_LEFT_OF_COMMA,  /* left operand of comma operator */
+  ICV_STATEMENT,      /* statement */
+  ICV_THIRD_IN_FOR    /* for increment expression */
+};
+
+/* BUILT_IN_FRONTEND function codes.  */
+enum rs_built_in_function
+{
+  RS_BUILT_IN_IS_CONSTANT_EVALUATED,
+  RS_BUILT_IN_INTEGER_PACK,
+  RS_BUILT_IN_IS_CORRESPONDING_MEMBER,
+  RS_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS,
+  RS_BUILT_IN_SOURCE_LOCATION,
+  RS_BUILT_IN_LAST
+};
+
+extern tree
+convert_to_void (tree expr, impl_conv_void implicit);
+
+// The lvalue-to-rvalue conversion (7.1) is applied if and only if the
+// expression is a glvalue of volatile-qualified type and it is one of the
+// following:
+// * ( expression ), where expression is one of these expressions,
+// * id-expression (8.1.4),
+// * subscripting (8.2.1),
+// * class member access (8.2.5),
+// * indirection (8.3.1),
+// * pointer-to-member operation (8.5),
+// * conditional expression (8.16) where both the second and the third
+//   operands are one of these expressions, or
+// * comma expression (8.19) where the right operand is one of these
+//   expressions.
+extern tree
+mark_discarded_use (tree expr);
+
+// Mark EXP as read, not just set, for set but not used -Wunused warning
+// purposes.
+extern void
+mark_exp_read (tree exp);
+
+// We've seen an actual use of EXPR.  Possibly replace an outer variable
+// reference inside with its constant value or a lambda capture.
+extern tree
+mark_use (tree expr, bool rvalue_p, bool read_p, location_t loc,
+	  bool reject_builtin);
+
+// Called whenever the expression EXPR is used in an rvalue context.
+// When REJECT_BUILTIN is true the expression is checked to make sure
+// it doesn't make it possible to obtain the address of a GCC built-in
+// function with no library fallback (or any of its bits, such as in
+// a conversion to bool).
+extern tree
+mark_rvalue_use (tree e, location_t loc /* = UNKNOWN_LOCATION */,
+		 bool reject_builtin /* = true */);
+
+// Called whenever an expression is used in an lvalue context.
+extern tree
+mark_lvalue_use (tree expr);
+
+// As above, but don't consider this use a read.
+extern tree
+mark_lvalue_use_nonread (tree expr);
+
+// We are using a reference VAL for its value. Bash that reference all the way
+// down to its lowest form.
+extern tree
+convert_from_reference (tree val);
+
+// Subroutine of convert_to_void.  Warn if we're discarding something with
+// attribute [[nodiscard]].
+extern void
+maybe_warn_nodiscard (tree expr, impl_conv_void implicit);
+
+extern location_t
+expr_loc_or_loc (const_tree t, location_t or_loc);
+
+extern location_t
+expr_loc_or_input_loc (const_tree t);
+
+// FN is the callee of a CALL_EXPR or AGGR_INIT_EXPR; return the FUNCTION_DECL
+// if we can.
+extern tree
+get_fndecl_from_callee (tree fn);
+
+// FIXME some helpers from HIRCompileBase could probably be moved here over time
+
+// Return an expression for the address of BASE[INDEX], used in offset intrinsic
+extern tree
+pointer_offset_expression (tree base_tree, tree index_tree, location_t locus);
+
+/* A tree node, together with a location, so that we can track locations
+   (and ranges) during parsing.
+
+   The location is redundant for node kinds that have locations,
+   but not all node kinds do (e.g. constants, and references to
+   params, locals, etc), so we stash a copy here.  */
+
+extern location_t rs_expr_location (const_tree);
+
+extern int
+is_empty_class (tree type);
+
+extern tree array_type_nelts_top (tree);
+
+extern bool
+is_really_empty_class (tree, bool);
+
+extern bool builtin_valid_in_constant_expr_p (const_tree);
+
+extern bool maybe_constexpr_fn (tree);
+
+extern bool var_in_maybe_constexpr_fn (tree);
+
+extern int
+rs_type_quals (const_tree type);
+
+extern bool decl_maybe_constant_var_p (tree);
+
+extern tree
+rs_walk_subtrees (tree *, int *, walk_tree_fn, void *, hash_set<tree> *);
+#define rs_walk_tree(tp, func, data, pset)                                     \
+  walk_tree_1 (tp, func, data, pset, rs_walk_subtrees)
+#define rs_walk_tree_without_duplicates(tp, func, data)                        \
+  walk_tree_without_duplicates_1 (tp, func, data, rs_walk_subtrees)
+
+// forked from gcc/cp/cp-tree.h cp_expr_loc_or_loc
+
+inline location_t
+rs_expr_loc_or_loc (const_tree t, location_t or_loc)
+{
+  location_t loc = rs_expr_location (t);
+  if (loc == UNKNOWN_LOCATION)
+    loc = or_loc;
+  return loc;
+}
+
+// forked from gcc/cp/cp-tree.h cp_expr_loc_or_input_loc
+
+inline location_t
+rs_expr_loc_or_input_loc (const_tree t)
+{
+  return rs_expr_loc_or_loc (t, input_location);
+}
+
+} // namespace Rust
+
+#endif // RUST_TREE
diff --git a/gcc/rust/rust-backend.h b/gcc/rust/rust-backend.h
new file mode 100644
index 00000000000..126283c1a54
--- /dev/null
+++ b/gcc/rust/rust-backend.h
@@ -0,0 +1,506 @@
+// 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_BACKEND_H
+#define RUST_BACKEND_H
+
+#include <gmp.h>
+#include <mpfr.h>
+#include <mpc.h>
+
+#include "rust-location.h"
+#include "rust-linemap.h"
+#include "rust-diagnostics.h"
+#include "operator.h"
+#include "tree.h"
+
+// Pointers to these types are created by the backend, passed to the
+// frontend, and passed back to the backend.  The types must be
+// defined by the backend using these names.
+
+// The backend representation of a variable.
+class Bvariable;
+
+// The backend interface.  This is a pure abstract class that a
+// specific backend will implement.
+
+class Backend
+{
+public:
+  virtual ~Backend () {}
+
+  // Name/type/location.  Used for function parameters, struct fields,
+  // interface methods.
+  struct typed_identifier
+  {
+    std::string name;
+    tree type;
+    Location location;
+
+    typed_identifier ()
+      : name (), type (NULL_TREE), location (Linemap::unknown_location ())
+    {}
+
+    typed_identifier (const std::string &a_name, tree a_type,
+		      Location a_location)
+      : name (a_name), type (a_type), location (a_location)
+    {}
+  };
+
+  // debug
+  virtual void debug (tree) = 0;
+  virtual void debug (Bvariable *) = 0;
+
+  virtual tree get_identifier_node (const std::string &str) = 0;
+
+  // Types.
+
+  // get unit-type
+  virtual tree unit_type () = 0;
+
+  // Get the unnamed boolean type.
+  virtual tree bool_type () = 0;
+
+  // Get the char type
+  virtual tree char_type () = 0;
+
+  // Get the wchar type
+  virtual tree wchar_type () = 0;
+
+  // Get the Host pointer size in bits
+  virtual int get_pointer_size () = 0;
+
+  // Get the raw str type const char*
+  virtual tree raw_str_type () = 0;
+
+  // Get an unnamed integer type with the given signedness and number
+  // of bits.
+  virtual tree integer_type (bool is_unsigned, int bits) = 0;
+
+  // Get an unnamed floating point type with the given number of bits
+  // (32 or 64).
+  virtual tree float_type (int bits) = 0;
+
+  // Get an unnamed complex type with the given number of bits (64 or 128).
+  virtual tree complex_type (int bits) = 0;
+
+  // Get a pointer type.
+  virtual tree pointer_type (tree to_type) = 0;
+
+  // Get a reference type.
+  virtual tree reference_type (tree to_type) = 0;
+
+  // make type immutable
+  virtual tree immutable_type (tree base) = 0;
+
+  // Get a function type.  The receiver, parameter, and results are
+  // generated from the types in the Function_type.  The Function_type
+  // is provided so that the names are available.  This should return
+  // not the type of a Go function (which is a pointer to a struct)
+  // but the type of a C function pointer (which will be used as the
+  // type of the first field of the struct).  If there is more than
+  // one result, RESULT_STRUCT is a struct type to hold the results,
+  // and RESULTS may be ignored; if there are zero or one results,
+  // RESULT_STRUCT is NULL.
+  virtual tree function_type (const typed_identifier &receiver,
+			      const std::vector<typed_identifier> &parameters,
+			      const std::vector<typed_identifier> &results,
+			      tree result_struct, Location location)
+    = 0;
+
+  virtual tree
+  function_type_varadic (const typed_identifier &receiver,
+			 const std::vector<typed_identifier> &parameters,
+			 const std::vector<typed_identifier> &results,
+			 tree result_struct, Location location)
+    = 0;
+
+  virtual tree function_ptr_type (tree result,
+				  const std::vector<tree> &praameters,
+				  Location location)
+    = 0;
+
+  // Get a struct type.
+  virtual tree struct_type (const std::vector<typed_identifier> &fields) = 0;
+
+  // Get a union type.
+  virtual tree union_type (const std::vector<typed_identifier> &fields) = 0;
+
+  // Get an array type.
+  virtual tree array_type (tree element_type, tree length) = 0;
+
+  // Return a named version of a type.  The location is the location
+  // of the type definition.  This will not be called for a type
+  // created via placeholder_pointer_type, placeholder_struct_type, or
+  // placeholder_array_type..  (It may be called for a pointer,
+  // struct, or array type in a case like "type P *byte; type Q P".)
+  virtual tree named_type (const std::string &name, tree, Location) = 0;
+
+  // Return the size of a type.
+  virtual int64_t type_size (tree) = 0;
+
+  // Return the alignment of a type.
+  virtual int64_t type_alignment (tree) = 0;
+
+  // Return the alignment of a struct field of this type.  This is
+  // normally the same as type_alignment, but not always.
+  virtual int64_t type_field_alignment (tree) = 0;
+
+  // Return the offset of field INDEX in a struct type.  INDEX is the
+  // entry in the FIELDS std::vector parameter of struct_type or
+  // set_placeholder_struct_type.
+  virtual int64_t type_field_offset (tree, size_t index) = 0;
+
+  // Expressions.
+
+  // Return an expression for a zero value of the given type.  This is
+  // used for cases such as local variable initialization and
+  // converting nil to other types.
+  virtual tree zero_expression (tree) = 0;
+
+  virtual tree unit_expression () = 0;
+
+  // Create a reference to a variable.
+  virtual tree var_expression (Bvariable *var, Location) = 0;
+
+  // Return an expression for the multi-precision integer VAL in BTYPE.
+  virtual tree integer_constant_expression (tree btype, mpz_t val) = 0;
+
+  // Return an expression for the floating point value VAL in BTYPE.
+  virtual tree float_constant_expression (tree btype, mpfr_t val) = 0;
+
+  // Return an expression for the complex value VAL in BTYPE.
+  virtual tree complex_constant_expression (tree btype, mpc_t val) = 0;
+
+  // Return an expression for the string value VAL.
+  virtual tree string_constant_expression (const std::string &val) = 0;
+
+  // Get a char literal
+  virtual tree char_constant_expression (char c) = 0;
+
+  // Get a char literal
+  virtual tree wchar_constant_expression (wchar_t c) = 0;
+
+  // Return an expression for the boolean value VAL.
+  virtual tree boolean_constant_expression (bool val) = 0;
+
+  // Return an expression for the real part of BCOMPLEX.
+  virtual tree real_part_expression (tree bcomplex, Location) = 0;
+
+  // Return an expression for the imaginary part of BCOMPLEX.
+  virtual tree imag_part_expression (tree bcomplex, Location) = 0;
+
+  // Return an expression for the complex number (BREAL, BIMAG).
+  virtual tree complex_expression (tree breal, tree bimag, Location) = 0;
+
+  // Return an expression that converts EXPR to TYPE.
+  virtual tree convert_expression (tree type, tree expr, Location) = 0;
+
+  // Return an expression for the field at INDEX in BSTRUCT.
+  virtual tree struct_field_expression (tree bstruct, size_t index, Location)
+    = 0;
+
+  // Create an expression that executes BSTAT before BEXPR.
+  virtual tree compound_expression (tree bstat, tree bexpr, Location) = 0;
+
+  // Return an expression that executes THEN_EXPR if CONDITION is true, or
+  // ELSE_EXPR otherwise and returns the result as type BTYPE, within the
+  // specified function FUNCTION.  ELSE_EXPR may be NULL.  BTYPE may be NULL.
+  virtual tree conditional_expression (tree function, tree btype,
+				       tree condition, tree then_expr,
+				       tree else_expr, Location)
+    = 0;
+
+  // Return an expression for the negation operation OP EXPR.
+  // Supported values of OP are enumerated in NegationOperator.
+  virtual tree negation_expression (NegationOperator op, tree expr, Location)
+    = 0;
+
+  // Return an expression for the operation LEFT OP RIGHT.
+  // Supported values of OP are enumerated in ArithmeticOrLogicalOperator.
+  virtual tree arithmetic_or_logical_expression (ArithmeticOrLogicalOperator op,
+						 tree left, tree right,
+						 Location)
+    = 0;
+
+  // Return an expression for the operation LEFT OP RIGHT.
+  // Supported values of OP are enumerated in ComparisonOperator.
+  virtual tree comparison_expression (ComparisonOperator op, tree left,
+				      tree right, Location)
+    = 0;
+
+  // Return an expression for the operation LEFT OP RIGHT.
+  // Supported values of OP are enumerated in LazyBooleanOperator.
+  virtual tree lazy_boolean_expression (LazyBooleanOperator op, tree left,
+					tree right, Location)
+    = 0;
+
+  // Return an expression that constructs BTYPE with VALS.  BTYPE must be the
+  // backend representation a of struct.  VALS must be in the same order as the
+  // corresponding fields in BTYPE.
+  virtual tree constructor_expression (tree btype, bool is_variant,
+				       const std::vector<tree> &vals, int,
+				       Location)
+    = 0;
+
+  // Return an expression that constructs an array of BTYPE with INDEXES and
+  // VALS.  INDEXES and VALS must have the same amount of elements. Each index
+  // in INDEXES must be in the same order as the corresponding value in VALS.
+  virtual tree
+  array_constructor_expression (tree btype,
+				const std::vector<unsigned long> &indexes,
+				const std::vector<tree> &vals, Location)
+    = 0;
+
+  virtual tree array_initializer (tree, tree, tree, tree, tree, tree *,
+				  Location)
+    = 0;
+
+  // Return an expression for ARRAY[INDEX] as an l-value.  ARRAY is a valid
+  // fixed-length array, not a slice.
+  virtual tree array_index_expression (tree array, tree index, Location) = 0;
+
+  // Create an expression for a call to FN with ARGS, taking place within
+  // caller CALLER.
+  virtual tree call_expression (tree fn, const std::vector<tree> &args,
+				tree static_chain, Location)
+    = 0;
+
+  // Statements.
+
+  // Create a variable initialization statement in the specified
+  // function.  This initializes a local variable at the point in the
+  // program flow where it is declared.
+  virtual tree init_statement (tree, Bvariable *var, tree init) = 0;
+
+  // Create an assignment statement within the specified function.
+  virtual tree assignment_statement (tree lhs, tree rhs, Location) = 0;
+
+  // Create a return statement, passing the representation of the
+  // function and the list of values to return.
+  virtual tree return_statement (tree, const std::vector<tree> &, Location) = 0;
+
+  // Create an if statement within a function.  ELSE_BLOCK may be NULL.
+  virtual tree if_statement (tree, tree condition, tree then_block,
+			     tree else_block, Location)
+    = 0;
+
+  // infinite loop expressions
+  virtual tree loop_expression (tree body, Location) = 0;
+
+  // exit expressions
+  virtual tree exit_expression (tree condition, Location) = 0;
+
+  // Create a single statement from two statements.
+  virtual tree compound_statement (tree, tree) = 0;
+
+  // Create a single statement from a list of statements.
+  virtual tree statement_list (const std::vector<tree> &) = 0;
+
+  // Create a statement that attempts to execute BSTAT and calls EXCEPT_STMT if
+  // an exception occurs. EXCEPT_STMT may be NULL.  FINALLY_STMT may be NULL and
+  // if not NULL, it will always be executed.  This is used for handling defers
+  // in Go functions.  In C++, the resulting code is of this form:
+  //   try { BSTAT; } catch { EXCEPT_STMT; } finally { FINALLY_STMT; }
+  virtual tree exception_handler_statement (tree bstat, tree except_stmt,
+					    tree finally_stmt, Location)
+    = 0;
+
+  // Blocks.
+
+  // Create a block.  The frontend will call this function when it
+  // starts converting a block within a function.  FUNCTION is the
+  // current function.  ENCLOSING is the enclosing block; it will be
+  // NULL for the top-level block in a function.  VARS is the list of
+  // local variables defined within this block; each entry will be
+  // created by the local_variable function.  START_LOCATION is the
+  // location of the start of the block, more or less the location of
+  // the initial curly brace.  END_LOCATION is the location of the end
+  // of the block, more or less the location of the final curly brace.
+  // The statements will be added after the block is created.
+  virtual tree block (tree function, tree enclosing,
+		      const std::vector<Bvariable *> &vars,
+		      Location start_location, Location end_location)
+    = 0;
+
+  // Add the statements to a block.  The block is created first.  Then
+  // the statements are created.  Then the statements are added to the
+  // block.  This will called exactly once per block.  The vector may
+  // be empty if there are no statements.
+  virtual void block_add_statements (tree, const std::vector<tree> &) = 0;
+
+  // Variables.
+
+  // Create an error variable.  This is used for cases which should
+  // not occur in a correct program, in order to keep the compilation
+  // going without crashing.
+  virtual Bvariable *error_variable () = 0;
+
+  // Create a global variable. NAME is the package-qualified name of
+  // the variable.  ASM_NAME is the encoded identifier for the
+  // variable, incorporating the package, and made safe for the
+  // assembler.  BTYPE is the type of the variable.  IS_EXTERNAL is
+  // true if the variable is defined in some other package.  IS_HIDDEN
+  // is true if the variable is not exported (name begins with a lower
+  // case letter).  IN_UNIQUE_SECTION is true if the variable should
+  // be put into a unique section if possible; this is intended to
+  // permit the linker to garbage collect the variable if it is not
+  // referenced.  LOCATION is where the variable was defined.
+  virtual Bvariable *global_variable (const std::string &name,
+				      const std::string &asm_name, tree btype,
+				      bool is_external, bool is_hidden,
+				      bool in_unique_section, Location location)
+    = 0;
+
+  // A global variable will 1) be initialized to zero, or 2) be
+  // initialized to a constant value, or 3) be initialized in the init
+  // function.  In case 2, the frontend will call
+  // global_variable_set_init to set the initial value.  If this is
+  // not called, the backend should initialize a global variable to 0.
+  // The init function may then assign a value to it.
+  virtual void global_variable_set_init (Bvariable *, tree) = 0;
+
+  // Create a local variable.  The frontend will create the local
+  // variables first, and then create the block which contains them.
+  // FUNCTION is the function in which the variable is defined.  NAME
+  // is the name of the variable.  TYPE is the type.  DECL_VAR, if not
+  // null, gives the location at which the value of this variable may
+  // be found, typically used to create an inner-scope reference to an
+  // outer-scope variable, to extend the lifetime of the variable beyond
+  // the inner scope.  IS_ADDRESS_TAKEN is true if the address of this
+  // variable is taken (this implies that the address does not escape
+  // the function, as otherwise the variable would be on the heap).
+  // LOCATION is where the variable is defined.  For each local variable
+  // the frontend will call init_statement to set the initial value.
+  virtual Bvariable *local_variable (tree function, const std::string &name,
+				     tree type, Bvariable *decl_var,
+				     Location location)
+    = 0;
+
+  // Create a function parameter.  This is an incoming parameter, not
+  // a result parameter (result parameters are treated as local
+  // variables).  The arguments are as for local_variable.
+  virtual Bvariable *parameter_variable (tree function, const std::string &name,
+					 tree type, Location location)
+    = 0;
+
+  // Create a static chain parameter.  This is the closure parameter.
+  virtual Bvariable *static_chain_variable (tree function,
+					    const std::string &name, tree type,
+					    Location location)
+    = 0;
+
+  // Create a temporary variable.  A temporary variable has no name,
+  // just a type.  We pass in FUNCTION and BLOCK in case they are
+  // needed.  If INIT is not NULL, the variable should be initialized
+  // to that value.  Otherwise the initial value is irrelevant--the
+  // backend does not have to explicitly initialize it to zero.
+  // ADDRESS_IS_TAKEN is true if the programs needs to take the
+  // address of this temporary variable.  LOCATION is the location of
+  // the statement or expression which requires creating the temporary
+  // variable, and may not be very useful.  This function should
+  // return a variable which can be referenced later and should set
+  // *PSTATEMENT to a statement which initializes the variable.
+  virtual Bvariable *temporary_variable (tree, tree, tree, tree init,
+					 bool address_is_taken,
+					 Location location, tree *pstatement)
+    = 0;
+
+  // Labels.
+
+  // Create a new label.  NAME will be empty if this is a label
+  // created by the frontend for a loop construct.  The location is
+  // where the label is defined.
+  virtual tree label (tree, const std::string &name, Location) = 0;
+
+  // Create a statement which defines a label.  This statement will be
+  // put into the codestream at the point where the label should be
+  // defined.
+  virtual tree label_definition_statement (tree) = 0;
+
+  // Create a goto statement to a label.
+  virtual tree goto_statement (tree, Location) = 0;
+
+  // Create an expression for the address of a label.  This is used to
+  // get the return address of a deferred function which may call
+  // recover.
+  virtual tree label_address (tree, Location) = 0;
+
+  // Functions.
+
+  // Bit flags to pass to the function method.
+
+  // Set if this is a function declaration rather than a definition;
+  // the definition will be in another compilation unit.
+  static const unsigned int function_is_declaration = 1 << 0;
+
+  // Set if the function should never be inlined because they call
+  // recover and must be visible for correct panic recovery.
+  static const unsigned int function_is_uninlinable = 1 << 1;
+
+  // Set if the function does not return.  This is set for the
+  // implementation of panic.
+  static const unsigned int function_does_not_return = 1 << 2;
+
+  // Set if the function should be put in a unique section if
+  // possible.  This is used for field tracking.
+  static const unsigned int function_in_unique_section = 1 << 3;
+
+  // Declare or define a function of FNTYPE.
+  // NAME is the Go name of the function.  ASM_NAME, if not the empty
+  // string, is the name that should be used in the symbol table; this
+  // will be non-empty if a magic extern comment is used.  FLAGS is
+  // bit flags described above.
+  virtual tree function (tree fntype, const std::string &name,
+			 const std::string &asm_name, unsigned int flags,
+			 Location)
+    = 0;
+
+  // Create a statement that runs all deferred calls for FUNCTION.  This should
+  // be a statement that looks like this in C++:
+  //   finish:
+  //     try { DEFER_RETURN; } catch { CHECK_DEFER; goto finish; }
+  virtual tree function_defer_statement (tree function, tree undefer,
+					 tree check_defer, Location)
+    = 0;
+
+  // Record PARAM_VARS as the variables to use for the parameters of FUNCTION.
+  // This will only be called for a function definition.  Returns true on
+  // success, false on failure.
+  virtual bool
+  function_set_parameters (tree function,
+			   const std::vector<Bvariable *> &param_vars)
+    = 0;
+
+  // Utility.
+
+  // Write the definitions for all TYPE_DECLS, CONSTANT_DECLS,
+  // FUNCTION_DECLS, and VARIABLE_DECLS declared globally.
+  virtual void
+  write_global_definitions (const std::vector<tree> &type_decls,
+			    const std::vector<tree> &constant_decls,
+			    const std::vector<tree> &function_decls,
+			    const std::vector<Bvariable *> &variable_decls)
+    = 0;
+
+  // Write SIZE bytes of export data from BYTES to the proper
+  // section in the output object file.
+  virtual void write_export_data (const char *bytes, unsigned int size) = 0;
+};
+
+#endif // RUST_BACKEND_H
diff --git a/gcc/rust/rust-gcc.cc b/gcc/rust/rust-gcc.cc
new file mode 100644
index 00000000000..ffb67f3fe78
--- /dev/null
+++ b/gcc/rust/rust-gcc.cc
@@ -0,0 +1,2717 @@
+// rust-gcc.cc -- Rust frontend to gcc IR.
+// Copyright (C) 2011-2022 Free Software Foundation, Inc.
+// Contributed by Ian Lance Taylor, Google.
+// forked from gccgo
+
+// 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/>.
+
+#include "rust-system.h"
+
+// This has to be included outside of extern "C", so we have to
+// include it here before tree.h includes it later.
+#include <gmp.h>
+
+#include "tree.h"
+#include "opts.h"
+#include "fold-const.h"
+#include "stringpool.h"
+#include "stor-layout.h"
+#include "varasm.h"
+#include "tree-iterator.h"
+#include "tm.h"
+#include "function.h"
+#include "cgraph.h"
+#include "convert.h"
+#include "gimple-expr.h"
+#include "gimplify.h"
+#include "langhooks.h"
+#include "toplev.h"
+#include "output.h"
+#include "realmpfr.h"
+#include "builtins.h"
+#include "print-tree.h"
+#include "attribs.h"
+
+#include "rust-location.h"
+#include "rust-linemap.h"
+#include "rust-backend.h"
+#include "rust-object-export.h"
+
+#include "backend/rust-tree.h"
+
+// TODO: this will have to be significantly modified to work with Rust
+
+// Bvariable is a bit more complicated, because of zero-sized types.
+// The GNU linker does not permit dynamic variables with zero size.
+// When we see such a variable, we generate a version of the type with
+// non-zero size.  However, when referring to the global variable, we
+// want an expression of zero size; otherwise, if, say, the global
+// variable is passed to a function, we will be passing a
+// non-zero-sized value to a zero-sized value, which can lead to a
+// miscompilation.
+
+class Bvariable
+{
+public:
+  Bvariable (tree t) : t_ (t), orig_type_ (NULL) {}
+
+  Bvariable (tree t, tree orig_type) : t_ (t), orig_type_ (orig_type) {}
+
+  // Get the tree for use as an expression.
+  tree get_tree (Location) const;
+
+  // Get the actual decl;
+  tree get_decl () const { return this->t_; }
+
+private:
+  tree t_;
+  tree orig_type_;
+};
+
+// Get the tree of a variable for use as an expression.  If this is a
+// zero-sized global, create an expression that refers to the decl but
+// has zero size.
+tree
+Bvariable::get_tree (Location location) const
+{
+  if (this->t_ == error_mark_node)
+    return error_mark_node;
+
+  TREE_USED (this->t_) = 1;
+  if (this->orig_type_ == NULL || TREE_TYPE (this->t_) == this->orig_type_)
+    {
+      return this->t_;
+    }
+
+  // Return *(orig_type*)&decl.  */
+  tree t = build_fold_addr_expr_loc (location.gcc_location (), this->t_);
+  t = fold_build1_loc (location.gcc_location (), NOP_EXPR,
+		       build_pointer_type (this->orig_type_), t);
+  return build_fold_indirect_ref_loc (location.gcc_location (), t);
+}
+
+// This file implements the interface between the Rust frontend proper
+// and the gcc IR.  This implements specific instantiations of
+// abstract classes defined by the Rust frontend proper.  The Rust
+// frontend proper class methods of these classes to generate the
+// backend representation.
+
+class Gcc_backend : public Backend
+{
+public:
+  Gcc_backend ();
+
+  void debug (tree t) { debug_tree (t); };
+  void debug (Bvariable *t) { debug_tree (t->get_decl ()); };
+
+  tree get_identifier_node (const std::string &str)
+  {
+    return get_identifier_with_length (str.data (), str.length ());
+  }
+
+  // Types.
+
+  tree unit_type ()
+  {
+    static tree unit_type;
+    if (unit_type == nullptr)
+      {
+	auto unit_type_node = struct_type ({});
+	unit_type = named_type ("()", unit_type_node,
+				::Linemap::predeclared_location ());
+      }
+
+    return unit_type;
+  }
+
+  tree bool_type () { return boolean_type_node; }
+
+  tree char_type () { return char_type_node; }
+
+  tree wchar_type ()
+  {
+    tree wchar = make_unsigned_type (32);
+    TYPE_STRING_FLAG (wchar) = 1;
+    return wchar;
+  }
+
+  int get_pointer_size ();
+
+  tree raw_str_type ();
+
+  tree integer_type (bool, int);
+
+  tree float_type (int);
+
+  tree complex_type (int);
+
+  tree pointer_type (tree);
+
+  tree reference_type (tree);
+
+  tree immutable_type (tree);
+
+  tree function_type (const typed_identifier &,
+		      const std::vector<typed_identifier> &,
+		      const std::vector<typed_identifier> &, tree,
+		      const Location);
+
+  tree function_type_varadic (const typed_identifier &,
+			      const std::vector<typed_identifier> &,
+			      const std::vector<typed_identifier> &, tree,
+			      const Location);
+
+  tree function_ptr_type (tree, const std::vector<tree> &, Location);
+
+  tree struct_type (const std::vector<typed_identifier> &);
+
+  tree union_type (const std::vector<typed_identifier> &);
+
+  tree array_type (tree, tree);
+
+  tree named_type (const std::string &, tree, Location);
+
+  int64_t type_size (tree);
+
+  int64_t type_alignment (tree);
+
+  int64_t type_field_alignment (tree);
+
+  int64_t type_field_offset (tree, size_t index);
+
+  // Expressions.
+
+  tree zero_expression (tree);
+
+  tree unit_expression () { return integer_zero_node; }
+
+  tree var_expression (Bvariable *var, Location);
+
+  tree integer_constant_expression (tree type, mpz_t val);
+
+  tree float_constant_expression (tree type, mpfr_t val);
+
+  tree complex_constant_expression (tree type, mpc_t val);
+
+  tree string_constant_expression (const std::string &val);
+
+  tree wchar_constant_expression (wchar_t c);
+
+  tree char_constant_expression (char c);
+
+  tree boolean_constant_expression (bool val);
+
+  tree real_part_expression (tree bcomplex, Location);
+
+  tree imag_part_expression (tree bcomplex, Location);
+
+  tree complex_expression (tree breal, tree bimag, Location);
+
+  tree convert_expression (tree type, tree expr, Location);
+
+  tree struct_field_expression (tree, size_t, Location);
+
+  tree compound_expression (tree, tree, Location);
+
+  tree conditional_expression (tree, tree, tree, tree, tree, Location);
+
+  tree negation_expression (NegationOperator op, tree expr, Location);
+
+  tree arithmetic_or_logical_expression (ArithmeticOrLogicalOperator op,
+					 tree left, tree right, Location);
+
+  tree comparison_expression (ComparisonOperator op, tree left, tree right,
+			      Location);
+
+  tree lazy_boolean_expression (LazyBooleanOperator op, tree left, tree right,
+				Location);
+
+  tree constructor_expression (tree, bool, const std::vector<tree> &, int,
+			       Location);
+
+  tree array_constructor_expression (tree, const std::vector<unsigned long> &,
+				     const std::vector<tree> &, Location);
+
+  tree array_initializer (tree, tree, tree, tree, tree, tree *, Location);
+
+  tree array_index_expression (tree array, tree index, Location);
+
+  tree call_expression (tree fn, const std::vector<tree> &args,
+			tree static_chain, Location);
+
+  // Statements.
+
+  tree init_statement (tree, Bvariable *var, tree init);
+
+  tree assignment_statement (tree lhs, tree rhs, Location);
+
+  tree return_statement (tree, const std::vector<tree> &, Location);
+
+  tree if_statement (tree, tree condition, tree then_block, tree else_block,
+		     Location);
+
+  tree compound_statement (tree, tree);
+
+  tree statement_list (const std::vector<tree> &);
+
+  tree exception_handler_statement (tree bstat, tree except_stmt,
+				    tree finally_stmt, Location);
+
+  tree loop_expression (tree body, Location);
+
+  tree exit_expression (tree condition, Location);
+
+  // Blocks.
+
+  tree block (tree, tree, const std::vector<Bvariable *> &, Location, Location);
+
+  void block_add_statements (tree, const std::vector<tree> &);
+
+  // Variables.
+
+  Bvariable *error_variable () { return new Bvariable (error_mark_node); }
+
+  Bvariable *global_variable (const std::string &var_name,
+			      const std::string &asm_name, tree type,
+			      bool is_external, bool is_hidden,
+			      bool in_unique_section, Location location);
+
+  void global_variable_set_init (Bvariable *, tree);
+
+  Bvariable *local_variable (tree, const std::string &, tree, Bvariable *,
+			     Location);
+
+  Bvariable *parameter_variable (tree, const std::string &, tree, Location);
+
+  Bvariable *static_chain_variable (tree, const std::string &, tree, Location);
+
+  Bvariable *temporary_variable (tree, tree, tree, tree, bool, Location,
+				 tree *);
+
+  // Labels.
+
+  tree label (tree, const std::string &name, Location);
+
+  tree label_definition_statement (tree);
+
+  tree goto_statement (tree, Location);
+
+  tree label_address (tree, Location);
+
+  // Functions.
+
+  tree function (tree fntype, const std::string &name,
+		 const std::string &asm_name, unsigned int flags, Location);
+
+  tree function_defer_statement (tree function, tree undefer, tree defer,
+				 Location);
+
+  bool function_set_parameters (tree function,
+				const std::vector<Bvariable *> &);
+
+  void write_global_definitions (const std::vector<tree> &,
+				 const std::vector<tree> &,
+				 const std::vector<tree> &,
+				 const std::vector<Bvariable *> &);
+
+  void write_export_data (const char *bytes, unsigned int size);
+
+private:
+  tree fill_in_fields (tree, const std::vector<typed_identifier> &);
+
+  tree fill_in_array (tree, tree, tree);
+
+  tree non_zero_size_type (tree);
+
+  tree convert_tree (tree, tree, Location);
+};
+
+// A helper function to create a GCC identifier from a C++ string.
+
+static inline tree
+get_identifier_from_string (const std::string &str)
+{
+  return get_identifier_with_length (str.data (), str.length ());
+}
+
+// Define the built-in functions that are exposed to GCCRust.
+
+Gcc_backend::Gcc_backend ()
+{
+  /* We need to define the fetch_and_add functions, since we use them
+     for ++ and --.  */
+  // tree t = this->integer_type (true, BITS_PER_UNIT)->get_tree ();
+  // tree p = build_pointer_type (build_qualified_type (t, TYPE_QUAL_VOLATILE));
+  // this->define_builtin (BUILT_IN_SYNC_ADD_AND_FETCH_1,
+  // "__sync_fetch_and_add_1",
+  //       		NULL, build_function_type_list (t, p, t, NULL_TREE), 0);
+
+  // t = this->integer_type (true, BITS_PER_UNIT * 2)->get_tree ();
+  // p = build_pointer_type (build_qualified_type (t, TYPE_QUAL_VOLATILE));
+  // this->define_builtin (BUILT_IN_SYNC_ADD_AND_FETCH_2,
+  // "__sync_fetch_and_add_2",
+  //       		NULL, build_function_type_list (t, p, t, NULL_TREE), 0);
+
+  // t = this->integer_type (true, BITS_PER_UNIT * 4)->get_tree ();
+  // p = build_pointer_type (build_qualified_type (t, TYPE_QUAL_VOLATILE));
+  // this->define_builtin (BUILT_IN_SYNC_ADD_AND_FETCH_4,
+  // "__sync_fetch_and_add_4",
+  //       		NULL, build_function_type_list (t, p, t, NULL_TREE), 0);
+
+  // t = this->integer_type (true, BITS_PER_UNIT * 8)->get_tree ();
+  // p = build_pointer_type (build_qualified_type (t, TYPE_QUAL_VOLATILE));
+  // this->define_builtin (BUILT_IN_SYNC_ADD_AND_FETCH_8,
+  // "__sync_fetch_and_add_8",
+  //       		NULL, build_function_type_list (t, p, t, NULL_TREE), 0);
+
+  // // We use __builtin_expect for magic import functions.
+  // this->define_builtin (BUILT_IN_EXPECT, "__builtin_expect", NULL,
+  //       		build_function_type_list (long_integer_type_node,
+  //       					  long_integer_type_node,
+  //       					  long_integer_type_node,
+  //       					  NULL_TREE),
+  //       		builtin_const);
+
+  // // We use __builtin_memcmp for struct comparisons.
+  // this->define_builtin (BUILT_IN_MEMCMP, "__builtin_memcmp", "memcmp",
+  //       		build_function_type_list (integer_type_node,
+  //       					  const_ptr_type_node,
+  //       					  const_ptr_type_node,
+  //       					  size_type_node, NULL_TREE),
+  //       		0);
+
+  // // We use __builtin_memmove for copying data.
+  // this->define_builtin (BUILT_IN_MEMMOVE, "__builtin_memmove", "memmove",
+  //       		build_function_type_list (void_type_node, ptr_type_node,
+  //       					  const_ptr_type_node,
+  //       					  size_type_node, NULL_TREE),
+  //       		0);
+
+  // // We use __builtin_memset for zeroing data.
+  // this->define_builtin (BUILT_IN_MEMSET, "__builtin_memset", "memset",
+  //       		build_function_type_list (void_type_node, ptr_type_node,
+  //       					  integer_type_node,
+  //       					  size_type_node, NULL_TREE),
+  //       		0);
+
+  // // Used by runtime/internal/sys and math/bits.
+  // this->define_builtin (BUILT_IN_CTZ, "__builtin_ctz", "ctz",
+  //       		build_function_type_list (integer_type_node,
+  //       					  unsigned_type_node,
+  //       					  NULL_TREE),
+  //       		builtin_const);
+  // this->define_builtin (BUILT_IN_CTZLL, "__builtin_ctzll", "ctzll",
+  //       		build_function_type_list (integer_type_node,
+  //       					  long_long_unsigned_type_node,
+  //       					  NULL_TREE),
+  //       		builtin_const);
+  // this->define_builtin (BUILT_IN_CLZ, "__builtin_clz", "clz",
+  //       		build_function_type_list (integer_type_node,
+  //       					  unsigned_type_node,
+  //       					  NULL_TREE),
+  //       		builtin_const);
+  // this->define_builtin (BUILT_IN_CLZLL, "__builtin_clzll", "clzll",
+  //       		build_function_type_list (integer_type_node,
+  //       					  long_long_unsigned_type_node,
+  //       					  NULL_TREE),
+  //       		builtin_const);
+  // this->define_builtin (BUILT_IN_POPCOUNT, "__builtin_popcount", "popcount",
+  //       		build_function_type_list (integer_type_node,
+  //       					  unsigned_type_node,
+  //       					  NULL_TREE),
+  //       		builtin_const);
+  // this->define_builtin (BUILT_IN_POPCOUNTLL, "__builtin_popcountll",
+  //       		"popcountll",
+  //       		build_function_type_list (integer_type_node,
+  //       					  long_long_unsigned_type_node,
+  //       					  NULL_TREE),
+  //       		builtin_const);
+  // this->define_builtin (BUILT_IN_BSWAP16, "__builtin_bswap16", "bswap16",
+  //       		build_function_type_list (uint16_type_node,
+  //       					  uint16_type_node, NULL_TREE),
+  //       		builtin_const);
+  // this->define_builtin (BUILT_IN_BSWAP32, "__builtin_bswap32", "bswap32",
+  //       		build_function_type_list (uint32_type_node,
+  //       					  uint32_type_node, NULL_TREE),
+  //       		builtin_const);
+  // this->define_builtin (BUILT_IN_BSWAP64, "__builtin_bswap64", "bswap64",
+  //       		build_function_type_list (uint64_type_node,
+  //       					  uint64_type_node, NULL_TREE),
+  //       		builtin_const);
+
+  // We provide some functions for the math library.
+
+  // We use __builtin_return_address in the thunk we build for
+  // functions which call recover, and for runtime.getcallerpc.
+  // t = build_function_type_list (ptr_type_node, unsigned_type_node,
+  // NULL_TREE); this->define_builtin (BUILT_IN_RETURN_ADDRESS,
+  // "__builtin_return_address",
+  //       		NULL, t, 0);
+
+  // The runtime calls __builtin_dwarf_cfa for runtime.getcallersp.
+  // t = build_function_type_list (ptr_type_node, NULL_TREE);
+  // this->define_builtin (BUILT_IN_DWARF_CFA, "__builtin_dwarf_cfa", NULL, t,
+  // 0);
+
+  // The runtime calls __builtin_extract_return_addr when recording
+  // the address to which a function returns.
+  // this->define_builtin (
+  //   BUILT_IN_EXTRACT_RETURN_ADDR, "__builtin_extract_return_addr", NULL,
+  //   build_function_type_list (ptr_type_node, ptr_type_node, NULL_TREE), 0);
+
+  // The compiler uses __builtin_trap for some exception handling
+  // cases.
+  // this->define_builtin (BUILT_IN_TRAP, "__builtin_trap", NULL,
+  //       		build_function_type (void_type_node, void_list_node),
+  //       		builtin_noreturn);
+
+  // The runtime uses __builtin_prefetch.
+  // this->define_builtin (BUILT_IN_PREFETCH, "__builtin_prefetch", NULL,
+  //       		build_varargs_function_type_list (void_type_node,
+  //       						  const_ptr_type_node,
+  //       						  NULL_TREE),
+  //       		builtin_novops);
+
+  // The compiler uses __builtin_unreachable for cases that cannot
+  // occur.
+  // this->define_builtin (BUILT_IN_UNREACHABLE, "__builtin_unreachable", NULL,
+  //       		build_function_type (void_type_node, void_list_node),
+  //       		builtin_const | builtin_noreturn);
+
+  // We provide some atomic functions.
+  // t = build_function_type_list (uint32_type_node, ptr_type_node,
+  //       			integer_type_node, NULL_TREE);
+  // this->define_builtin (BUILT_IN_ATOMIC_LOAD_4, "__atomic_load_4", NULL, t,
+  // 0);
+
+  // t = build_function_type_list (uint64_type_node, ptr_type_node,
+  //       			integer_type_node, NULL_TREE);
+  // this->define_builtin (BUILT_IN_ATOMIC_LOAD_8, "__atomic_load_8", NULL, t,
+  // 0);
+
+  // t = build_function_type_list (void_type_node, ptr_type_node,
+  // uint32_type_node,
+  //       			integer_type_node, NULL_TREE);
+  // this->define_builtin (BUILT_IN_ATOMIC_STORE_4, "__atomic_store_4", NULL, t,
+  //       		0);
+
+  // t = build_function_type_list (void_type_node, ptr_type_node,
+  // uint64_type_node,
+  //       			integer_type_node, NULL_TREE);
+  // this->define_builtin (BUILT_IN_ATOMIC_STORE_8, "__atomic_store_8", NULL, t,
+  //       		0);
+
+  // t = build_function_type_list (uint32_type_node, ptr_type_node,
+  //       			uint32_type_node, integer_type_node, NULL_TREE);
+  // this->define_builtin (BUILT_IN_ATOMIC_EXCHANGE_4, "__atomic_exchange_4",
+  // NULL,
+  //       		t, 0);
+
+  // t = build_function_type_list (uint64_type_node, ptr_type_node,
+  //       			uint64_type_node, integer_type_node, NULL_TREE);
+  // this->define_builtin (BUILT_IN_ATOMIC_EXCHANGE_8, "__atomic_exchange_8",
+  // NULL,
+  //       		t, 0);
+
+  // t = build_function_type_list (boolean_type_node, ptr_type_node,
+  // ptr_type_node,
+  //       			uint32_type_node, boolean_type_node,
+  //       			integer_type_node, integer_type_node,
+  //       			NULL_TREE);
+  // this->define_builtin (BUILT_IN_ATOMIC_COMPARE_EXCHANGE_4,
+  //       		"__atomic_compare_exchange_4", NULL, t, 0);
+
+  // t = build_function_type_list (boolean_type_node, ptr_type_node,
+  // ptr_type_node,
+  //       			uint64_type_node, boolean_type_node,
+  //       			integer_type_node, integer_type_node,
+  //       			NULL_TREE);
+  // this->define_builtin (BUILT_IN_ATOMIC_COMPARE_EXCHANGE_8,
+  //       		"__atomic_compare_exchange_8", NULL, t, 0);
+
+  // t = build_function_type_list (uint32_type_node, ptr_type_node,
+  //       			uint32_type_node, integer_type_node, NULL_TREE);
+  // this->define_builtin (BUILT_IN_ATOMIC_ADD_FETCH_4, "__atomic_add_fetch_4",
+  //       		NULL, t, 0);
+
+  // t = build_function_type_list (uint64_type_node, ptr_type_node,
+  //       			uint64_type_node, integer_type_node, NULL_TREE);
+  // this->define_builtin (BUILT_IN_ATOMIC_ADD_FETCH_8, "__atomic_add_fetch_8",
+  //       		NULL, t, 0);
+
+  // t = build_function_type_list (unsigned_char_type_node, ptr_type_node,
+  //       			unsigned_char_type_node, integer_type_node,
+  //       			NULL_TREE);
+  // this->define_builtin (BUILT_IN_ATOMIC_AND_FETCH_1, "__atomic_and_fetch_1",
+  //       		NULL, t, 0);
+  // this->define_builtin (BUILT_IN_ATOMIC_FETCH_AND_1, "__atomic_fetch_and_1",
+  //       		NULL, t, 0);
+
+  // t = build_function_type_list (unsigned_char_type_node, ptr_type_node,
+  //       			unsigned_char_type_node, integer_type_node,
+  //       			NULL_TREE);
+  // this->define_builtin (BUILT_IN_ATOMIC_OR_FETCH_1, "__atomic_or_fetch_1",
+  // NULL,
+  //       		t, 0);
+  // this->define_builtin (BUILT_IN_ATOMIC_FETCH_OR_1, "__atomic_fetch_or_1",
+  // NULL,
+  //       		t, 0);
+}
+
+// Get an unnamed integer type.
+
+int
+Gcc_backend::get_pointer_size ()
+{
+  return POINTER_SIZE;
+}
+
+tree
+Gcc_backend::raw_str_type ()
+{
+  tree char_ptr = build_pointer_type (char_type_node);
+  tree const_char_type = build_qualified_type (char_ptr, TYPE_QUAL_CONST);
+  return const_char_type;
+}
+
+tree
+Gcc_backend::integer_type (bool is_unsigned, int bits)
+{
+  tree type;
+  if (is_unsigned)
+    {
+      if (bits == INT_TYPE_SIZE)
+	type = unsigned_type_node;
+      else if (bits == SHORT_TYPE_SIZE)
+	type = short_unsigned_type_node;
+      else if (bits == LONG_TYPE_SIZE)
+	type = long_unsigned_type_node;
+      else if (bits == LONG_LONG_TYPE_SIZE)
+	type = long_long_unsigned_type_node;
+      else
+	type = make_unsigned_type (bits);
+    }
+  else
+    {
+      if (bits == INT_TYPE_SIZE)
+	type = integer_type_node;
+      else if (bits == SHORT_TYPE_SIZE)
+	type = short_integer_type_node;
+      else if (bits == LONG_TYPE_SIZE)
+	type = long_integer_type_node;
+      else if (bits == LONG_LONG_TYPE_SIZE)
+	type = long_long_integer_type_node;
+      else
+	type = make_signed_type (bits);
+    }
+  return type;
+}
+
+// Get an unnamed float type.
+
+tree
+Gcc_backend::float_type (int bits)
+{
+  tree type;
+  if (bits == FLOAT_TYPE_SIZE)
+    type = float_type_node;
+  else if (bits == DOUBLE_TYPE_SIZE)
+    type = double_type_node;
+  else if (bits == LONG_DOUBLE_TYPE_SIZE)
+    type = long_double_type_node;
+  else
+    {
+      type = make_node (REAL_TYPE);
+      TYPE_PRECISION (type) = bits;
+      layout_type (type);
+    }
+  return type;
+}
+
+// Get an unnamed complex type.
+
+tree
+Gcc_backend::complex_type (int bits)
+{
+  tree type;
+  if (bits == FLOAT_TYPE_SIZE * 2)
+    type = complex_float_type_node;
+  else if (bits == DOUBLE_TYPE_SIZE * 2)
+    type = complex_double_type_node;
+  else if (bits == LONG_DOUBLE_TYPE_SIZE * 2)
+    type = complex_long_double_type_node;
+  else
+    {
+      type = make_node (REAL_TYPE);
+      TYPE_PRECISION (type) = bits / 2;
+      layout_type (type);
+      type = build_complex_type (type);
+    }
+  return type;
+}
+
+// Get a pointer type.
+
+tree
+Gcc_backend::pointer_type (tree to_type)
+{
+  if (to_type == error_mark_node)
+    return error_mark_node;
+  tree type = build_pointer_type (to_type);
+  return type;
+}
+
+// Get a reference type.
+
+tree
+Gcc_backend::reference_type (tree to_type)
+{
+  if (to_type == error_mark_node)
+    return error_mark_node;
+  tree type = build_reference_type (to_type);
+  return type;
+}
+
+// Get immutable type
+
+tree
+Gcc_backend::immutable_type (tree base)
+{
+  if (base == error_mark_node)
+    return error_mark_node;
+  tree constified = build_qualified_type (base, TYPE_QUAL_CONST);
+  return constified;
+}
+
+// Make a function type.
+
+tree
+Gcc_backend::function_type (const typed_identifier &receiver,
+			    const std::vector<typed_identifier> &parameters,
+			    const std::vector<typed_identifier> &results,
+			    tree result_struct, Location)
+{
+  tree args = NULL_TREE;
+  tree *pp = &args;
+  if (receiver.type != NULL_TREE)
+    {
+      tree t = receiver.type;
+      if (t == error_mark_node)
+	return error_mark_node;
+      *pp = tree_cons (NULL_TREE, t, NULL_TREE);
+      pp = &TREE_CHAIN (*pp);
+    }
+
+  for (std::vector<typed_identifier>::const_iterator p = parameters.begin ();
+       p != parameters.end (); ++p)
+    {
+      tree t = p->type;
+      if (t == error_mark_node)
+	return error_mark_node;
+      *pp = tree_cons (NULL_TREE, t, NULL_TREE);
+      pp = &TREE_CHAIN (*pp);
+    }
+
+  // Varargs is handled entirely at the Rust level.  When converted to
+  // GENERIC functions are not varargs.
+  *pp = void_list_node;
+
+  tree result;
+  if (results.empty ())
+    result = void_type_node;
+  else if (results.size () == 1)
+    result = results.front ().type;
+  else
+    {
+      gcc_assert (result_struct != NULL);
+      result = result_struct;
+    }
+  if (result == error_mark_node)
+    return error_mark_node;
+
+  // The libffi library cannot represent a zero-sized object.  To
+  // avoid causing confusion on 32-bit SPARC, we treat a function that
+  // returns a zero-sized value as returning void.  That should do no
+  // harm since there is no actual value to be returned.  See
+  // https://gcc.gnu.org/PR72814 for details.
+  if (result != void_type_node && int_size_in_bytes (result) == 0)
+    result = void_type_node;
+
+  tree fntype = build_function_type (result, args);
+  if (fntype == error_mark_node)
+    return error_mark_node;
+
+  return build_pointer_type (fntype);
+}
+
+tree
+Gcc_backend::function_type_varadic (
+  const typed_identifier &receiver,
+  const std::vector<typed_identifier> &parameters,
+  const std::vector<typed_identifier> &results, tree result_struct, Location)
+{
+  size_t n = parameters.size () + (receiver.type != NULL_TREE ? 1 : 0);
+  tree *args = XALLOCAVEC (tree, n);
+  size_t offs = 0;
+
+  if (receiver.type != NULL_TREE)
+    {
+      tree t = receiver.type;
+      if (t == error_mark_node)
+	return error_mark_node;
+
+      args[offs++] = t;
+    }
+
+  for (std::vector<typed_identifier>::const_iterator p = parameters.begin ();
+       p != parameters.end (); ++p)
+    {
+      tree t = p->type;
+      if (t == error_mark_node)
+	return error_mark_node;
+      args[offs++] = t;
+    }
+
+  tree result;
+  if (results.empty ())
+    result = void_type_node;
+  else if (results.size () == 1)
+    result = results.front ().type;
+  else
+    {
+      gcc_assert (result_struct != NULL_TREE);
+      result = result_struct;
+    }
+  if (result == error_mark_node)
+    return error_mark_node;
+
+  // The libffi library cannot represent a zero-sized object.  To
+  // avoid causing confusion on 32-bit SPARC, we treat a function that
+  // returns a zero-sized value as returning void.  That should do no
+  // harm since there is no actual value to be returned.  See
+  // https://gcc.gnu.org/PR72814 for details.
+  if (result != void_type_node && int_size_in_bytes (result) == 0)
+    result = void_type_node;
+
+  tree fntype = build_varargs_function_type_array (result, n, args);
+  if (fntype == error_mark_node)
+    return error_mark_node;
+
+  return build_pointer_type (fntype);
+}
+
+tree
+Gcc_backend::function_ptr_type (tree result_type,
+				const std::vector<tree> &parameters,
+				Location /* locus */)
+{
+  tree args = NULL_TREE;
+  tree *pp = &args;
+
+  for (auto &param : parameters)
+    {
+      if (param == error_mark_node)
+	return error_mark_node;
+
+      *pp = tree_cons (NULL_TREE, param, NULL_TREE);
+      pp = &TREE_CHAIN (*pp);
+    }
+
+  *pp = void_list_node;
+
+  tree result = result_type;
+  if (result != void_type_node && int_size_in_bytes (result) == 0)
+    result = void_type_node;
+
+  tree fntype = build_function_type (result, args);
+  if (fntype == error_mark_node)
+    return error_mark_node;
+
+  return build_pointer_type (fntype);
+}
+
+// Make a struct type.
+
+tree
+Gcc_backend::struct_type (const std::vector<typed_identifier> &fields)
+{
+  return this->fill_in_fields (make_node (RECORD_TYPE), fields);
+}
+
+// Make a union type.
+
+tree
+Gcc_backend::union_type (const std::vector<typed_identifier> &fields)
+{
+  return this->fill_in_fields (make_node (UNION_TYPE), fields);
+}
+
+// Fill in the fields of a struct or union type.
+
+tree
+Gcc_backend::fill_in_fields (tree fill,
+			     const std::vector<typed_identifier> &fields)
+{
+  tree field_trees = NULL_TREE;
+  tree *pp = &field_trees;
+  for (std::vector<typed_identifier>::const_iterator p = fields.begin ();
+       p != fields.end (); ++p)
+    {
+      tree name_tree = get_identifier_from_string (p->name);
+      tree type_tree = p->type;
+      if (type_tree == error_mark_node)
+	return error_mark_node;
+      tree field = build_decl (p->location.gcc_location (), FIELD_DECL,
+			       name_tree, type_tree);
+      DECL_CONTEXT (field) = fill;
+      *pp = field;
+      pp = &DECL_CHAIN (field);
+    }
+  TYPE_FIELDS (fill) = field_trees;
+  layout_type (fill);
+
+  // Because Rust permits converting between named struct types and
+  // equivalent struct types, for which we use VIEW_CONVERT_EXPR, and
+  // because we don't try to maintain TYPE_CANONICAL for struct types,
+  // we need to tell the middle-end to use structural equality.
+  SET_TYPE_STRUCTURAL_EQUALITY (fill);
+
+  return fill;
+}
+
+// Make an array type.
+
+tree
+Gcc_backend::array_type (tree element_type, tree length)
+{
+  return this->fill_in_array (make_node (ARRAY_TYPE), element_type, length);
+}
+
+// Fill in an array type.
+
+tree
+Gcc_backend::fill_in_array (tree fill, tree element_type, tree length_tree)
+{
+  if (element_type == error_mark_node || length_tree == error_mark_node)
+    return error_mark_node;
+
+  gcc_assert (TYPE_SIZE (element_type) != NULL_TREE);
+
+  length_tree = fold_convert (sizetype, length_tree);
+
+  // build_index_type takes the maximum index, which is one less than
+  // the length.
+  tree index_type_tree = build_index_type (
+    fold_build2 (MINUS_EXPR, sizetype, length_tree, size_one_node));
+
+  TREE_TYPE (fill) = element_type;
+  TYPE_DOMAIN (fill) = index_type_tree;
+  TYPE_ADDR_SPACE (fill) = TYPE_ADDR_SPACE (element_type);
+  layout_type (fill);
+
+  if (TYPE_STRUCTURAL_EQUALITY_P (element_type))
+    SET_TYPE_STRUCTURAL_EQUALITY (fill);
+  else if (TYPE_CANONICAL (element_type) != element_type
+	   || TYPE_CANONICAL (index_type_tree) != index_type_tree)
+    TYPE_CANONICAL (fill) = build_array_type (TYPE_CANONICAL (element_type),
+					      TYPE_CANONICAL (index_type_tree));
+
+  return fill;
+}
+
+// Return a named version of a type.
+
+tree
+Gcc_backend::named_type (const std::string &name, tree type, Location location)
+{
+  if (type == error_mark_node)
+    return error_mark_node;
+
+  // The middle-end expects a basic type to have a name.  In Rust every
+  // basic type will have a name.  The first time we see a basic type,
+  // give it whatever Rust name we have at this point.
+  if (TYPE_NAME (type) == NULL_TREE
+      && location.gcc_location () == BUILTINS_LOCATION
+      && (TREE_CODE (type) == INTEGER_TYPE || TREE_CODE (type) == REAL_TYPE
+	  || TREE_CODE (type) == COMPLEX_TYPE
+	  || TREE_CODE (type) == BOOLEAN_TYPE))
+    {
+      tree decl = build_decl (BUILTINS_LOCATION, TYPE_DECL,
+			      get_identifier_from_string (name), type);
+      TYPE_NAME (type) = decl;
+      return type;
+    }
+
+  tree copy = build_variant_type_copy (type);
+  tree decl = build_decl (location.gcc_location (), TYPE_DECL,
+			  get_identifier_from_string (name), copy);
+  DECL_ORIGINAL_TYPE (decl) = type;
+  TYPE_NAME (copy) = decl;
+  return copy;
+}
+
+// Return the size of a type.
+
+int64_t
+Gcc_backend::type_size (tree t)
+{
+  if (t == error_mark_node)
+    return 1;
+  if (t == void_type_node)
+    return 0;
+  t = TYPE_SIZE_UNIT (t);
+  gcc_assert (tree_fits_uhwi_p (t));
+  unsigned HOST_WIDE_INT val_wide = TREE_INT_CST_LOW (t);
+  int64_t ret = static_cast<int64_t> (val_wide);
+  if (ret < 0 || static_cast<unsigned HOST_WIDE_INT> (ret) != val_wide)
+    return -1;
+  return ret;
+}
+
+// Return the alignment of a type.
+
+int64_t
+Gcc_backend::type_alignment (tree t)
+{
+  if (t == error_mark_node)
+    return 1;
+  return TYPE_ALIGN_UNIT (t);
+}
+
+// Return the alignment of a struct field of type BTYPE.
+
+int64_t
+Gcc_backend::type_field_alignment (tree t)
+{
+  if (t == error_mark_node)
+    return 1;
+  return rust_field_alignment (t);
+}
+
+// Return the offset of a field in a struct.
+
+int64_t
+Gcc_backend::type_field_offset (tree struct_tree, size_t index)
+{
+  if (struct_tree == error_mark_node)
+    return 0;
+  gcc_assert (TREE_CODE (struct_tree) == RECORD_TYPE);
+  tree field = TYPE_FIELDS (struct_tree);
+  for (; index > 0; --index)
+    {
+      field = DECL_CHAIN (field);
+      gcc_assert (field != NULL_TREE);
+    }
+  HOST_WIDE_INT offset_wide = int_byte_position (field);
+  int64_t ret = static_cast<int64_t> (offset_wide);
+  gcc_assert (ret == offset_wide);
+  return ret;
+}
+
+// Return the zero value for a type.
+
+tree
+Gcc_backend::zero_expression (tree t)
+{
+  tree ret;
+  if (t == error_mark_node)
+    ret = error_mark_node;
+  else
+    ret = build_zero_cst (t);
+  return ret;
+}
+
+// An expression that references a variable.
+
+tree
+Gcc_backend::var_expression (Bvariable *var, Location location)
+{
+  return var->get_tree (location);
+}
+
+// Return a typed value as a constant integer.
+
+tree
+Gcc_backend::integer_constant_expression (tree t, mpz_t val)
+{
+  if (t == error_mark_node)
+    return error_mark_node;
+
+  tree ret = double_int_to_tree (t, mpz_get_double_int (t, val, true));
+  return ret;
+}
+
+// Return a typed value as a constant floating-point number.
+
+tree
+Gcc_backend::float_constant_expression (tree t, mpfr_t val)
+{
+  tree ret;
+  if (t == error_mark_node)
+    return error_mark_node;
+
+  REAL_VALUE_TYPE r1;
+  real_from_mpfr (&r1, val, t, GMP_RNDN);
+  REAL_VALUE_TYPE r2;
+  real_convert (&r2, TYPE_MODE (t), &r1);
+  ret = build_real (t, r2);
+  return ret;
+}
+
+// Return a typed real and imaginary value as a constant complex number.
+
+tree
+Gcc_backend::complex_constant_expression (tree t, mpc_t val)
+{
+  tree ret;
+  if (t == error_mark_node)
+    return error_mark_node;
+
+  REAL_VALUE_TYPE r1;
+  real_from_mpfr (&r1, mpc_realref (val), TREE_TYPE (t), GMP_RNDN);
+  REAL_VALUE_TYPE r2;
+  real_convert (&r2, TYPE_MODE (TREE_TYPE (t)), &r1);
+
+  REAL_VALUE_TYPE r3;
+  real_from_mpfr (&r3, mpc_imagref (val), TREE_TYPE (t), GMP_RNDN);
+  REAL_VALUE_TYPE r4;
+  real_convert (&r4, TYPE_MODE (TREE_TYPE (t)), &r3);
+
+  ret = build_complex (t, build_real (TREE_TYPE (t), r2),
+		       build_real (TREE_TYPE (t), r4));
+  return ret;
+}
+
+// Make a constant string expression.
+
+tree
+Gcc_backend::string_constant_expression (const std::string &val)
+{
+  tree index_type = build_index_type (size_int (val.length ()));
+  tree const_char_type = build_qualified_type (char_type_node, TYPE_QUAL_CONST);
+  tree string_type = build_array_type (const_char_type, index_type);
+  TYPE_STRING_FLAG (string_type) = 1;
+  tree string_val = build_string (val.length (), val.data ());
+  TREE_TYPE (string_val) = string_type;
+
+  return string_val;
+}
+
+tree
+Gcc_backend::wchar_constant_expression (wchar_t c)
+{
+  return build_int_cst (this->wchar_type (), c);
+}
+
+tree
+Gcc_backend::char_constant_expression (char c)
+{
+  return build_int_cst (this->char_type (), c);
+}
+
+// Make a constant boolean expression.
+
+tree
+Gcc_backend::boolean_constant_expression (bool val)
+{
+  return val ? boolean_true_node : boolean_false_node;
+}
+
+// Return the real part of a complex expression.
+
+tree
+Gcc_backend::real_part_expression (tree complex_tree, Location location)
+{
+  if (complex_tree == error_mark_node)
+    return error_mark_node;
+  gcc_assert (COMPLEX_FLOAT_TYPE_P (TREE_TYPE (complex_tree)));
+  tree ret
+    = fold_build1_loc (location.gcc_location (), REALPART_EXPR,
+		       TREE_TYPE (TREE_TYPE (complex_tree)), complex_tree);
+  return ret;
+}
+
+// Return the imaginary part of a complex expression.
+
+tree
+Gcc_backend::imag_part_expression (tree complex_tree, Location location)
+{
+  if (complex_tree == error_mark_node)
+    return error_mark_node;
+  gcc_assert (COMPLEX_FLOAT_TYPE_P (TREE_TYPE (complex_tree)));
+  tree ret
+    = fold_build1_loc (location.gcc_location (), IMAGPART_EXPR,
+		       TREE_TYPE (TREE_TYPE (complex_tree)), complex_tree);
+  return ret;
+}
+
+// Make a complex expression given its real and imaginary parts.
+
+tree
+Gcc_backend::complex_expression (tree real_tree, tree imag_tree,
+				 Location location)
+{
+  if (real_tree == error_mark_node || imag_tree == error_mark_node)
+    return error_mark_node;
+  gcc_assert (TYPE_MAIN_VARIANT (TREE_TYPE (real_tree))
+	      == TYPE_MAIN_VARIANT (TREE_TYPE (imag_tree)));
+  gcc_assert (SCALAR_FLOAT_TYPE_P (TREE_TYPE (real_tree)));
+  tree ret = fold_build2_loc (location.gcc_location (), COMPLEX_EXPR,
+			      build_complex_type (TREE_TYPE (real_tree)),
+			      real_tree, imag_tree);
+  return ret;
+}
+
+// An expression that converts an expression to a different type.
+
+tree
+Gcc_backend::convert_expression (tree type_tree, tree expr_tree,
+				 Location location)
+{
+  if (type_tree == error_mark_node || expr_tree == error_mark_node
+      || TREE_TYPE (expr_tree) == error_mark_node)
+    return error_mark_node;
+
+  tree ret;
+  if (this->type_size (type_tree) == 0
+      || TREE_TYPE (expr_tree) == void_type_node)
+    {
+      // Do not convert zero-sized types.
+      ret = expr_tree;
+    }
+  else if (TREE_CODE (type_tree) == INTEGER_TYPE)
+    ret = fold (convert_to_integer (type_tree, expr_tree));
+  else if (TREE_CODE (type_tree) == REAL_TYPE)
+    ret = fold (convert_to_real (type_tree, expr_tree));
+  else if (TREE_CODE (type_tree) == COMPLEX_TYPE)
+    ret = fold (convert_to_complex (type_tree, expr_tree));
+  else if (TREE_CODE (type_tree) == POINTER_TYPE
+	   && TREE_CODE (TREE_TYPE (expr_tree)) == INTEGER_TYPE)
+    ret = fold (convert_to_pointer (type_tree, expr_tree));
+  else if (TREE_CODE (type_tree) == RECORD_TYPE
+	   || TREE_CODE (type_tree) == ARRAY_TYPE)
+    ret = fold_build1_loc (location.gcc_location (), VIEW_CONVERT_EXPR,
+			   type_tree, expr_tree);
+  else
+    ret = fold_convert_loc (location.gcc_location (), type_tree, expr_tree);
+
+  return ret;
+}
+
+// Return an expression for the field at INDEX in BSTRUCT.
+
+tree
+Gcc_backend::struct_field_expression (tree struct_tree, size_t index,
+				      Location location)
+{
+  if (struct_tree == error_mark_node
+      || TREE_TYPE (struct_tree) == error_mark_node)
+    return error_mark_node;
+  gcc_assert (TREE_CODE (TREE_TYPE (struct_tree)) == RECORD_TYPE
+	      || TREE_CODE (TREE_TYPE (struct_tree)) == UNION_TYPE);
+  tree field = TYPE_FIELDS (TREE_TYPE (struct_tree));
+  if (field == NULL_TREE)
+    {
+      // This can happen for a type which refers to itself indirectly
+      // and then turns out to be erroneous.
+      return error_mark_node;
+    }
+  for (unsigned int i = index; i > 0; --i)
+    {
+      field = DECL_CHAIN (field);
+      gcc_assert (field != NULL_TREE);
+    }
+  if (TREE_TYPE (field) == error_mark_node)
+    return error_mark_node;
+  tree ret = fold_build3_loc (location.gcc_location (), COMPONENT_REF,
+			      TREE_TYPE (field), struct_tree, field, NULL_TREE);
+  if (TREE_CONSTANT (struct_tree))
+    TREE_CONSTANT (ret) = 1;
+  return ret;
+}
+
+// Return an expression that executes BSTAT before BEXPR.
+
+tree
+Gcc_backend::compound_expression (tree stat, tree expr, Location location)
+{
+  if (stat == error_mark_node || expr == error_mark_node)
+    return error_mark_node;
+  tree ret = fold_build2_loc (location.gcc_location (), COMPOUND_EXPR,
+			      TREE_TYPE (expr), stat, expr);
+  return ret;
+}
+
+// Return an expression that executes THEN_EXPR if CONDITION is true, or
+// ELSE_EXPR otherwise.
+
+tree
+Gcc_backend::conditional_expression (tree, tree type_tree, tree cond_expr,
+				     tree then_expr, tree else_expr,
+				     Location location)
+{
+  if (type_tree == error_mark_node || cond_expr == error_mark_node
+      || then_expr == error_mark_node || else_expr == error_mark_node)
+    return error_mark_node;
+  tree ret = build3_loc (location.gcc_location (), COND_EXPR, type_tree,
+			 cond_expr, then_expr, else_expr);
+  return ret;
+}
+
+/* Helper function that converts rust operators to equivalent GCC tree_code.
+   Note that CompoundAssignmentOperator don't get their corresponding tree_code,
+   because they get compiled away when we lower AST to HIR. */
+static enum tree_code
+operator_to_tree_code (NegationOperator op)
+{
+  switch (op)
+    {
+    case NegationOperator::NEGATE:
+      return NEGATE_EXPR;
+    case NegationOperator::NOT:
+      return TRUTH_NOT_EXPR;
+    default:
+      gcc_unreachable ();
+    }
+}
+
+/* Note that GCC tree code distinguishes floating point division and integer
+   division. These two types of division are represented as the same rust
+   operator, and can only be distinguished via context(i.e. the TREE_TYPE of the
+   operands). */
+static enum tree_code
+operator_to_tree_code (ArithmeticOrLogicalOperator op, bool floating_point)
+{
+  switch (op)
+    {
+    case ArithmeticOrLogicalOperator::ADD:
+      return PLUS_EXPR;
+    case ArithmeticOrLogicalOperator::SUBTRACT:
+      return MINUS_EXPR;
+    case ArithmeticOrLogicalOperator::MULTIPLY:
+      return MULT_EXPR;
+    case ArithmeticOrLogicalOperator::DIVIDE:
+      if (floating_point)
+	return RDIV_EXPR;
+      else
+	return TRUNC_DIV_EXPR;
+    case ArithmeticOrLogicalOperator::MODULUS:
+      return TRUNC_MOD_EXPR;
+    case ArithmeticOrLogicalOperator::BITWISE_AND:
+      return BIT_AND_EXPR;
+    case ArithmeticOrLogicalOperator::BITWISE_OR:
+      return BIT_IOR_EXPR;
+    case ArithmeticOrLogicalOperator::BITWISE_XOR:
+      return BIT_XOR_EXPR;
+    case ArithmeticOrLogicalOperator::LEFT_SHIFT:
+      return LSHIFT_EXPR;
+    case ArithmeticOrLogicalOperator::RIGHT_SHIFT:
+      return RSHIFT_EXPR;
+    default:
+      gcc_unreachable ();
+    }
+}
+
+static enum tree_code
+operator_to_tree_code (ComparisonOperator op)
+{
+  switch (op)
+    {
+    case ComparisonOperator::EQUAL:
+      return EQ_EXPR;
+    case ComparisonOperator::NOT_EQUAL:
+      return NE_EXPR;
+    case ComparisonOperator::GREATER_THAN:
+      return GT_EXPR;
+    case ComparisonOperator::LESS_THAN:
+      return LT_EXPR;
+    case ComparisonOperator::GREATER_OR_EQUAL:
+      return GE_EXPR;
+    case ComparisonOperator::LESS_OR_EQUAL:
+      return LE_EXPR;
+    default:
+      gcc_unreachable ();
+    }
+}
+
+static enum tree_code
+operator_to_tree_code (LazyBooleanOperator op)
+{
+  switch (op)
+    {
+    case LazyBooleanOperator::LOGICAL_OR:
+      return TRUTH_ORIF_EXPR;
+    case LazyBooleanOperator::LOGICAL_AND:
+      return TRUTH_ANDIF_EXPR;
+    default:
+      gcc_unreachable ();
+    }
+}
+
+/* Helper function for deciding if a tree is a floating point node. */
+bool
+is_floating_point (tree t)
+{
+  auto tree_type = TREE_CODE (TREE_TYPE (t));
+  return tree_type == REAL_TYPE || tree_type == COMPLEX_TYPE;
+}
+
+// Return an expression for the negation operation OP EXPR.
+tree
+Gcc_backend::negation_expression (NegationOperator op, tree expr_tree,
+				  Location location)
+{
+  /* Check if the expression is an error, in which case we return an error
+     expression. */
+  if (expr_tree == error_mark_node || TREE_TYPE (expr_tree) == error_mark_node)
+    return error_mark_node;
+
+  /* For negation operators, the resulting type should be the same as its
+     operand. */
+  auto tree_type = TREE_TYPE (expr_tree);
+  auto original_type = tree_type;
+  auto tree_code = operator_to_tree_code (op);
+
+  /* For floating point operations we may need to extend the precision of type.
+     For example, a 64-bit machine may not support operations on float32. */
+  bool floating_point = is_floating_point (expr_tree);
+  auto extended_type = NULL_TREE;
+  if (floating_point)
+    {
+      extended_type = excess_precision_type (tree_type);
+      if (extended_type != NULL_TREE)
+	{
+	  expr_tree = convert (extended_type, expr_tree);
+	  tree_type = extended_type;
+	}
+    }
+
+  /* Construct a new tree and build an expression from it. */
+  auto new_tree = fold_build1_loc (location.gcc_location (), tree_code,
+				   tree_type, expr_tree);
+  if (floating_point && extended_type != NULL_TREE)
+    new_tree = convert (original_type, expr_tree);
+  return new_tree;
+}
+
+// Return an expression for the arithmetic or logical operation LEFT OP RIGHT.
+tree
+Gcc_backend::arithmetic_or_logical_expression (ArithmeticOrLogicalOperator op,
+					       tree left_tree, tree right_tree,
+					       Location location)
+{
+  /* Check if either expression is an error, in which case we return an error
+     expression. */
+  if (left_tree == error_mark_node || right_tree == error_mark_node)
+    return error_mark_node;
+
+  /* We need to determine if we're doing floating point arithmetics of integer
+     arithmetics. */
+  bool floating_point = is_floating_point (left_tree);
+
+  /* For arithmetic or logical operators, the resulting type should be the same
+     as the lhs operand. */
+  auto tree_type = TREE_TYPE (left_tree);
+  auto original_type = tree_type;
+  auto tree_code = operator_to_tree_code (op, floating_point);
+
+  /* For floating point operations we may need to extend the precision of type.
+     For example, a 64-bit machine may not support operations on float32. */
+  auto extended_type = NULL_TREE;
+  if (floating_point)
+    {
+      extended_type = excess_precision_type (tree_type);
+      if (extended_type != NULL_TREE)
+	{
+	  left_tree = convert (extended_type, left_tree);
+	  right_tree = convert (extended_type, right_tree);
+	  tree_type = extended_type;
+	}
+    }
+
+  /* Construct a new tree and build an expression from it. */
+  auto new_tree = fold_build2_loc (location.gcc_location (), tree_code,
+				   tree_type, left_tree, right_tree);
+  TREE_CONSTANT (new_tree)
+    = TREE_CONSTANT (left_tree) && TREE_CONSTANT (right_tree);
+
+  if (floating_point && extended_type != NULL_TREE)
+    new_tree = convert (original_type, new_tree);
+  return new_tree;
+}
+
+// Return an expression for the comparison operation LEFT OP RIGHT.
+tree
+Gcc_backend::comparison_expression (ComparisonOperator op, tree left_tree,
+				    tree right_tree, Location location)
+{
+  /* Check if either expression is an error, in which case we return an error
+     expression. */
+  if (left_tree == error_mark_node || right_tree == error_mark_node)
+    return error_mark_node;
+
+  /* For comparison operators, the resulting type should be boolean. */
+  auto tree_type = boolean_type_node;
+  auto tree_code = operator_to_tree_code (op);
+
+  /* Construct a new tree and build an expression from it. */
+  auto new_tree = fold_build2_loc (location.gcc_location (), tree_code,
+				   tree_type, left_tree, right_tree);
+  return new_tree;
+}
+
+// Return an expression for the lazy boolean operation LEFT OP RIGHT.
+tree
+Gcc_backend::lazy_boolean_expression (LazyBooleanOperator op, tree left_tree,
+				      tree right_tree, Location location)
+{
+  /* Check if either expression is an error, in which case we return an error
+     expression. */
+  if (left_tree == error_mark_node || right_tree == error_mark_node)
+    return error_mark_node;
+
+  /* For lazy boolean operators, the resulting type should be the same as the
+     rhs operand. */
+  auto tree_type = TREE_TYPE (right_tree);
+  auto tree_code = operator_to_tree_code (op);
+
+  /* Construct a new tree and build an expression from it. */
+  auto new_tree = fold_build2_loc (location.gcc_location (), tree_code,
+				   tree_type, left_tree, right_tree);
+  return new_tree;
+}
+
+// Return an expression that constructs BTYPE with VALS.
+
+tree
+Gcc_backend::constructor_expression (tree type_tree, bool is_variant,
+				     const std::vector<tree> &vals,
+				     int union_index, Location location)
+{
+  if (type_tree == error_mark_node)
+    return error_mark_node;
+
+  vec<constructor_elt, va_gc> *init;
+  vec_alloc (init, vals.size ());
+
+  tree sink = NULL_TREE;
+  bool is_constant = true;
+  tree field = TYPE_FIELDS (type_tree);
+
+  if (is_variant)
+    {
+      gcc_assert (union_index != -1);
+      gcc_assert (TREE_CODE (type_tree) == UNION_TYPE);
+
+      for (int i = 0; i < union_index; i++)
+	{
+	  gcc_assert (field != NULL_TREE);
+	  field = DECL_CHAIN (field);
+	}
+
+      tree nested_ctor
+	= constructor_expression (TREE_TYPE (field), false, vals, -1, location);
+
+      constructor_elt empty = {NULL, NULL};
+      constructor_elt *elt = init->quick_push (empty);
+      elt->index = field;
+      elt->value
+	= this->convert_tree (TREE_TYPE (field), nested_ctor, location);
+      if (!TREE_CONSTANT (elt->value))
+	is_constant = false;
+    }
+  else
+    {
+      if (union_index != -1)
+	{
+	  gcc_assert (TREE_CODE (type_tree) == UNION_TYPE);
+	  tree val = vals.front ();
+	  for (int i = 0; i < union_index; i++)
+	    {
+	      gcc_assert (field != NULL_TREE);
+	      field = DECL_CHAIN (field);
+	    }
+	  if (TREE_TYPE (field) == error_mark_node || val == error_mark_node
+	      || TREE_TYPE (val) == error_mark_node)
+	    return error_mark_node;
+
+	  if (int_size_in_bytes (TREE_TYPE (field)) == 0)
+	    {
+	      // GIMPLE cannot represent indices of zero-sized types so
+	      // trying to construct a map with zero-sized keys might lead
+	      // to errors.  Instead, we evaluate each expression that
+	      // would have been added as a map element for its
+	      // side-effects and construct an empty map.
+	      append_to_statement_list (val, &sink);
+	    }
+	  else
+	    {
+	      constructor_elt empty = {NULL, NULL};
+	      constructor_elt *elt = init->quick_push (empty);
+	      elt->index = field;
+	      elt->value
+		= this->convert_tree (TREE_TYPE (field), val, location);
+	      if (!TREE_CONSTANT (elt->value))
+		is_constant = false;
+	    }
+	}
+      else
+	{
+	  gcc_assert (TREE_CODE (type_tree) == RECORD_TYPE);
+	  for (std::vector<tree>::const_iterator p = vals.begin ();
+	       p != vals.end (); ++p, field = DECL_CHAIN (field))
+	    {
+	      gcc_assert (field != NULL_TREE);
+	      tree val = (*p);
+	      if (TREE_TYPE (field) == error_mark_node || val == error_mark_node
+		  || TREE_TYPE (val) == error_mark_node)
+		return error_mark_node;
+
+	      if (int_size_in_bytes (TREE_TYPE (field)) == 0)
+		{
+		  // GIMPLE cannot represent indices of zero-sized types so
+		  // trying to construct a map with zero-sized keys might lead
+		  // to errors.  Instead, we evaluate each expression that
+		  // would have been added as a map element for its
+		  // side-effects and construct an empty map.
+		  append_to_statement_list (val, &sink);
+		  continue;
+		}
+
+	      constructor_elt empty = {NULL, NULL};
+	      constructor_elt *elt = init->quick_push (empty);
+	      elt->index = field;
+	      elt->value
+		= this->convert_tree (TREE_TYPE (field), val, location);
+	      if (!TREE_CONSTANT (elt->value))
+		is_constant = false;
+	    }
+	  gcc_assert (field == NULL_TREE);
+	}
+    }
+
+  tree ret = build_constructor (type_tree, init);
+  if (is_constant)
+    TREE_CONSTANT (ret) = 1;
+  if (sink != NULL_TREE)
+    ret = fold_build2_loc (location.gcc_location (), COMPOUND_EXPR, type_tree,
+			   sink, ret);
+  return ret;
+}
+
+tree
+Gcc_backend::array_constructor_expression (
+  tree type_tree, const std::vector<unsigned long> &indexes,
+  const std::vector<tree> &vals, Location location)
+{
+  if (type_tree == error_mark_node)
+    return error_mark_node;
+
+  gcc_assert (indexes.size () == vals.size ());
+
+  tree element_type = TREE_TYPE (type_tree);
+  HOST_WIDE_INT element_size = int_size_in_bytes (element_type);
+  vec<constructor_elt, va_gc> *init;
+  vec_alloc (init, element_size == 0 ? 0 : vals.size ());
+
+  tree sink = NULL_TREE;
+  bool is_constant = true;
+  for (size_t i = 0; i < vals.size (); ++i)
+    {
+      tree index = size_int (indexes[i]);
+      tree val = vals[i];
+
+      if (index == error_mark_node || val == error_mark_node)
+	return error_mark_node;
+
+      if (element_size == 0)
+	{
+	  // GIMPLE cannot represent arrays of zero-sized types so trying
+	  // to construct an array of zero-sized values might lead to errors.
+	  // Instead, we evaluate each expression that would have been added as
+	  // an array value for its side-effects and construct an empty array.
+	  append_to_statement_list (val, &sink);
+	  continue;
+	}
+
+      if (!TREE_CONSTANT (val))
+	is_constant = false;
+
+      constructor_elt empty = {NULL, NULL};
+      constructor_elt *elt = init->quick_push (empty);
+      elt->index = index;
+      elt->value = val;
+    }
+
+  tree ret = build_constructor (type_tree, init);
+  if (is_constant)
+    TREE_CONSTANT (ret) = 1;
+  if (sink != NULL_TREE)
+    ret = fold_build2_loc (location.gcc_location (), COMPOUND_EXPR, type_tree,
+			   sink, ret);
+  return ret;
+}
+
+// Build insns to create an array, initialize all elements of the array to
+// value, and return it
+tree
+Gcc_backend::array_initializer (tree fndecl, tree block, tree array_type,
+				tree length, tree value, tree *tmp,
+				Location locus)
+{
+  std::vector<tree> stmts;
+
+  // Temporary array we initialize with the desired value.
+  tree t = NULL_TREE;
+  Bvariable *tmp_array = this->temporary_variable (fndecl, block, array_type,
+						   NULL_TREE, true, locus, &t);
+  tree arr = tmp_array->get_tree (locus);
+  stmts.push_back (t);
+
+  // Temporary for the array length used for initialization loop guard.
+  Bvariable *tmp_len = this->temporary_variable (fndecl, block, size_type_node,
+						 length, true, locus, &t);
+  tree len = tmp_len->get_tree (locus);
+  stmts.push_back (t);
+
+  // Temporary variable for pointer used to initialize elements.
+  tree ptr_type = this->pointer_type (TREE_TYPE (array_type));
+  tree ptr_init
+    = build1_loc (locus.gcc_location (), ADDR_EXPR, ptr_type,
+		  this->array_index_expression (arr, integer_zero_node, locus));
+  Bvariable *tmp_ptr = this->temporary_variable (fndecl, block, ptr_type,
+						 ptr_init, false, locus, &t);
+  tree ptr = tmp_ptr->get_tree (locus);
+  stmts.push_back (t);
+
+  // push statement list for the loop
+  std::vector<tree> loop_stmts;
+
+  // Loop exit condition:
+  //   if (length == 0) break;
+  t = this->comparison_expression (ComparisonOperator::EQUAL, len,
+				   this->zero_expression (TREE_TYPE (len)),
+				   locus);
+
+  t = this->exit_expression (t, locus);
+  loop_stmts.push_back (t);
+
+  // Assign value to the current pointer position
+  //   *ptr = value;
+  t = this->assignment_statement (build_fold_indirect_ref (ptr), value, locus);
+  loop_stmts.push_back (t);
+
+  // Move pointer to next element
+  //   ptr++;
+  tree size = TYPE_SIZE_UNIT (TREE_TYPE (ptr_type));
+  t = build2 (POSTINCREMENT_EXPR, ptr_type, ptr, convert (ptr_type, size));
+  loop_stmts.push_back (t);
+
+  // Decrement loop counter.
+  //   length--;
+  t = build2 (POSTDECREMENT_EXPR, TREE_TYPE (len), len,
+	      convert (TREE_TYPE (len), integer_one_node));
+  loop_stmts.push_back (t);
+
+  // pop statments and finish loop
+  tree loop_body = this->statement_list (loop_stmts);
+  stmts.push_back (this->loop_expression (loop_body, locus));
+
+  // Return the temporary in the provided pointer and the statement list which
+  // initializes it.
+  *tmp = tmp_array->get_tree (locus);
+  return this->statement_list (stmts);
+}
+
+// Return an expression representing ARRAY[INDEX]
+
+tree
+Gcc_backend::array_index_expression (tree array_tree, tree index_tree,
+				     Location location)
+{
+  if (array_tree == error_mark_node || TREE_TYPE (array_tree) == error_mark_node
+      || index_tree == error_mark_node)
+    return error_mark_node;
+
+  // A function call that returns a zero sized object will have been
+  // changed to return void.  If we see void here, assume we are
+  // dealing with a zero sized type and just evaluate the operands.
+  tree ret;
+  if (TREE_TYPE (array_tree) != void_type_node)
+    ret = build4_loc (location.gcc_location (), ARRAY_REF,
+		      TREE_TYPE (TREE_TYPE (array_tree)), array_tree,
+		      index_tree, NULL_TREE, NULL_TREE);
+  else
+    ret = fold_build2_loc (location.gcc_location (), COMPOUND_EXPR,
+			   void_type_node, array_tree, index_tree);
+
+  return ret;
+}
+
+// Create an expression for a call to FN_EXPR with FN_ARGS.
+tree
+Gcc_backend::call_expression (tree fn, const std::vector<tree> &fn_args,
+			      tree chain_expr, Location location)
+{
+  if (fn == error_mark_node || TREE_TYPE (fn) == error_mark_node)
+    return error_mark_node;
+
+  gcc_assert (FUNCTION_POINTER_TYPE_P (TREE_TYPE (fn)));
+  tree rettype = TREE_TYPE (TREE_TYPE (TREE_TYPE (fn)));
+
+  size_t nargs = fn_args.size ();
+  tree *args = nargs == 0 ? NULL : new tree[nargs];
+  for (size_t i = 0; i < nargs; ++i)
+    {
+      args[i] = fn_args.at (i);
+    }
+
+  tree fndecl = fn;
+  if (TREE_CODE (fndecl) == ADDR_EXPR)
+    fndecl = TREE_OPERAND (fndecl, 0);
+
+  // This is to support builtin math functions when using 80387 math.
+  tree excess_type = NULL_TREE;
+  if (optimize && TREE_CODE (fndecl) == FUNCTION_DECL
+      && fndecl_built_in_p (fndecl, BUILT_IN_NORMAL)
+      && DECL_IS_UNDECLARED_BUILTIN (fndecl) && nargs > 0
+      && ((SCALAR_FLOAT_TYPE_P (rettype)
+	   && SCALAR_FLOAT_TYPE_P (TREE_TYPE (args[0])))
+	  || (COMPLEX_FLOAT_TYPE_P (rettype)
+	      && COMPLEX_FLOAT_TYPE_P (TREE_TYPE (args[0])))))
+    {
+      excess_type = excess_precision_type (TREE_TYPE (args[0]));
+      if (excess_type != NULL_TREE)
+	{
+	  tree excess_fndecl
+	    = mathfn_built_in (excess_type, DECL_FUNCTION_CODE (fndecl));
+	  if (excess_fndecl == NULL_TREE)
+	    excess_type = NULL_TREE;
+	  else
+	    {
+	      fn = build_fold_addr_expr_loc (location.gcc_location (),
+					     excess_fndecl);
+	      for (size_t i = 0; i < nargs; ++i)
+		{
+		  if (SCALAR_FLOAT_TYPE_P (TREE_TYPE (args[i]))
+		      || COMPLEX_FLOAT_TYPE_P (TREE_TYPE (args[i])))
+		    args[i] = ::convert (excess_type, args[i]);
+		}
+	    }
+	}
+    }
+
+  tree ret
+    = build_call_array_loc (location.gcc_location (),
+			    excess_type != NULL_TREE ? excess_type : rettype,
+			    fn, nargs, args);
+
+  // check for deprecated function usage
+  if (fndecl && TREE_DEPRECATED (fndecl))
+    {
+      // set up the call-site information for `warn_deprecated_use`
+      input_location = location.gcc_location ();
+      warn_deprecated_use (fndecl, NULL_TREE);
+    }
+
+  if (chain_expr)
+    CALL_EXPR_STATIC_CHAIN (ret) = chain_expr;
+
+  if (excess_type != NULL_TREE)
+    {
+      // Calling convert here can undo our excess precision change.
+      // That may or may not be a bug in convert_to_real.
+      ret = build1_loc (location.gcc_location (), NOP_EXPR, rettype, ret);
+    }
+
+  delete[] args;
+  return ret;
+}
+
+// Variable initialization.
+
+tree
+Gcc_backend::init_statement (tree, Bvariable *var, tree init_tree)
+{
+  tree var_tree = var->get_decl ();
+  if (var_tree == error_mark_node || init_tree == error_mark_node)
+    return error_mark_node;
+  gcc_assert (TREE_CODE (var_tree) == VAR_DECL);
+
+  // To avoid problems with GNU ld, we don't make zero-sized
+  // externally visible variables.  That might lead us to doing an
+  // initialization of a zero-sized expression to a non-zero sized
+  // variable, or vice-versa.  Avoid crashes by omitting the
+  // initializer.  Such initializations don't mean anything anyhow.
+  if (int_size_in_bytes (TREE_TYPE (var_tree)) != 0 && init_tree != NULL_TREE
+      && TREE_TYPE (init_tree) != void_type_node
+      && int_size_in_bytes (TREE_TYPE (init_tree)) != 0)
+    {
+      DECL_INITIAL (var_tree) = init_tree;
+      init_tree = NULL_TREE;
+    }
+
+  tree ret = build1_loc (DECL_SOURCE_LOCATION (var_tree), DECL_EXPR,
+			 void_type_node, var_tree);
+  if (init_tree != NULL_TREE)
+    ret = build2_loc (DECL_SOURCE_LOCATION (var_tree), COMPOUND_EXPR,
+		      void_type_node, init_tree, ret);
+
+  return ret;
+}
+
+// Assignment.
+
+tree
+Gcc_backend::assignment_statement (tree lhs, tree rhs, Location location)
+{
+  if (lhs == error_mark_node || rhs == error_mark_node)
+    return error_mark_node;
+
+  // To avoid problems with GNU ld, we don't make zero-sized
+  // externally visible variables.  That might lead us to doing an
+  // assignment of a zero-sized expression to a non-zero sized
+  // expression; avoid crashes here by avoiding assignments of
+  // zero-sized expressions.  Such assignments don't really mean
+  // anything anyhow.
+  if (TREE_TYPE (lhs) == void_type_node
+      || int_size_in_bytes (TREE_TYPE (lhs)) == 0
+      || TREE_TYPE (rhs) == void_type_node
+      || int_size_in_bytes (TREE_TYPE (rhs)) == 0)
+    return this->compound_statement (lhs, rhs);
+
+  rhs = this->convert_tree (TREE_TYPE (lhs), rhs, location);
+
+  return fold_build2_loc (location.gcc_location (), MODIFY_EXPR, void_type_node,
+			  lhs, rhs);
+}
+
+// Return.
+
+tree
+Gcc_backend::return_statement (tree fntree, const std::vector<tree> &vals,
+			       Location location)
+{
+  if (fntree == error_mark_node)
+    return error_mark_node;
+  tree result = DECL_RESULT (fntree);
+  if (result == error_mark_node)
+    return error_mark_node;
+
+  // If the result size is zero bytes, we have set the function type
+  // to have a result type of void, so don't return anything.
+  // See the function_type method.
+  tree res_type = TREE_TYPE (result);
+  if (res_type == void_type_node || int_size_in_bytes (res_type) == 0)
+    {
+      tree stmt_list = NULL_TREE;
+      for (std::vector<tree>::const_iterator p = vals.begin ();
+	   p != vals.end (); p++)
+	{
+	  tree val = (*p);
+	  if (val == error_mark_node)
+	    return error_mark_node;
+	  append_to_statement_list (val, &stmt_list);
+	}
+      tree ret = fold_build1_loc (location.gcc_location (), RETURN_EXPR,
+				  void_type_node, NULL_TREE);
+      append_to_statement_list (ret, &stmt_list);
+      return stmt_list;
+    }
+
+  tree ret;
+  if (vals.empty ())
+    ret = fold_build1_loc (location.gcc_location (), RETURN_EXPR,
+			   void_type_node, NULL_TREE);
+  else if (vals.size () == 1)
+    {
+      tree val = vals.front ();
+      if (val == error_mark_node)
+	return error_mark_node;
+      tree set = fold_build2_loc (location.gcc_location (), MODIFY_EXPR,
+				  void_type_node, result, vals.front ());
+      ret = fold_build1_loc (location.gcc_location (), RETURN_EXPR,
+			     void_type_node, set);
+    }
+  else
+    {
+      // To return multiple values, copy the values into a temporary
+      // variable of the right structure type, and then assign the
+      // temporary variable to the DECL_RESULT in the return
+      // statement.
+      tree stmt_list = NULL_TREE;
+      tree rettype = TREE_TYPE (result);
+
+      if (DECL_STRUCT_FUNCTION (fntree) == NULL)
+	push_struct_function (fntree);
+      else
+	push_cfun (DECL_STRUCT_FUNCTION (fntree));
+      tree rettmp = create_tmp_var (rettype, "RESULT");
+      pop_cfun ();
+
+      tree field = TYPE_FIELDS (rettype);
+      for (std::vector<tree>::const_iterator p = vals.begin ();
+	   p != vals.end (); p++, field = DECL_CHAIN (field))
+	{
+	  gcc_assert (field != NULL_TREE);
+	  tree ref
+	    = fold_build3_loc (location.gcc_location (), COMPONENT_REF,
+			       TREE_TYPE (field), rettmp, field, NULL_TREE);
+	  tree val = (*p);
+	  if (val == error_mark_node)
+	    return error_mark_node;
+	  tree set = fold_build2_loc (location.gcc_location (), MODIFY_EXPR,
+				      void_type_node, ref, (*p));
+	  append_to_statement_list (set, &stmt_list);
+	}
+      gcc_assert (field == NULL_TREE);
+      tree set = fold_build2_loc (location.gcc_location (), MODIFY_EXPR,
+				  void_type_node, result, rettmp);
+      tree ret_expr = fold_build1_loc (location.gcc_location (), RETURN_EXPR,
+				       void_type_node, set);
+      append_to_statement_list (ret_expr, &stmt_list);
+      ret = stmt_list;
+    }
+  return ret;
+}
+
+// Create a statement that attempts to execute BSTAT and calls EXCEPT_STMT if an
+// error occurs.  EXCEPT_STMT may be NULL.  FINALLY_STMT may be NULL and if not
+// NULL, it will always be executed.  This is used for handling defers in Rust
+// functions.  In C++, the resulting code is of this form:
+//   try { BSTAT; } catch { EXCEPT_STMT; } finally { FINALLY_STMT; }
+
+tree
+Gcc_backend::exception_handler_statement (tree try_stmt, tree except_stmt,
+					  tree finally_stmt, Location location)
+{
+  if (try_stmt == error_mark_node || except_stmt == error_mark_node
+      || finally_stmt == error_mark_node)
+    return error_mark_node;
+
+  if (except_stmt != NULL_TREE)
+    try_stmt = build2_loc (location.gcc_location (), TRY_CATCH_EXPR,
+			   void_type_node, try_stmt,
+			   build2_loc (location.gcc_location (), CATCH_EXPR,
+				       void_type_node, NULL, except_stmt));
+  if (finally_stmt != NULL_TREE)
+    try_stmt = build2_loc (location.gcc_location (), TRY_FINALLY_EXPR,
+			   void_type_node, try_stmt, finally_stmt);
+  return try_stmt;
+}
+
+// If.
+
+tree
+Gcc_backend::if_statement (tree, tree cond_tree, tree then_tree, tree else_tree,
+			   Location location)
+{
+  if (cond_tree == error_mark_node || then_tree == error_mark_node
+      || else_tree == error_mark_node)
+    return error_mark_node;
+  tree ret = build3_loc (location.gcc_location (), COND_EXPR, void_type_node,
+			 cond_tree, then_tree, else_tree);
+  return ret;
+}
+
+// Loops
+
+tree
+Gcc_backend::loop_expression (tree body, Location locus)
+{
+  return fold_build1_loc (locus.gcc_location (), LOOP_EXPR, void_type_node,
+			  body);
+}
+
+tree
+Gcc_backend::exit_expression (tree cond_tree, Location locus)
+{
+  return fold_build1_loc (locus.gcc_location (), EXIT_EXPR, void_type_node,
+			  cond_tree);
+}
+
+// Pair of statements.
+
+tree
+Gcc_backend::compound_statement (tree s1, tree s2)
+{
+  tree stmt_list = NULL_TREE;
+  tree t = s1;
+  if (t == error_mark_node)
+    return error_mark_node;
+  append_to_statement_list (t, &stmt_list);
+  t = s2;
+  if (t == error_mark_node)
+    return error_mark_node;
+  append_to_statement_list (t, &stmt_list);
+
+  // If neither statement has any side effects, stmt_list can be NULL
+  // at this point.
+  if (stmt_list == NULL_TREE)
+    stmt_list = integer_zero_node;
+
+  return stmt_list;
+}
+
+// List of statements.
+
+tree
+Gcc_backend::statement_list (const std::vector<tree> &statements)
+{
+  tree stmt_list = NULL_TREE;
+  for (std::vector<tree>::const_iterator p = statements.begin ();
+       p != statements.end (); ++p)
+    {
+      tree t = (*p);
+      if (t == error_mark_node)
+	return error_mark_node;
+      append_to_statement_list (t, &stmt_list);
+    }
+  return stmt_list;
+}
+
+// Make a block.  For some reason gcc uses a dual structure for
+// blocks: BLOCK tree nodes and BIND_EXPR tree nodes.  Since the
+// BIND_EXPR node points to the BLOCK node, we store the BIND_EXPR in
+// the Bblock.
+
+tree
+Gcc_backend::block (tree fndecl, tree enclosing,
+		    const std::vector<Bvariable *> &vars,
+		    Location start_location, Location)
+{
+  tree block_tree = make_node (BLOCK);
+  if (enclosing == NULL)
+    {
+      gcc_assert (fndecl != NULL_TREE);
+
+      // We may have already created a block for local variables when
+      // we take the address of a parameter.
+      if (DECL_INITIAL (fndecl) == NULL_TREE)
+	{
+	  BLOCK_SUPERCONTEXT (block_tree) = fndecl;
+	  DECL_INITIAL (fndecl) = block_tree;
+	}
+      else
+	{
+	  tree superblock_tree = DECL_INITIAL (fndecl);
+	  BLOCK_SUPERCONTEXT (block_tree) = superblock_tree;
+	  tree *pp;
+	  for (pp = &BLOCK_SUBBLOCKS (superblock_tree); *pp != NULL_TREE;
+	       pp = &BLOCK_CHAIN (*pp))
+	    ;
+	  *pp = block_tree;
+	}
+    }
+  else
+    {
+      tree superblock_tree = BIND_EXPR_BLOCK (enclosing);
+      gcc_assert (TREE_CODE (superblock_tree) == BLOCK);
+
+      BLOCK_SUPERCONTEXT (block_tree) = superblock_tree;
+      tree *pp;
+      for (pp = &BLOCK_SUBBLOCKS (superblock_tree); *pp != NULL_TREE;
+	   pp = &BLOCK_CHAIN (*pp))
+	;
+      *pp = block_tree;
+    }
+
+  tree *pp = &BLOCK_VARS (block_tree);
+  for (std::vector<Bvariable *>::const_iterator pv = vars.begin ();
+       pv != vars.end (); ++pv)
+    {
+      *pp = (*pv)->get_decl ();
+      if (*pp != error_mark_node)
+	pp = &DECL_CHAIN (*pp);
+    }
+  *pp = NULL_TREE;
+
+  TREE_USED (block_tree) = 1;
+
+  tree bind_tree
+    = build3_loc (start_location.gcc_location (), BIND_EXPR, void_type_node,
+		  BLOCK_VARS (block_tree), NULL_TREE, block_tree);
+  TREE_SIDE_EFFECTS (bind_tree) = 1;
+  return bind_tree;
+}
+
+// Add statements to a block.
+
+void
+Gcc_backend::block_add_statements (tree bind_tree,
+				   const std::vector<tree> &statements)
+{
+  tree stmt_list = NULL_TREE;
+  for (std::vector<tree>::const_iterator p = statements.begin ();
+       p != statements.end (); ++p)
+    {
+      tree s = (*p);
+      if (s != error_mark_node)
+	append_to_statement_list (s, &stmt_list);
+    }
+
+  gcc_assert (TREE_CODE (bind_tree) == BIND_EXPR);
+  BIND_EXPR_BODY (bind_tree) = stmt_list;
+}
+
+// This is not static because we declare it with GTY(()) in rust-c.h.
+tree rust_non_zero_struct;
+
+// Return a type corresponding to TYPE with non-zero size.
+
+tree
+Gcc_backend::non_zero_size_type (tree type)
+{
+  if (int_size_in_bytes (type) != 0)
+    return type;
+
+  switch (TREE_CODE (type))
+    {
+    case RECORD_TYPE:
+      if (TYPE_FIELDS (type) != NULL_TREE)
+	{
+	  tree ns = make_node (RECORD_TYPE);
+	  tree field_trees = NULL_TREE;
+	  tree *pp = &field_trees;
+	  for (tree field = TYPE_FIELDS (type); field != NULL_TREE;
+	       field = DECL_CHAIN (field))
+	    {
+	      tree ft = TREE_TYPE (field);
+	      if (field == TYPE_FIELDS (type))
+		ft = non_zero_size_type (ft);
+	      tree f = build_decl (DECL_SOURCE_LOCATION (field), FIELD_DECL,
+				   DECL_NAME (field), ft);
+	      DECL_CONTEXT (f) = ns;
+	      *pp = f;
+	      pp = &DECL_CHAIN (f);
+	    }
+	  TYPE_FIELDS (ns) = field_trees;
+	  layout_type (ns);
+	  return ns;
+	}
+
+      if (rust_non_zero_struct == NULL_TREE)
+	{
+	  type = make_node (RECORD_TYPE);
+	  tree field = build_decl (UNKNOWN_LOCATION, FIELD_DECL,
+				   get_identifier ("dummy"), boolean_type_node);
+	  DECL_CONTEXT (field) = type;
+	  TYPE_FIELDS (type) = field;
+	  layout_type (type);
+	  rust_non_zero_struct = type;
+	}
+      return rust_non_zero_struct;
+
+      case ARRAY_TYPE: {
+	tree element_type = non_zero_size_type (TREE_TYPE (type));
+	return build_array_type_nelts (element_type, 1);
+      }
+
+    default:
+      gcc_unreachable ();
+    }
+
+  gcc_unreachable ();
+}
+
+// Convert EXPR_TREE to TYPE_TREE.  Sometimes the same unnamed Rust type
+// can be created multiple times and thus have multiple tree
+// representations.  Make sure this does not confuse the middle-end.
+
+tree
+Gcc_backend::convert_tree (tree type_tree, tree expr_tree, Location location)
+{
+  if (type_tree == TREE_TYPE (expr_tree))
+    return expr_tree;
+
+  if (type_tree == error_mark_node || expr_tree == error_mark_node
+      || TREE_TYPE (expr_tree) == error_mark_node)
+    return error_mark_node;
+
+  if (POINTER_TYPE_P (type_tree) || INTEGRAL_TYPE_P (type_tree)
+      || SCALAR_FLOAT_TYPE_P (type_tree) || COMPLEX_FLOAT_TYPE_P (type_tree))
+    return fold_convert_loc (location.gcc_location (), type_tree, expr_tree);
+  else if (TREE_CODE (type_tree) == RECORD_TYPE
+	   || TREE_CODE (type_tree) == UNION_TYPE
+	   || TREE_CODE (type_tree) == ARRAY_TYPE)
+    {
+      gcc_assert (int_size_in_bytes (type_tree)
+		  == int_size_in_bytes (TREE_TYPE (expr_tree)));
+      if (TYPE_MAIN_VARIANT (type_tree)
+	  == TYPE_MAIN_VARIANT (TREE_TYPE (expr_tree)))
+	return fold_build1_loc (location.gcc_location (), NOP_EXPR, type_tree,
+				expr_tree);
+      return fold_build1_loc (location.gcc_location (), VIEW_CONVERT_EXPR,
+			      type_tree, expr_tree);
+    }
+
+  gcc_unreachable ();
+}
+
+// Make a global variable.
+
+Bvariable *
+Gcc_backend::global_variable (const std::string &var_name,
+			      const std::string &asm_name, tree type_tree,
+			      bool is_external, bool is_hidden,
+			      bool in_unique_section, Location location)
+{
+  if (type_tree == error_mark_node)
+    return this->error_variable ();
+
+  // The GNU linker does not like dynamic variables with zero size.
+  tree orig_type_tree = type_tree;
+  if ((is_external || !is_hidden) && int_size_in_bytes (type_tree) == 0)
+    type_tree = this->non_zero_size_type (type_tree);
+
+  tree decl = build_decl (location.gcc_location (), VAR_DECL,
+			  get_identifier_from_string (var_name), type_tree);
+  if (is_external)
+    DECL_EXTERNAL (decl) = 1;
+  else
+    TREE_STATIC (decl) = 1;
+  if (!is_hidden)
+    {
+      TREE_PUBLIC (decl) = 1;
+      SET_DECL_ASSEMBLER_NAME (decl, get_identifier_from_string (asm_name));
+    }
+  else
+    {
+      SET_DECL_ASSEMBLER_NAME (decl, get_identifier_from_string (asm_name));
+    }
+
+  TREE_USED (decl) = 1;
+
+  if (in_unique_section)
+    resolve_unique_section (decl, 0, 1);
+
+  rust_preserve_from_gc (decl);
+
+  return new Bvariable (decl, orig_type_tree);
+}
+
+// Set the initial value of a global variable.
+
+void
+Gcc_backend::global_variable_set_init (Bvariable *var, tree expr_tree)
+{
+  if (expr_tree == error_mark_node)
+    return;
+  gcc_assert (TREE_CONSTANT (expr_tree));
+  tree var_decl = var->get_decl ();
+  if (var_decl == error_mark_node)
+    return;
+  DECL_INITIAL (var_decl) = expr_tree;
+
+  // If this variable goes in a unique section, it may need to go into
+  // a different one now that DECL_INITIAL is set.
+  if (symtab_node::get (var_decl)
+      && symtab_node::get (var_decl)->implicit_section)
+    {
+      set_decl_section_name (var_decl, (const char *) NULL);
+      resolve_unique_section (var_decl, compute_reloc_for_constant (expr_tree),
+			      1);
+    }
+}
+
+// Make a local variable.
+
+Bvariable *
+Gcc_backend::local_variable (tree function, const std::string &name,
+			     tree type_tree, Bvariable *decl_var,
+			     Location location)
+{
+  if (type_tree == error_mark_node)
+    return this->error_variable ();
+  tree decl = build_decl (location.gcc_location (), VAR_DECL,
+			  get_identifier_from_string (name), type_tree);
+  DECL_CONTEXT (decl) = function;
+
+  if (decl_var != NULL)
+    {
+      DECL_HAS_VALUE_EXPR_P (decl) = 1;
+      SET_DECL_VALUE_EXPR (decl, decl_var->get_decl ());
+    }
+  rust_preserve_from_gc (decl);
+  return new Bvariable (decl);
+}
+
+// Make a function parameter variable.
+
+Bvariable *
+Gcc_backend::parameter_variable (tree function, const std::string &name,
+				 tree type_tree, Location location)
+{
+  if (type_tree == error_mark_node)
+    return this->error_variable ();
+  tree decl = build_decl (location.gcc_location (), PARM_DECL,
+			  get_identifier_from_string (name), type_tree);
+  DECL_CONTEXT (decl) = function;
+  DECL_ARG_TYPE (decl) = type_tree;
+
+  rust_preserve_from_gc (decl);
+  return new Bvariable (decl);
+}
+
+// Make a static chain variable.
+
+Bvariable *
+Gcc_backend::static_chain_variable (tree fndecl, const std::string &name,
+				    tree type_tree, Location location)
+{
+  if (type_tree == error_mark_node)
+    return this->error_variable ();
+  tree decl = build_decl (location.gcc_location (), PARM_DECL,
+			  get_identifier_from_string (name), type_tree);
+  DECL_CONTEXT (decl) = fndecl;
+  DECL_ARG_TYPE (decl) = type_tree;
+  TREE_USED (decl) = 1;
+  DECL_ARTIFICIAL (decl) = 1;
+  DECL_IGNORED_P (decl) = 1;
+  TREE_READONLY (decl) = 1;
+
+  struct function *f = DECL_STRUCT_FUNCTION (fndecl);
+  if (f == NULL)
+    {
+      push_struct_function (fndecl);
+      pop_cfun ();
+      f = DECL_STRUCT_FUNCTION (fndecl);
+    }
+  gcc_assert (f->static_chain_decl == NULL);
+  f->static_chain_decl = decl;
+  DECL_STATIC_CHAIN (fndecl) = 1;
+
+  rust_preserve_from_gc (decl);
+  return new Bvariable (decl);
+}
+
+// Make a temporary variable.
+
+Bvariable *
+Gcc_backend::temporary_variable (tree fndecl, tree bind_tree, tree type_tree,
+				 tree init_tree, bool is_address_taken,
+				 Location location, tree *pstatement)
+{
+  gcc_assert (fndecl != NULL_TREE);
+  if (type_tree == error_mark_node || init_tree == error_mark_node
+      || fndecl == error_mark_node)
+    {
+      *pstatement = error_mark_node;
+      return this->error_variable ();
+    }
+
+  tree var;
+  // We can only use create_tmp_var if the type is not addressable.
+  if (!TREE_ADDRESSABLE (type_tree))
+    {
+      if (DECL_STRUCT_FUNCTION (fndecl) == NULL)
+	push_struct_function (fndecl);
+      else
+	push_cfun (DECL_STRUCT_FUNCTION (fndecl));
+
+      var = create_tmp_var (type_tree, "RUSTTMP");
+      pop_cfun ();
+    }
+  else
+    {
+      gcc_assert (bind_tree != NULL_TREE);
+      var = build_decl (location.gcc_location (), VAR_DECL,
+			create_tmp_var_name ("RUSTTMP"), type_tree);
+      DECL_ARTIFICIAL (var) = 1;
+      DECL_IGNORED_P (var) = 1;
+      TREE_USED (var) = 1;
+      DECL_CONTEXT (var) = fndecl;
+
+      // We have to add this variable to the BLOCK and the BIND_EXPR.
+      gcc_assert (TREE_CODE (bind_tree) == BIND_EXPR);
+      tree block_tree = BIND_EXPR_BLOCK (bind_tree);
+      gcc_assert (TREE_CODE (block_tree) == BLOCK);
+      DECL_CHAIN (var) = BLOCK_VARS (block_tree);
+      BLOCK_VARS (block_tree) = var;
+      BIND_EXPR_VARS (bind_tree) = BLOCK_VARS (block_tree);
+    }
+
+  if (this->type_size (type_tree) != 0 && init_tree != NULL_TREE
+      && TREE_TYPE (init_tree) != void_type_node)
+    DECL_INITIAL (var) = this->convert_tree (type_tree, init_tree, location);
+
+  if (is_address_taken)
+    TREE_ADDRESSABLE (var) = 1;
+
+  *pstatement
+    = build1_loc (location.gcc_location (), DECL_EXPR, void_type_node, var);
+
+  // For a zero sized type, don't initialize VAR with BINIT, but still
+  // evaluate BINIT for its side effects.
+  if (init_tree != NULL_TREE
+      && (this->type_size (type_tree) == 0
+	  || TREE_TYPE (init_tree) == void_type_node))
+    *pstatement = this->compound_statement (init_tree, *pstatement);
+
+  return new Bvariable (var);
+}
+
+// Make a label.
+
+tree
+Gcc_backend::label (tree func_tree, const std::string &name, Location location)
+{
+  tree decl;
+  if (name.empty ())
+    {
+      if (DECL_STRUCT_FUNCTION (func_tree) == NULL)
+	push_struct_function (func_tree);
+      else
+	push_cfun (DECL_STRUCT_FUNCTION (func_tree));
+
+      decl = create_artificial_label (location.gcc_location ());
+
+      pop_cfun ();
+    }
+  else
+    {
+      tree id = get_identifier_from_string (name);
+      decl
+	= build_decl (location.gcc_location (), LABEL_DECL, id, void_type_node);
+      DECL_CONTEXT (decl) = func_tree;
+    }
+  return decl;
+}
+
+// Make a statement which defines a label.
+
+tree
+Gcc_backend::label_definition_statement (tree label)
+{
+  return fold_build1_loc (DECL_SOURCE_LOCATION (label), LABEL_EXPR,
+			  void_type_node, label);
+}
+
+// Make a goto statement.
+
+tree
+Gcc_backend::goto_statement (tree label, Location location)
+{
+  return fold_build1_loc (location.gcc_location (), GOTO_EXPR, void_type_node,
+			  label);
+}
+
+// Get the address of a label.
+
+tree
+Gcc_backend::label_address (tree label, Location location)
+{
+  TREE_USED (label) = 1;
+  TREE_ADDRESSABLE (label) = 1;
+  tree ret
+    = fold_convert_loc (location.gcc_location (), ptr_type_node,
+			build_fold_addr_expr_loc (location.gcc_location (),
+						  label));
+  return ret;
+}
+
+// Declare or define a new function.
+
+tree
+Gcc_backend::function (tree functype, const std::string &name,
+		       const std::string &asm_name, unsigned int flags,
+		       Location location)
+{
+  if (functype != error_mark_node)
+    {
+      gcc_assert (FUNCTION_POINTER_TYPE_P (functype));
+      functype = TREE_TYPE (functype);
+    }
+  tree id = get_identifier_from_string (name);
+  if (functype == error_mark_node || id == error_mark_node)
+    return error_mark_node;
+
+  tree decl
+    = build_decl (location.gcc_location (), FUNCTION_DECL, id, functype);
+  if (!asm_name.empty ())
+    SET_DECL_ASSEMBLER_NAME (decl, get_identifier_from_string (asm_name));
+
+  if ((flags & function_is_declaration) != 0)
+    DECL_EXTERNAL (decl) = 1;
+  else
+    {
+      tree restype = TREE_TYPE (functype);
+      tree resdecl = build_decl (location.gcc_location (), RESULT_DECL,
+				 NULL_TREE, restype);
+      DECL_ARTIFICIAL (resdecl) = 1;
+      DECL_IGNORED_P (resdecl) = 1;
+      DECL_CONTEXT (resdecl) = decl;
+      DECL_RESULT (decl) = resdecl;
+    }
+  if ((flags & function_is_uninlinable) != 0)
+    DECL_UNINLINABLE (decl) = 1;
+  if ((flags & function_does_not_return) != 0)
+    TREE_THIS_VOLATILE (decl) = 1;
+  if ((flags & function_in_unique_section) != 0)
+    resolve_unique_section (decl, 0, 1);
+
+  rust_preserve_from_gc (decl);
+  return decl;
+}
+
+// Create a statement that runs all deferred calls for FUNCTION.  This should
+// be a statement that looks like this in C++:
+//   finish:
+//     try { UNDEFER; } catch { CHECK_DEFER; goto finish; }
+
+tree
+Gcc_backend::function_defer_statement (tree function, tree undefer_tree,
+				       tree defer_tree, Location location)
+{
+  if (undefer_tree == error_mark_node || defer_tree == error_mark_node
+      || function == error_mark_node)
+    return error_mark_node;
+
+  if (DECL_STRUCT_FUNCTION (function) == NULL)
+    push_struct_function (function);
+  else
+    push_cfun (DECL_STRUCT_FUNCTION (function));
+
+  tree stmt_list = NULL;
+  tree label = this->label (function, "", location);
+  tree label_def = this->label_definition_statement (label);
+  append_to_statement_list (label_def, &stmt_list);
+
+  tree jump_stmt = this->goto_statement (label, location);
+  tree catch_body
+    = build2 (COMPOUND_EXPR, void_type_node, defer_tree, jump_stmt);
+  catch_body = build2 (CATCH_EXPR, void_type_node, NULL, catch_body);
+  tree try_catch
+    = build2 (TRY_CATCH_EXPR, void_type_node, undefer_tree, catch_body);
+  append_to_statement_list (try_catch, &stmt_list);
+  pop_cfun ();
+
+  return stmt_list;
+}
+
+// Record PARAM_VARS as the variables to use for the parameters of FUNCTION.
+// This will only be called for a function definition.
+
+bool
+Gcc_backend::function_set_parameters (
+  tree function, const std::vector<Bvariable *> &param_vars)
+{
+  if (function == error_mark_node)
+    return false;
+
+  tree params = NULL_TREE;
+  tree *pp = &params;
+  for (std::vector<Bvariable *>::const_iterator pv = param_vars.begin ();
+       pv != param_vars.end (); ++pv)
+    {
+      *pp = (*pv)->get_decl ();
+      gcc_assert (*pp != error_mark_node);
+      pp = &DECL_CHAIN (*pp);
+    }
+  *pp = NULL_TREE;
+  DECL_ARGUMENTS (function) = params;
+  return true;
+}
+
+// Write the definitions for all TYPE_DECLS, CONSTANT_DECLS,
+// FUNCTION_DECLS, and VARIABLE_DECLS declared globally, as well as
+// emit early debugging information.
+
+void
+Gcc_backend::write_global_definitions (
+  const std::vector<tree> &type_decls, const std::vector<tree> &constant_decls,
+  const std::vector<tree> &function_decls,
+  const std::vector<Bvariable *> &variable_decls)
+{
+  size_t count_definitions = type_decls.size () + constant_decls.size ()
+			     + function_decls.size () + variable_decls.size ();
+
+  tree *defs = new tree[count_definitions];
+
+  // Convert all non-erroneous declarations into Gimple form.
+  size_t i = 0;
+  for (std::vector<Bvariable *>::const_iterator p = variable_decls.begin ();
+       p != variable_decls.end (); ++p)
+    {
+      tree v = (*p)->get_decl ();
+      if (v != error_mark_node)
+	{
+	  defs[i] = v;
+	  rust_preserve_from_gc (defs[i]);
+	  ++i;
+	}
+    }
+
+  for (std::vector<tree>::const_iterator p = type_decls.begin ();
+       p != type_decls.end (); ++p)
+    {
+      tree type_tree = (*p);
+      if (type_tree != error_mark_node && IS_TYPE_OR_DECL_P (type_tree))
+	{
+	  defs[i] = TYPE_NAME (type_tree);
+	  gcc_assert (defs[i] != NULL);
+	  rust_preserve_from_gc (defs[i]);
+	  ++i;
+	}
+    }
+  for (std::vector<tree>::const_iterator p = constant_decls.begin ();
+       p != constant_decls.end (); ++p)
+    {
+      if ((*p) != error_mark_node)
+	{
+	  defs[i] = (*p);
+	  rust_preserve_from_gc (defs[i]);
+	  ++i;
+	}
+    }
+  for (std::vector<tree>::const_iterator p = function_decls.begin ();
+       p != function_decls.end (); ++p)
+    {
+      tree decl = (*p);
+      if (decl != error_mark_node)
+	{
+	  rust_preserve_from_gc (decl);
+	  if (DECL_STRUCT_FUNCTION (decl) == NULL)
+	    allocate_struct_function (decl, false);
+	  dump_function (TDI_original, decl);
+	  cgraph_node::finalize_function (decl, true);
+
+	  defs[i] = decl;
+	  ++i;
+	}
+    }
+
+  // Pass everything back to the middle-end.
+
+  wrapup_global_declarations (defs, i);
+
+  delete[] defs;
+}
+
+void
+Gcc_backend::write_export_data (const char *bytes, unsigned int size)
+{
+  rust_write_export_data (bytes, size);
+}
+
+// Return the backend generator.
+
+Backend *
+rust_get_backend ()
+{
+  return new Gcc_backend ();
+}
-- 
2.25.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [PATCH Rust front-end v2 30/37] gccrs: These are wrappers ported from reusing gccgo
  2022-08-24 11:59 Rust frontend patches v2 herron.philip
                   ` (28 preceding siblings ...)
  2022-08-24 11:59 ` [PATCH Rust front-end v2 29/37] gccrs: HIR to GCC GENERIC lowering herron.philip
@ 2022-08-24 11:59 ` herron.philip
  2022-08-24 11:59 ` [PATCH Rust front-end v2 31/37] gccrs: Add GCC Rust front-end Make-lang.in herron.philip
                   ` (7 subsequent siblings)
  37 siblings, 0 replies; 59+ messages in thread
From: herron.philip @ 2022-08-24 11:59 UTC (permalink / raw)
  To: gcc-patches; +Cc: gcc-rust, Philip Herron

From: Philip Herron <philip.herron@embecosm.com>

The wrappers over linemap and location will eventually disappear here but
served as a useful starting point for us. We have wrappers over the
diagnostics system which we might be able to get rid of as well.
---
 gcc/rust/rust-diagnostics.cc     | 244 +++++++++++++++++++++++++++++++
 gcc/rust/rust-diagnostics.h      | 154 +++++++++++++++++++
 gcc/rust/rust-gcc-diagnostics.cc |  84 +++++++++++
 gcc/rust/rust-linemap.cc         | 229 +++++++++++++++++++++++++++++
 gcc/rust/rust-linemap.h          | 163 +++++++++++++++++++++
 gcc/rust/rust-location.h         | 105 +++++++++++++
 gcc/rust/rust-system.h           |  86 +++++++++++
 7 files changed, 1065 insertions(+)
 create mode 100644 gcc/rust/rust-diagnostics.cc
 create mode 100644 gcc/rust/rust-diagnostics.h
 create mode 100644 gcc/rust/rust-gcc-diagnostics.cc
 create mode 100644 gcc/rust/rust-linemap.cc
 create mode 100644 gcc/rust/rust-linemap.h
 create mode 100644 gcc/rust/rust-location.h
 create mode 100644 gcc/rust/rust-system.h

diff --git a/gcc/rust/rust-diagnostics.cc b/gcc/rust/rust-diagnostics.cc
new file mode 100644
index 00000000000..c2d3e4ee8be
--- /dev/null
+++ b/gcc/rust/rust-diagnostics.cc
@@ -0,0 +1,244 @@
+// rust-diagnostics.cc -- GCC implementation of rust diagnostics interface.
+// Copyright (C) 2016-2022 Free Software Foundation, Inc.
+// Contributed by Than McIntosh, Google.
+
+// 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/>.
+
+#include "rust-system.h"
+#include "rust-diagnostics.h"
+
+static std::string
+mformat_value ()
+{
+  return std::string (xstrerror (errno));
+}
+
+// Rewrite a format string to expand any extensions not
+// supported by sprintf(). See comments in rust-diagnostics.h
+// for list of supported format specifiers.
+
+static std::string
+expand_format (const char *fmt)
+{
+  std::stringstream ss;
+  for (const char *c = fmt; *c; ++c)
+    {
+      if (*c != '%')
+	{
+	  ss << *c;
+	  continue;
+	}
+      c++;
+      switch (*c)
+	{
+	  case '\0': {
+	    // malformed format string
+	    rust_unreachable ();
+	  }
+	  case '%': {
+	    ss << "%";
+	    break;
+	  }
+	  case 'm': {
+	    ss << mformat_value ();
+	    break;
+	  }
+	  case '<': {
+	    ss << rust_open_quote ();
+	    break;
+	  }
+	  case '>': {
+	    ss << rust_close_quote ();
+	    break;
+	  }
+	  case 'q': {
+	    ss << rust_open_quote ();
+	    c++;
+	    if (*c == 'm')
+	      {
+		ss << mformat_value ();
+	      }
+	    else
+	      {
+		ss << "%" << *c;
+	      }
+	    ss << rust_close_quote ();
+	    break;
+	  }
+	  default: {
+	    ss << "%" << *c;
+	  }
+	}
+    }
+  return ss.str ();
+}
+
+// Expand message format specifiers, using a combination of
+// expand_format above to handle extensions (ex: %m, %q) and vasprintf()
+// to handle regular printf-style formatting. A pragma is being used here to
+// suppress this warning:
+//
+//   warning: function ‘std::__cxx11::string expand_message(const char*,
+//   __va_list_tag*)’ might be a candidate for ‘gnu_printf’ format attribute
+//   [-Wsuggest-attribute=format]
+//
+// What appears to be happening here is that the checker is deciding that
+// because of the call to vasprintf() (which has attribute gnu_printf), the
+// calling function must need to have attribute gnu_printf as well, even
+// though there is already an attribute declaration for it.
+
+static std::string
+expand_message (const char *fmt, va_list ap) RUST_ATTRIBUTE_GCC_DIAG (1, 0);
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wsuggest-attribute=format"
+
+static std::string
+expand_message (const char *fmt, va_list ap)
+{
+  char *mbuf = 0;
+  std::string expanded_fmt = expand_format (fmt);
+  int nwr = vasprintf (&mbuf, expanded_fmt.c_str (), ap);
+  if (nwr == -1)
+    {
+      // memory allocation failed
+      rust_be_error_at (Linemap::unknown_location (),
+			"memory allocation failed in vasprintf");
+      rust_assert (0);
+    }
+  std::string rval = std::string (mbuf);
+  free (mbuf);
+  return rval;
+}
+
+#pragma GCC diagnostic pop
+
+static const char *cached_open_quote = NULL;
+static const char *cached_close_quote = NULL;
+
+const char *
+rust_open_quote ()
+{
+  if (cached_open_quote == NULL)
+    rust_be_get_quotechars (&cached_open_quote, &cached_close_quote);
+  return cached_open_quote;
+}
+
+const char *
+rust_close_quote ()
+{
+  if (cached_close_quote == NULL)
+    rust_be_get_quotechars (&cached_open_quote, &cached_close_quote);
+  return cached_close_quote;
+}
+
+void
+rust_internal_error_at (const Location location, const char *fmt, ...)
+{
+  va_list ap;
+
+  va_start (ap, fmt);
+  rust_be_internal_error_at (location, expand_message (fmt, ap));
+  va_end (ap);
+}
+
+void
+rust_error_at (const Location location, const char *fmt, ...)
+{
+  va_list ap;
+
+  va_start (ap, fmt);
+  rust_be_error_at (location, expand_message (fmt, ap));
+  va_end (ap);
+}
+
+void
+rust_warning_at (const Location location, int opt, const char *fmt, ...)
+{
+  va_list ap;
+
+  va_start (ap, fmt);
+  rust_be_warning_at (location, opt, expand_message (fmt, ap));
+  va_end (ap);
+}
+
+void
+rust_fatal_error (const Location location, const char *fmt, ...)
+{
+  va_list ap;
+
+  va_start (ap, fmt);
+  rust_be_fatal_error (location, expand_message (fmt, ap));
+  va_end (ap);
+}
+
+void
+rust_inform (const Location location, const char *fmt, ...)
+{
+  va_list ap;
+
+  va_start (ap, fmt);
+  rust_be_inform (location, expand_message (fmt, ap));
+  va_end (ap);
+}
+
+// Rich Locations
+void
+rust_error_at (const RichLocation &location, const char *fmt, ...)
+{
+  va_list ap;
+
+  va_start (ap, fmt);
+  rust_be_error_at (location, expand_message (fmt, ap));
+  va_end (ap);
+}
+
+void
+rust_debug_loc (const Location location, const char *fmt, ...)
+{
+  if (!rust_be_debug_p ())
+    return;
+
+  va_list ap;
+
+  va_start (ap, fmt);
+  char *mbuf = NULL;
+  int nwr = vasprintf (&mbuf, fmt, ap);
+  va_end (ap);
+  if (nwr == -1)
+    {
+      rust_be_error_at (Linemap::unknown_location (),
+			"memory allocation failed in vasprintf");
+      rust_assert (0);
+    }
+  std::string rval = std::string (mbuf);
+  free (mbuf);
+  rust_be_inform (location, rval);
+}
+
+namespace Rust {
+Error::Error (const Location location, const char *fmt, ...) : locus (location)
+{
+  va_list ap;
+
+  va_start (ap, fmt);
+  message = expand_message (fmt, ap);
+  va_end (ap);
+
+  message.shrink_to_fit ();
+}
+} // namespace Rust
diff --git a/gcc/rust/rust-diagnostics.h b/gcc/rust/rust-diagnostics.h
new file mode 100644
index 00000000000..93bd1b3237b
--- /dev/null
+++ b/gcc/rust/rust-diagnostics.h
@@ -0,0 +1,154 @@
+// 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/>.
+
+// rust-diagnostics.h -- interface to diagnostic reporting   -*- C++ -*-
+
+#ifndef RUST_DIAGNOSTICS_H
+#define RUST_DIAGNOSTICS_H
+
+#include "rust-linemap.h"
+
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)
+#define RUST_ATTRIBUTE_GCC_DIAG(m, n)                                          \
+  __attribute__ ((__format__ (__gcc_tdiag__, m, n)))                           \
+    __attribute__ ((__nonnull__ (m)))
+#else
+#define RUST_ATTRIBUTE_GCC_DIAG(m, n)
+#endif
+
+// These declarations define the interface through which the frontend
+// reports errors and warnings. These functions accept printf-like
+// format specifiers (e.g. %d, %f, %s, etc), with the following additional
+// extensions:
+//
+//  1.  'q' qualifier may be applied to a specifier to add quoting, e.g.
+//      %qd produces a quoted decimal output, %qs a quoted string output.
+//      [This extension is supported only with single-character format
+//      specifiers].
+//
+//  2.  %m specifier outputs value of "strerror(errno)" at time of call.
+//
+//  3.  %< outputs an opening quote, %> a closing quote.
+//
+// All other format specifiers are as defined by 'sprintf'. The final resulting
+// message is then sent to the back end via rust_be_error_at/rust_be_warning_at.
+
+// clang-format off
+// simple location
+extern void
+rust_internal_error_at (const Location, const char *fmt, ...)
+  RUST_ATTRIBUTE_GCC_DIAG (2, 3)
+  RUST_ATTRIBUTE_NORETURN;
+extern void
+rust_error_at (const Location, const char *fmt, ...)
+  RUST_ATTRIBUTE_GCC_DIAG (2, 3);
+extern void
+rust_warning_at (const Location, int opt, const char *fmt, ...)
+  RUST_ATTRIBUTE_GCC_DIAG (3, 4);
+extern void
+rust_fatal_error (const Location, const char *fmt, ...)
+  RUST_ATTRIBUTE_GCC_DIAG (2, 3)
+  RUST_ATTRIBUTE_NORETURN;
+extern void
+rust_inform (const Location, const char *fmt, ...)
+  RUST_ATTRIBUTE_GCC_DIAG (2, 3);
+
+// rich locations
+extern void
+rust_error_at (const RichLocation &, const char *fmt, ...)
+  RUST_ATTRIBUTE_GCC_DIAG (2, 3);
+// clang-format on
+
+// These interfaces provide a way for the front end to ask for
+// the open/close quote characters it should use when formatting
+// diagnostics (warnings, errors).
+extern const char *
+rust_open_quote ();
+extern const char *
+rust_close_quote ();
+
+// These interfaces are used by utilities above to pass warnings and
+// errors (once format specifiers have been expanded) to the back end,
+// and to determine quoting style. Avoid calling these routines directly;
+// instead use the equivalent routines above. The back end is required to
+// implement these routines.
+
+// clang-format off
+extern void
+rust_be_internal_error_at (const Location, const std::string &errmsg)
+  RUST_ATTRIBUTE_NORETURN;
+extern void
+rust_be_error_at (const Location, const std::string &errmsg);
+extern void
+rust_be_error_at (const RichLocation &, const std::string &errmsg);
+extern void
+rust_be_warning_at (const Location, int opt, const std::string &warningmsg);
+extern void
+rust_be_fatal_error (const Location, const std::string &errmsg)
+  RUST_ATTRIBUTE_NORETURN;
+extern void
+rust_be_inform (const Location, const std::string &infomsg);
+extern void
+rust_be_get_quotechars (const char **open_quote, const char **close_quote);
+extern bool
+rust_be_debug_p (void);
+// clang-format on
+
+namespace Rust {
+/* A structure used to represent an error. Useful for enabling
+ * errors to be ignored, e.g. if backtracking. */
+struct Error
+{
+  Location locus;
+  std::string message;
+  // TODO: store more stuff? e.g. node id?
+
+  Error (Location locus, std::string message)
+    : locus (locus), message (std::move (message))
+  {
+    message.shrink_to_fit ();
+  }
+
+  // TODO: the attribute part might be incorrect
+  Error (Location locus, const char *fmt,
+	 ...) /*RUST_ATTRIBUTE_GCC_DIAG (2, 3)*/ RUST_ATTRIBUTE_GCC_DIAG (3, 4);
+
+  // Irreversibly emits the error as an error.
+  void emit_error () const { rust_error_at (locus, "%s", message.c_str ()); }
+
+  // Irreversibly emits the error as a fatal error.
+  void emit_fatal_error () const
+  {
+    rust_fatal_error (locus, "%s", message.c_str ());
+  }
+};
+} // namespace Rust
+
+// rust_debug uses normal printf formatting, not GCC diagnostic formatting.
+#define rust_debug(...) rust_debug_loc (Location (), __VA_ARGS__)
+
+// rust_sorry_at wraps GCC diagnostic "sorry_at" to accept "Location" instead of
+// "location_t"
+#define rust_sorry_at(location, ...)                                           \
+  sorry_at (location.gcc_location (), __VA_ARGS__)
+
+void
+rust_debug_loc (const Location location, const char *fmt,
+		...) ATTRIBUTE_PRINTF_2;
+
+#endif // !defined(RUST_DIAGNOSTICS_H)
diff --git a/gcc/rust/rust-gcc-diagnostics.cc b/gcc/rust/rust-gcc-diagnostics.cc
new file mode 100644
index 00000000000..db07372dfb5
--- /dev/null
+++ b/gcc/rust/rust-gcc-diagnostics.cc
@@ -0,0 +1,84 @@
+// 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/>.
+
+// rust-gcc-diagnostics.cc -- GCC implementation of rust diagnostics interface.
+
+#include "rust-system.h"
+#include "rust-diagnostics.h"
+
+#include "options.h"
+
+void
+rust_be_internal_error_at (const Location location, const std::string &errmsg)
+{
+  std::string loc_str = Linemap::location_to_string (location);
+  if (loc_str.empty ())
+    internal_error ("%s", errmsg.c_str ());
+  else
+    internal_error ("at %s, %s", loc_str.c_str (), errmsg.c_str ());
+}
+
+void
+rust_be_error_at (const Location location, const std::string &errmsg)
+{
+  location_t gcc_loc = location.gcc_location ();
+  error_at (gcc_loc, "%s", errmsg.c_str ());
+}
+
+void
+rust_be_warning_at (const Location location, int opt,
+		    const std::string &warningmsg)
+{
+  location_t gcc_loc = location.gcc_location ();
+  warning_at (gcc_loc, opt, "%s", warningmsg.c_str ());
+}
+
+void
+rust_be_fatal_error (const Location location, const std::string &fatalmsg)
+{
+  location_t gcc_loc = location.gcc_location ();
+  fatal_error (gcc_loc, "%s", fatalmsg.c_str ());
+}
+
+void
+rust_be_inform (const Location location, const std::string &infomsg)
+{
+  location_t gcc_loc = location.gcc_location ();
+  inform (gcc_loc, "%s", infomsg.c_str ());
+}
+
+void
+rust_be_error_at (const RichLocation &location, const std::string &errmsg)
+{
+  /* TODO: 'error_at' would like a non-'const' 'rich_location *'.  */
+  rich_location &gcc_loc = const_cast<rich_location &> (location.get ());
+  error_at (&gcc_loc, "%s", errmsg.c_str ());
+}
+
+void
+rust_be_get_quotechars (const char **open_qu, const char **close_qu)
+{
+  *open_qu = open_quote;
+  *close_qu = close_quote;
+}
+
+bool
+rust_be_debug_p (void)
+{
+  return !!flag_rust_debug;
+}
diff --git a/gcc/rust/rust-linemap.cc b/gcc/rust/rust-linemap.cc
new file mode 100644
index 00000000000..b32a965a4aa
--- /dev/null
+++ b/gcc/rust/rust-linemap.cc
@@ -0,0 +1,229 @@
+// 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/>.
+
+// rust-linemap.cc -- GCC implementation of Linemap.
+
+#include "rust-linemap.h"
+
+// This class implements the Linemap interface defined by the
+// frontend.
+
+class Gcc_linemap : public Linemap
+{
+public:
+  Gcc_linemap () : Linemap (), in_file_ (false) {}
+
+  void start_file (const char *file_name, unsigned int line_begin);
+
+  void start_line (unsigned int line_number, unsigned int line_size);
+
+  Location get_location (unsigned int column);
+
+  void stop ();
+
+  std::string to_string (Location);
+
+  std::string location_file (Location);
+
+  int location_line (Location);
+
+  int location_column (Location);
+
+protected:
+  Location get_predeclared_location ();
+
+  Location get_unknown_location ();
+
+  bool is_predeclared (Location);
+
+  bool is_unknown (Location);
+
+private:
+  // Whether we are currently reading a file.
+  bool in_file_;
+};
+
+Linemap *Linemap::instance_ = NULL;
+
+// Start getting locations from a new file.
+
+void
+Gcc_linemap::start_file (const char *file_name, unsigned line_begin)
+{
+  if (this->in_file_)
+    linemap_add (line_table, LC_LEAVE, 0, NULL, 0);
+  linemap_add (line_table, LC_ENTER, 0, file_name, line_begin);
+  this->in_file_ = true;
+}
+
+// Stringify a location
+
+std::string
+Gcc_linemap::to_string (Location location)
+{
+  const line_map_ordinary *lmo;
+  location_t resolved_location;
+
+  // Screen out unknown and predeclared locations; produce output
+  // only for simple file:line locations.
+  resolved_location
+    = linemap_resolve_location (line_table, location.gcc_location (),
+				LRK_SPELLING_LOCATION, &lmo);
+  if (lmo == NULL || resolved_location < RESERVED_LOCATION_COUNT)
+    return "";
+  const char *path = LINEMAP_FILE (lmo);
+  if (!path)
+    return "";
+
+  // Strip the source file down to the base file, to reduce clutter.
+  std::stringstream ss;
+  ss << lbasename (path) << ":" << SOURCE_LINE (lmo, location.gcc_location ())
+     << ":" << SOURCE_COLUMN (lmo, location.gcc_location ());
+  return ss.str ();
+}
+
+// Return the file name for a given location.
+
+std::string
+Gcc_linemap::location_file (Location loc)
+{
+  return LOCATION_FILE (loc.gcc_location ());
+}
+
+// Return the line number for a given location.
+
+int
+Gcc_linemap::location_line (Location loc)
+{
+  return LOCATION_LINE (loc.gcc_location ());
+}
+
+// Return the column number for a given location.
+int
+Gcc_linemap::location_column (Location loc)
+{
+  return LOCATION_COLUMN (loc.gcc_location ());
+}
+
+// Stop getting locations.
+
+void
+Gcc_linemap::stop ()
+{
+  linemap_add (line_table, LC_LEAVE, 0, NULL, 0);
+  this->in_file_ = false;
+}
+
+// Start a new line.
+
+void
+Gcc_linemap::start_line (unsigned lineno, unsigned linesize)
+{
+  linemap_line_start (line_table, lineno, linesize);
+}
+
+// Get a location.
+
+Location
+Gcc_linemap::get_location (unsigned column)
+{
+  return Location (linemap_position_for_column (line_table, column));
+}
+
+// Get the unknown location.
+
+Location
+Gcc_linemap::get_unknown_location ()
+{
+  return Location (UNKNOWN_LOCATION);
+}
+
+// Get the predeclared location.
+
+Location
+Gcc_linemap::get_predeclared_location ()
+{
+  return Location (BUILTINS_LOCATION);
+}
+
+// Return whether a location is the predeclared location.
+
+bool
+Gcc_linemap::is_predeclared (Location loc)
+{
+  return loc.gcc_location () == BUILTINS_LOCATION;
+}
+
+// Return whether a location is the unknown location.
+
+bool
+Gcc_linemap::is_unknown (Location loc)
+{
+  return loc.gcc_location () == UNKNOWN_LOCATION;
+}
+
+// Return the Linemap to use for the gcc backend.
+
+Linemap *
+rust_get_linemap ()
+{
+  return new Gcc_linemap;
+}
+
+RichLocation::RichLocation (Location root)
+  : gcc_rich_loc (line_table, root.gcc_location ())
+{
+  /*rich_location (line_maps *set, location_t loc,
+		 const range_label *label = NULL);*/
+}
+
+RichLocation::~RichLocation () {}
+
+void
+RichLocation::add_range (Location loc)
+{
+  gcc_rich_loc.add_range (loc.gcc_location ());
+}
+
+void
+RichLocation::add_fixit_insert_before (const std::string &new_parent)
+{
+  gcc_rich_loc.add_fixit_insert_before (new_parent.c_str ());
+}
+
+void
+RichLocation::add_fixit_insert_before (Location where,
+				       const std::string &new_parent)
+{
+  gcc_rich_loc.add_fixit_insert_before (where.gcc_location (),
+					new_parent.c_str ());
+}
+
+void
+RichLocation::add_fixit_insert_after (const std::string &new_parent)
+{
+  gcc_rich_loc.add_fixit_insert_after (new_parent.c_str ());
+}
+
+void
+RichLocation::add_fixit_insert_after (Location where,
+				      const std::string &new_parent)
+{
+  gcc_rich_loc.add_fixit_insert_after (where.gcc_location (),
+				       new_parent.c_str ());
+}
diff --git a/gcc/rust/rust-linemap.h b/gcc/rust/rust-linemap.h
new file mode 100644
index 00000000000..0ba95f87575
--- /dev/null
+++ b/gcc/rust/rust-linemap.h
@@ -0,0 +1,163 @@
+// rust-linemap.h -- interface to location tracking   -*- C++ -*-
+
+// Copyright 2011 The Go Authors. All rights reserved.
+// Copyright (C) 2020-2022 Free Software Foundation, Inc.
+
+// Use of this source code is governed by a BSD-style
+// license that can be found in the '../go/gofrontend/LICENSE' file.
+
+#ifndef RUST_LINEMAP_H
+#define RUST_LINEMAP_H
+
+#include "rust-system.h"
+
+// The backend must define a type named Location which holds
+// information about a location in a source file.  The only thing the
+// frontend does with instances of Location is pass them back to the
+// backend interface.  The Location type must be assignable, and it
+// must be comparable: i.e., it must support operator= and operator<.
+// The type is normally passed by value rather than by reference, and
+// it should support that efficiently.  The type should be defined in
+// "rust-location.h".
+#include "rust-location.h"
+
+// The Linemap class is a pure abstract interface, plus some static
+// convenience functions.  The backend must implement the interface.
+
+/* TODO: probably better to replace linemap implementation as pure abstract
+ * interface with some sort of compile-time switch (macros or maybe templates if
+ * doable without too much extra annoyance) as to the definition of the methods
+ * or whatever. This is to improve performance, as virtual function calls would
+ * otherwise have to be made in tight loops like in the lexer. */
+
+class Linemap
+{
+public:
+  Linemap ()
+  {
+    // Only one instance of Linemap is allowed to exist.
+    rust_assert (Linemap::instance_ == NULL);
+    Linemap::instance_ = this;
+  }
+
+  virtual ~Linemap () { Linemap::instance_ = NULL; }
+
+  // Subsequent Location values will come from the file named
+  // FILE_NAME, starting at LINE_BEGIN.  Normally LINE_BEGIN will be
+  // 0, but it will be non-zero if the Rust source has a //line comment.
+  virtual void start_file (const char *file_name, unsigned int line_begin) = 0;
+
+  // Subsequent Location values will come from the line LINE_NUMBER,
+  // in the current file.  LINE_SIZE is the size of the line in bytes.
+  // This will normally be called for every line in a source file.
+  virtual void start_line (unsigned int line_number, unsigned int line_size)
+    = 0;
+
+  // Get a Location representing column position COLUMN on the current
+  // line in the current file.
+  virtual Location get_location (unsigned int column) = 0;
+
+  // Stop generating Location values.  This will be called after all
+  // input files have been read, in case any cleanup is required.
+  virtual void stop () = 0;
+
+  // Produce a human-readable description of a Location, e.g.
+  // "foo.rust:10". Returns an empty string for predeclared, builtin or
+  // unknown locations.
+  virtual std::string to_string (Location) = 0;
+
+  // Return the file name for a given location.
+  virtual std::string location_file (Location) = 0;
+
+  // Return the line number for a given location.
+  virtual int location_line (Location) = 0;
+
+  // Return the column number for a given location.
+  virtual int location_column (Location) = 0;
+
+protected:
+  // Return a special Location used for predeclared identifiers.  This
+  // Location should be different from that for any actual source
+  // file.  This location will be used for various different types,
+  // functions, and objects created by the frontend.
+  virtual Location get_predeclared_location () = 0;
+
+  // Return a special Location which indicates that no actual location
+  // is known.  This is used for undefined objects and for errors.
+  virtual Location get_unknown_location () = 0;
+
+  // Return whether the argument is the Location returned by
+  // get_predeclared_location.
+  virtual bool is_predeclared (Location) = 0;
+
+  // Return whether the argument is the Location returned by
+  // get_unknown_location.
+  virtual bool is_unknown (Location) = 0;
+
+  // The single existing instance of Linemap.
+  static Linemap *instance_;
+
+public:
+  // Following are convenience static functions, which allow us to
+  // access some virtual functions without explicitly passing around
+  // an instance of Linemap.
+
+  // Return the special Location used for predeclared identifiers.
+  static Location predeclared_location ()
+  {
+    rust_assert (Linemap::instance_ != NULL);
+    return Linemap::instance_->get_predeclared_location ();
+  }
+
+  // Return the special Location used when no location is known.
+  static Location unknown_location ()
+  {
+    rust_assert (Linemap::instance_ != NULL);
+    return Linemap::instance_->get_unknown_location ();
+  }
+
+  // Return whether the argument is the special location used for
+  // predeclared identifiers.
+  static bool is_predeclared_location (Location loc)
+  {
+    rust_assert (Linemap::instance_ != NULL);
+    return Linemap::instance_->is_predeclared (loc);
+  }
+
+  // Return whether the argument is the special location used when no
+  // location is known.
+  static bool is_unknown_location (Location loc)
+  {
+    rust_assert (Linemap::instance_ != NULL);
+    return Linemap::instance_->is_unknown (loc);
+  }
+
+  // Produce a human-readable description of a Location.
+  static std::string location_to_string (Location loc)
+  {
+    rust_assert (Linemap::instance_ != NULL);
+    return Linemap::instance_->to_string (loc);
+  }
+
+  // Return the file name of a location.
+  static std::string location_to_file (Location loc)
+  {
+    rust_assert (Linemap::instance_ != NULL);
+    return Linemap::instance_->location_file (loc);
+  }
+
+  // Return line number of a location.
+  static int location_to_line (Location loc)
+  {
+    rust_assert (Linemap::instance_ != NULL);
+    return Linemap::instance_->location_line (loc);
+  }
+
+  static int location_to_column (Location loc)
+  {
+    rust_assert (Linemap::instance_ != NULL);
+    return Linemap::instance_->location_column (loc);
+  }
+};
+
+#endif // !defined(RUST_LINEMAP_H)
diff --git a/gcc/rust/rust-location.h b/gcc/rust/rust-location.h
new file mode 100644
index 00000000000..1bb875fe6a4
--- /dev/null
+++ b/gcc/rust/rust-location.h
@@ -0,0 +1,105 @@
+// 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/>.
+
+// rust-location.h -- GCC specific Location declaration.   -*- C++ -*-
+
+#ifndef RUST_LOCATION_H
+#define RUST_LOCATION_H
+
+#include "rust-system.h"
+
+// A location in an input source file.
+
+class Location
+{
+public:
+  Location () : gcc_loc_ (UNKNOWN_LOCATION) {}
+
+  explicit Location (location_t loc) : gcc_loc_ (loc) {}
+
+  location_t gcc_location () const { return gcc_loc_; }
+
+  Location operator+= (location_t rhs)
+  {
+    gcc_loc_ += rhs;
+    return *this;
+  }
+
+  Location operator-= (location_t rhs)
+  {
+    gcc_loc_ -= rhs;
+    return *this;
+  }
+
+  bool operator== (location_t rhs) { return rhs == gcc_loc_; }
+
+private:
+  location_t gcc_loc_;
+};
+
+// The Rust frontend requires the ability to compare Locations.
+
+inline bool
+operator< (Location loca, Location locb)
+{
+  return loca.gcc_location () < locb.gcc_location ();
+}
+
+inline bool
+operator== (Location loca, Location locb)
+{
+  return loca.gcc_location () == locb.gcc_location ();
+}
+
+inline Location
+operator+ (Location lhs, location_t rhs)
+{
+  lhs += rhs;
+  return lhs;
+}
+
+inline Location
+operator- (Location lhs, location_t rhs)
+{
+  lhs -= rhs;
+  return lhs;
+}
+
+class RichLocation
+{
+public:
+  RichLocation (Location root);
+  ~RichLocation ();
+
+  void add_range (Location loc);
+
+  void add_fixit_insert_before (const std::string &new_parent);
+
+  void add_fixit_insert_before (Location where, const std::string &new_parent);
+
+  void add_fixit_insert_after (const std::string &new_parent);
+
+  void add_fixit_insert_after (Location where, const std::string &new_parent);
+
+  const rich_location &get () const { return gcc_rich_loc; }
+
+private:
+  rich_location gcc_rich_loc;
+};
+
+#endif // !defined(RUST_LOCATION_H)
diff --git a/gcc/rust/rust-system.h b/gcc/rust/rust-system.h
new file mode 100644
index 00000000000..3a600237966
--- /dev/null
+++ b/gcc/rust/rust-system.h
@@ -0,0 +1,86 @@
+// rust-system.h -- Rust frontend inclusion of gcc header files   -*- C++ -*-
+// Copyright (C) 2009-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_SYSTEM_H
+#define RUST_SYSTEM_H
+
+#define INCLUDE_ALGORITHM
+#include "config.h"
+
+/* Define this so that inttypes.h defines the PRI?64 macros even
+   when compiling with a C++ compiler.  Define it here so in the
+   event inttypes.h gets pulled in by another header it is already
+   defined.  */
+#define __STDC_FORMAT_MACROS
+
+// These must be included before the #poison declarations in system.h.
+
+#include <string>
+#include <list>
+#include <map>
+#include <set>
+#include <vector>
+#include <sstream>
+#include <string>
+#include <deque>
+#include <functional>
+#include <memory>
+#include <utility>
+#include <fstream>
+
+// Rust frontend requires C++11 minimum, so will have unordered_map and set
+#include <unordered_map>
+#include <unordered_set>
+
+/* We don't really need iostream, but some versions of gmp.h include
+ * it when compiled with C++, which means that we need to include it
+ * before the macro magic of safe-ctype.h, which is included by
+ * system.h. */
+#include <iostream>
+
+#include "system.h"
+#include "ansidecl.h"
+#include "coretypes.h"
+
+#include "diagnostic-core.h" /* For error_at and friends.  */
+#include "intl.h"	     /* For _().  */
+
+#define RUST_ATTRIBUTE_NORETURN ATTRIBUTE_NORETURN
+
+// File separator to use based on whether or not the OS we're working with is
+// DOS-based
+#if defined(HAVE_DOS_BASED_FILE_SYSTEM)
+constexpr static const char *file_separator = "\\";
+#else
+constexpr static const char *file_separator = "/";
+#endif /* HAVE_DOS_BASED_FILE_SYSTEM */
+
+// When using gcc, rust_assert is just gcc_assert.
+#define rust_assert(EXPR) gcc_assert (EXPR)
+
+// When using gcc, rust_unreachable is just gcc_unreachable.
+#define rust_unreachable() gcc_unreachable ()
+
+extern void
+rust_preserve_from_gc (tree t);
+
+extern const char *
+rust_localize_identifier (const char *ident);
+
+#endif // !defined(RUST_SYSTEM_H)
-- 
2.25.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [PATCH Rust front-end v2 31/37] gccrs: Add GCC Rust front-end Make-lang.in
  2022-08-24 11:59 Rust frontend patches v2 herron.philip
                   ` (29 preceding siblings ...)
  2022-08-24 11:59 ` [PATCH Rust front-end v2 30/37] gccrs: These are wrappers ported from reusing gccgo herron.philip
@ 2022-08-24 11:59 ` herron.philip
  2022-09-14 13:34   ` Richard Biener
  2022-08-24 11:59 ` [PATCH Rust front-end v2 32/37] gccrs: Add config-lang.in herron.philip
                   ` (6 subsequent siblings)
  37 siblings, 1 reply; 59+ messages in thread
From: herron.philip @ 2022-08-24 11:59 UTC (permalink / raw)
  To: gcc-patches; +Cc: gcc-rust, Philip Herron

From: Philip Herron <philip.herron@embecosm.com>

This is the Makefile for our front-end.
---
 gcc/rust/Make-lang.in | 400 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 400 insertions(+)
 create mode 100644 gcc/rust/Make-lang.in

diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in
new file mode 100644
index 00000000000..f687cc2f667
--- /dev/null
+++ b/gcc/rust/Make-lang.in
@@ -0,0 +1,400 @@
+# Make-lang.in -- Top level -*- makefile -*- fragment for GCC Rust frontend.
+
+# Copyright (C) 2009-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/>.
+
+# This file provides the language dependent support in the main Makefile.
+
+#RUST_EXES = rust
+
+# Use strict warnings for this front end.
+rust-warn = $(STRICT_WARN)
+
+# Installation name. Useful for cross compilers and used during install.
+GCCRS_INSTALL_NAME := $(shell echo gccrs|sed '$(program_transform_name)')
+GCCRS_TARGET_INSTALL_NAME := $(target_noncanonical)-$(shell echo gccrs|sed '$(program_transform_name)')
+
+# Define the names for selecting rust in LANGUAGES.
+rust: rust1$(exeext)
+
+# Tell GNU make to ignore files by these names if they exist.
+.PHONY: rust
+
+# removed GRS_CFLAGS from here
+
+CFLAGS-rust/rustspec.o += $(DRIVER_DEFINES)
+
+# Create the compiler driver gccrs.
+# A compiler driver is the program that interprets command argument and can be called from the command
+# line - e.g. gcc or g++, and not cc1, which is the actual compiler
+
+# Create driver objects
+GCCRS_D_OBJS = \
+   $(GCC_OBJS) \
+   rust/rustspec.o \
+   $(END)
+
+gccrs$(exeext): $(GCCRS_D_OBJS) $(EXTRA_GCC_OBJS) libcommon-target.a $(LIBDEPS)
+	+$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) -o $@ \
+	  $(GCCRS_D_OBJS) $(EXTRA_GCC_OBJS) libcommon-target.a \
+	  $(EXTRA_GCC_LIBS) $(LIBS)
+
+# List of host object files used by the rust language - files for translation from the parse tree
+# to GENERIC
+# The compiler proper, not driver
+GRS_OBJS = \
+    rust/rust-lang.o \
+    rust/rust-object-export.o \
+    rust/rust-linemap.o \
+    rust/rust-gcc-diagnostics.o \
+    rust/rust-diagnostics.o \
+    rust/rust-gcc.o \
+    rust/rust-token.o \
+    rust/rust-lex.o \
+    rust/rust-cfg-parser.o \
+    rust/rust-parse.o \
+    rust/rust-ast-full-test.o \
+    rust/rust-ast-dump.o \
+    rust/rust-hir-dump.o \
+    rust/rust-session-manager.o \
+    rust/rust-compile.o \
+    rust/rust-mangle.o \
+    rust/rust-compile-resolve-path.o \
+    rust/rust-macro-expand.o \
+    rust/rust-attribute-visitor.o \
+    rust/rust-macro-invoc-lexer.o \
+    rust/rust-macro-substitute-ctx.o \
+    rust/rust-macro-builtins.o \
+    rust/rust-hir-full-test.o \
+    rust/rust-hir-map.o \
+    rust/rust-attributes.o \
+    rust/rust-abi.o \
+    rust/rust-ast-lower.o \
+    rust/rust-ast-lower-base.o \
+    rust/rust-ast-lower-pattern.o \
+    rust/rust-ast-lower-item.o \
+    rust/rust-name-resolver.o \
+    rust/rust-ast-resolve.o \
+    rust/rust-ast-resolve-base.o \
+    rust/rust-ast-resolve-item.o \
+    rust/rust-ast-resolve-pattern.o \
+    rust/rust-ast-resolve-expr.o \
+    rust/rust-ast-resolve-type.o \
+    rust/rust-ast-resolve-path.o \
+    rust/rust-ast-resolve-stmt.o \
+    rust/rust-ast-resolve-struct-expr-field.o \
+    rust/rust-hir-type-check.o \
+    rust/rust-privacy-check.o \
+    rust/rust-privacy-ctx.o \
+    rust/rust-reachability.o \
+    rust/rust-visibility-resolver.o \
+    rust/rust-pub-restricted-visitor.o \
+    rust/rust-privacy-reporter.o \
+    rust/rust-tyty.o \
+    rust/rust-tyty-call.o \
+    rust/rust-tyctx.o \
+    rust/rust-tyty-bounds.o \
+    rust/rust-hir-type-check-util.o \
+    rust/rust-hir-trait-resolve.o \
+    rust/rust-hir-type-check-toplevel.o \
+    rust/rust-hir-type-check-item.o \
+    rust/rust-hir-type-check-type.o \
+    rust/rust-hir-type-check-struct.o \
+    rust/rust-hir-type-check-pattern.o \
+    rust/rust-hir-type-check-expr.o \
+    rust/rust-hir-type-check-stmt.o \
+    rust/rust-hir-type-check-enumitem.o \
+    rust/rust-hir-type-check-implitem.o \
+    rust/rust-hir-dot-operator.o \
+    rust/rust-coercion.o \
+    rust/rust-casts.o \
+    rust/rust-hir-type-check-base.o \
+    rust/rust-autoderef.o \
+    rust/rust-substitution-mapper.o \
+    rust/rust-const-checker.o \
+    rust/rust-lint-marklive.o \
+    rust/rust-lint-unused-var.o \
+    rust/rust-hir-type-check-path.o \
+    rust/rust-unsafe-checker.o \
+    rust/rust-compile-intrinsic.o \
+    rust/rust-compile-pattern.o \
+    rust/rust-compile-fnparam.o \
+    rust/rust-base62.o \
+    rust/rust-optional-test.o \
+    rust/rust-compile-item.o \
+    rust/rust-compile-implitem.o \
+    rust/rust-compile-stmt.o \
+    rust/rust-compile-expr.o \
+    rust/rust-compile-type.o \
+    rust/rust-compile-block.o \
+    rust/rust-compile-struct-field-expr.o \
+    rust/rust-constexpr.o \
+    rust/rust-compile-base.o \
+    rust/rust-tree.o \
+    rust/rust-compile-context.o \
+    rust/rust-export-metadata.o \
+    rust/rust-imports.o \
+    rust/rust-import-archive.o \
+    rust/rust-extern-crate.o \
+    $(END)
+# removed object files from here
+
+# All language-specific object files for Rust.
+RUST_ALL_OBJS = $(GRS_OBJS) $(RUST_TARGET_OBJS)
+
+rust_OBJS = $(RUST_ALL_OBJS) rust/rustspec.o
+
+# The compiler itself is called rust1 (formerly grs1)
+rust1$(exeext): $(RUST_ALL_OBJS) attribs.o $(BACKEND) $(LIBDEPS)
+	+$(LLINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) -o $@ \
+	      $(RUST_ALL_OBJS) attribs.o $(BACKEND) $(LIBS) $(BACKENDLIBS)
+
+# Build hooks.
+
+lang_checks += check-rust
+lang_checks_parallelized += check-rust
+check_rust_parallelize = 10
+
+# Copies its dependencies into the source directory. This generally should be used for generated files
+# such as Bison output files which are not version-controlled, but should be included in any release
+# tarballs. This target will be executed during a bootstrap if ‘--enable-generated-files-in-srcdir’
+# was specified as a configure option.
+rust.srcextra:
+
+rust.all.cross: gccrs$(exeext)
+
+# idk what this does but someone used it
+rust.start.encap: gccrs$(exeext)
+rust.rest.encap:
+
+# Build generated man pages for the front end from Texinfo manuals (see Man Page Generation), in the
+# build directory. This target is only called if the necessary tools are available, but should ignore
+# errors so as not to stop the build if errors occur; man pages are optional and the tools involved
+# may be installed in a broken way.
+rust.man:
+
+# Copies its dependencies into the source directory. These targets will be executed during a bootstrap
+# if ‘--enable-generated-files-in-srcdir’ was specified as a configure option.
+rust.srcman:
+
+# Clean hooks.
+
+rust.mostlyclean:
+#	cd $(srcdir)/rust; rm -f *.o y.tab.h y.tab.c lex.yy.c
+
+rust.clean: rust.mostlyclean
+
+# Builds an etags TAGS file in the language subdirectory in the source tree.
+# TODO: add more directories if I add more
+rust.tags: force
+	cd $(srcdir)/rust; \
+	etags -o TAGS.sub *.y *.l *.cc *.h ast/*.h ast/*.cc lex/*.h lex/*.cc parse/*.h parse/*.cc; \
+	etags --include TAGS.sub --include ../TAGS.sub
+
+# Build documentation hooks.
+
+# Build info documentation for the front end, in the build directory. This target is only called by
+# ‘make bootstrap’ if a suitable version of makeinfo is available, so does not need to check for this,
+# and should fail if an error occurs.
+rust.info:
+
+rust.srcinfo:
+
+# Build DVI documentation for the front end, in the build directory. This should be done using
+# $(TEXI2DVI), with appropriate -I arguments pointing to directories of included files.
+rust.dvi:
+
+# Build PDF documentation for the front end, in the build directory. This should be done using
+# $(TEXI2PDF), with appropriate -I arguments pointing to directories of included files.
+rust.pdf:
+
+doc/rust.info:
+doc/rust.dvi:
+doc/rust.pdf:
+
+# Build HTML documentation for the front end, in the build directory.
+rust.html:
+
+# Install hooks.
+
+# Install everything that is part of the front end, apart from the compiler executables listed in
+# compilers in config-lang.in.
+rust.install-common: installdirs
+#	-rm -f $(DESTDIR)$(bindir)/$(GCCRS_INSTALL_NAME)$(exeext)
+#	-rm -f $(DESTDIR)$(bindir)/$(GCCRS_TARGET_INSTALL_NAME)$(exeext)
+#	$(INSTALL_PROGRAM) gccrs$(exeext) $(DESTDIR)$(bindir)/$(GCCRS_INSTALL_NAME)$(exeext)
+#	if test -f $(DESTDIR)$(bindir)$(GCCRS_TARGET_INSTALL_NAME)$(exeext); then \
+#	  :; \
+#	else \
+#	  cd $(DESTDIR)$(bindir) && \
+#	   $(LN) $(GCCRS_INSTALL_NAME)$(exeext) $(GCCRS_TARGET_INSTALL_NAME)$(exeext); \
+#	fi
+	-rm -f $(DESTDIR)$(bindir)/$(GCCRS_INSTALL_NAME)$(exeext)
+	$(INSTALL_PROGRAM) gccrs$(exeext) $(DESTDIR)$(bindir)/$(GCCRS_INSTALL_NAME)$(exeext)
+	rm -f $(DESTDIR)$(bindir)/$(GCCRS_TARGET_INSTALL_NAME)$(exeext); \
+	( cd $(DESTDIR)$(bindir) && \
+      $(LN) $(GCCRS_INSTALL_NAME)$(exeext) $(GCCRS_TARGET_INSTALL_NAME)$(exeext) ); \
+
+# Install headers needed for plugins.
+rust.install-plugin:
+
+# Uninstall files installed by installing the compiler. This is currently documented not to be
+# supported, so the hook need not do anything.
+rust.uninstall:
+#	-rm -rf $(DESTDIR)/$(bindir)/$(GCCRS_INSTALL_NAME)$(exeext)
+	-rm -f gccrs$(exeext) grs1$(exeext)
+	-rm -f $(RUST_ALL_OBJS)
+# ^those two are a maybe
+
+# Enable selftests for the rust frontend
+selftest-rust: s-selftest-rust
+
+RUST_SELFTEST_FLAGS = -xrs $(SELFTEST_FLAGS)
+RUST_SELFTEST_DEPS = rust1$(exeext) $(SELFTEST_DEPS)
+
+# Run the rust selftests
+s-selftest-rust: $(RUST_SELFTEST_DEPS)
+	$(GCC_FOR_TARGET) $(RUST_SELFTEST_FLAGS)
+	$(STAMP) $@
+
+# Install info documentation for the front end, if it is present in the source directory. This target
+# should have dependencies on info files that should be installed.
+rust.install-info:
+
+rust.install-pdf:
+
+# Install man pages for the front end. This target should ignore errors.
+rust.install-man:
+
+# Stage hooks:
+# The toplevel makefile has already created stage?/rust at this point.
+# Used for handling bootstrap
+
+rust.stage1: stage1-start
+	-mv rust/*$(objext) stage1/rust
+rust.stage2: stage2-start
+	-mv rust/*$(objext) stage2/rust
+rust.stage3: stage3-start
+	-mv rust/*$(objext) stage3/rust
+rust.stage4: stage4-start
+	-mv rust/*$(objext) stage4/rust
+rust.stageprofile: stageprofile-start
+	-mv rust/*$(objext) stageprofile/rust
+rust.stagefeedback: stagefeedback-start
+	-mv rust/*$(objext) stagefeedback/rust
+
+CFLAGS-rust/rust-lang.o += -DDEFAULT_TARGET_VERSION=\"$(version)\" \
+	-DDEFAULT_TARGET_MACHINE=\"$(target_noncanonical)\"
+
+# cross-folder includes - add new folders later
+RUST_INCLUDES = -I $(srcdir)/rust \
+	-I $(srcdir)/rust/lex \
+	-I $(srcdir)/rust/parse \
+	-I $(srcdir)/rust/ast \
+	-I $(srcdir)/rust/analysis \
+	-I $(srcdir)/rust/backend \
+	-I $(srcdir)/rust/expand \
+	-I $(srcdir)/rust/hir/tree \
+	-I $(srcdir)/rust/hir \
+	-I $(srcdir)/rust/resolve \
+	-I $(srcdir)/rust/util \
+	-I $(srcdir)/rust/typecheck \
+	-I $(srcdir)/rust/checks/lints \
+	-I $(srcdir)/rust/checks/errors \
+	-I $(srcdir)/rust/checks/errors/privacy \
+	-I $(srcdir)/rust/util \
+        -I $(srcdir)/rust/metadata
+
+# add files that require cross-folder includes - currently rust-lang.o, rust-lex.o
+CFLAGS-rust/rust-lang.o += $(RUST_INCLUDES)
+CFLAGS-rust/rust-lex.o += $(RUST_INCLUDES)
+CFLAGS-rust/rust-parse.o += $(RUST_INCLUDES)
+CFLAGS-rust/rust-session-manager.o += $(RUST_INCLUDES)
+
+# TODO: possibly find a way to ensure C++11 compilation level here?
+RUST_CXXFLAGS = -std=c++11 -Wno-unused-parameter -Werror=overloaded-virtual
+
+# build all rust/lex files in rust folder, add cross-folder includes
+rust/%.o: rust/lex/%.cc
+	$(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $<
+	$(POSTCOMPILE)
+
+# build all rust/parse files in rust folder, add cross-folder includes
+rust/%.o: rust/parse/%.cc
+	$(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $<
+	$(POSTCOMPILE)
+
+# build rust/ast files in rust folder
+rust/%.o: rust/ast/%.cc
+	$(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $<
+	$(POSTCOMPILE)
+
+# build rust/backend files in rust folder
+rust/%.o: rust/backend/%.cc
+	$(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $<
+	$(POSTCOMPILE)
+
+# build rust/expand files in rust folder
+rust/%.o: rust/expand/%.cc
+	$(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $<
+	$(POSTCOMPILE)
+
+# build rust/util files in rust folder
+rust/%.o: rust/util/%.cc
+	$(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $<
+	$(POSTCOMPILE)
+
+# build rust/hir files in rust folder
+rust/%.o: rust/hir/%.cc
+	$(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $<
+	$(POSTCOMPILE)
+
+# build rust/hir/tree files in rust folder
+rust/%.o: rust/hir/tree/%.cc
+	$(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $<
+	$(POSTCOMPILE)
+
+# build rust/resolve files in rust folder
+rust/%.o: rust/resolve/%.cc
+	$(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $<
+	$(POSTCOMPILE)
+
+# build rust/typecheck files in rust folder
+rust/%.o: rust/typecheck/%.cc
+	$(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $<
+	$(POSTCOMPILE)
+
+# build rust/checks/lints files in rust folder
+rust/%.o: rust/checks/lints/%.cc
+	$(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $<
+	$(POSTCOMPILE)
+
+# build rust/checks/errors files in rust folder
+rust/%.o: rust/checks/errors/%.cc
+	$(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $<
+	$(POSTCOMPILE)
+
+# build privacy pass files in rust folder
+rust/%.o: rust/checks/errors/privacy/%.cc
+	$(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $<
+	$(POSTCOMPILE)
+
+# build rust/metadata files in rust folder
+rust/%.o: rust/metadata/%.cc
+	$(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $<
+	$(POSTCOMPILE)
-- 
2.25.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [PATCH Rust front-end v2 32/37] gccrs: Add config-lang.in
  2022-08-24 11:59 Rust frontend patches v2 herron.philip
                   ` (30 preceding siblings ...)
  2022-08-24 11:59 ` [PATCH Rust front-end v2 31/37] gccrs: Add GCC Rust front-end Make-lang.in herron.philip
@ 2022-08-24 11:59 ` herron.philip
  2022-09-14 13:40   ` Richard Biener
  2023-02-20 13:33   ` Rust: Don't depend on unused 'target-libffi', 'target-libbacktrace' (was: [PATCH Rust front-end v2 32/37] gccrs: Add config-lang.in) Thomas Schwinge
  2022-08-24 11:59 ` [PATCH Rust front-end v2 33/37] gccrs: add lang-spec.h herron.philip
                   ` (5 subsequent siblings)
  37 siblings, 2 replies; 59+ messages in thread
From: herron.philip @ 2022-08-24 11:59 UTC (permalink / raw)
  To: gcc-patches; +Cc: gcc-rust, Philip Herron

From: Philip Herron <philip.herron@embecosm.com>

This was a copy paste from gccgo front-end, we do not use any of the
target_libs yet but we will need these when we support the libpanic crate.
---
 gcc/rust/config-lang.in | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)
 create mode 100644 gcc/rust/config-lang.in

diff --git a/gcc/rust/config-lang.in b/gcc/rust/config-lang.in
new file mode 100644
index 00000000000..d2ff376032a
--- /dev/null
+++ b/gcc/rust/config-lang.in
@@ -0,0 +1,34 @@
+# config-lang.in -- Top level configure fragment for gcc Rust frontend.
+
+# Copyright (C) 2009-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/>.
+
+# Configure looks for the existence of this file to auto-config each language.
+# We define several parameters used by configure:
+#
+# language	- name of language as it would appear in $(LANGUAGES)
+# compilers	- value to add to $(COMPILERS)
+
+language="rust"
+compilers="rust1\$(exeext)"
+
+build_by_default="no"
+
+target_libs="target-libffi target-libbacktrace"
+
+gtfiles="\$(srcdir)/rust/rust-lang.cc"
-- 
2.25.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [PATCH Rust front-end v2 33/37] gccrs: add lang-spec.h
  2022-08-24 11:59 Rust frontend patches v2 herron.philip
                   ` (31 preceding siblings ...)
  2022-08-24 11:59 ` [PATCH Rust front-end v2 32/37] gccrs: Add config-lang.in herron.philip
@ 2022-08-24 11:59 ` herron.philip
  2022-09-14 13:40   ` Richard Biener
  2022-10-14 16:33   ` Iain Buclaw
  2022-08-24 11:59 ` [PATCH Rust front-end v2 34/37] gccrs: add lang.opt herron.philip
                   ` (4 subsequent siblings)
  37 siblings, 2 replies; 59+ messages in thread
From: herron.philip @ 2022-08-24 11:59 UTC (permalink / raw)
  To: gcc-patches; +Cc: gcc-rust, Philip Herron

From: Philip Herron <philip.herron@embecosm.com>

This specifies the extensions of the Rust language.
---
 gcc/rust/lang-specs.h | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)
 create mode 100644 gcc/rust/lang-specs.h

diff --git a/gcc/rust/lang-specs.h b/gcc/rust/lang-specs.h
new file mode 100644
index 00000000000..9b14a559dd6
--- /dev/null
+++ b/gcc/rust/lang-specs.h
@@ -0,0 +1,26 @@
+/* lang-specs.h -- gcc driver specs for Rust frontend.
+   Copyright (C) 2009-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/>.  */
+
+/* This is the contribution to the `default_compilers' array in gcc.cc
+   for the Rust language.  */
+
+{".rs", "@rs", 0, 1, 0},
+  {"@rs",
+   "rust1 %i %(cc1_options) %{I*} %{L*} %D %{!fsyntax-only:%(invoke_as)}", 0, 1,
+   0},
-- 
2.25.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [PATCH Rust front-end v2 34/37] gccrs: add lang.opt
  2022-08-24 11:59 Rust frontend patches v2 herron.philip
                   ` (32 preceding siblings ...)
  2022-08-24 11:59 ` [PATCH Rust front-end v2 33/37] gccrs: add lang-spec.h herron.philip
@ 2022-08-24 11:59 ` herron.philip
  2022-09-14 13:39   ` Richard Biener
  2022-08-24 11:59 ` [PATCH Rust front-end v2 35/37] gccrs: add compiler driver herron.philip
                   ` (3 subsequent siblings)
  37 siblings, 1 reply; 59+ messages in thread
From: herron.philip @ 2022-08-24 11:59 UTC (permalink / raw)
  To: gcc-patches; +Cc: gcc-rust, Philip Herron

From: Philip Herron <philip.herron@embecosm.com>

We have some rust specific langugage options note -fwrapv is enabled by
default in the code. We are trying to respect options such as
-Wunused-result which we get by porting over c++ no-discard for rust's
must-use attribute, so we have enabled these by default directly here.
---
 gcc/rust/lang.opt | 118 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 118 insertions(+)
 create mode 100644 gcc/rust/lang.opt

diff --git a/gcc/rust/lang.opt b/gcc/rust/lang.opt
new file mode 100644
index 00000000000..1f6855ede1d
--- /dev/null
+++ b/gcc/rust/lang.opt
@@ -0,0 +1,118 @@
+; Options for the Rust front end.
+; Copyright (C) 2003-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/>.
+
+; See the GCC internals manual for a description of this file's format.
+
+; Please try to keep this file in ASCII collating order.
+
+; Describes command-line options used by this frontend
+
+Language
+Rust
+
+I
+Rust Joined Separate
+; Documented in c.opt
+
+L
+Rust Joined Separate
+; Not documented
+
+Wall
+Rust
+; Documented in c.opt
+
+Wunused-variable
+Rust Var(warn_unused_variable) Init(1) Warning
+; documented in common.opt
+
+Wunused-const-variable
+Rust Warning Alias(Wunused-const-variable=, 2, 0)
+Warn when a const variable is unused.
+
+Wunused-const-variable=
+Rust Joined RejectNegative UInteger Var(warn_unused_const_variable) Init(1) Warning LangEnabledBy(Rust,Wunused-variable, 1, 0) IntegerRange(0, 2)
+Warn when a const variable is unused.
+
+Wunused-result
+Rust Var(warn_unused_result) Init(1) Warning
+Warn if a caller of a function, marked with attribute warn_unused_result, does not use its return value.
+
+frust-crate=
+Rust Joined RejectNegative
+-frust-crate=<name>             Set the crate name for the compilation
+
+frust-debug
+Rust Var(flag_rust_debug)
+Dump various Rust front end internals.
+
+frust-dump-
+Rust Joined RejectNegative
+-frust-dump-<type>	Dump Rust frontend internal information.
+
+frust-max-recursion-depth=
+Rust RejectNegative Type(int) Var(rust_max_recursion_depth) Init(64)
+-frust-max-recursion-depth=integer
+
+frust-mangling=
+Rust Joined RejectNegative Enum(frust_mangling) Var(flag_rust_mangling)
+-frust-mangling=[legacy|v0]     Choose which version to use for name mangling
+
+Enum
+Name(frust_mangling) Type(int) UnknownError(unknown rust mangling option %qs)
+
+EnumValue
+Enum(frust_mangling) String(legacy) Value(0)
+
+EnumValue
+Enum(frust_mangling) String(v0) Value(1)
+
+frust-cfg=
+Rust Joined RejectNegative
+-frust-cfg=<name>             Set a config expansion option
+
+frust-edition=
+Rust Joined RejectNegative Enum(frust_edition) Var(flag_rust_edition)
+-frust-edition=[2015|2018|2021]             Choose which edition to use when compiling rust code
+
+Enum
+Name(frust_edition) Type(int) UnknownError(unknown rust edition %qs)
+
+EnumValue
+Enum(frust_edition) String(2015) Value(0)
+
+EnumValue
+Enum(frust_edition) String(2018) Value(1)
+
+EnumValue
+Enum(frust_edition) String(2021) Value(2)
+
+frust-embed-metadata
+Rust Var(flag_rust_embed_metadata)
+Flag to enable embeding metadata directly into object files
+
+frust-metadata-output=
+Rust Joined RejectNegative
+-frust-metadata-output=<path.rox>  Path to output crate metadata
+
+o
+Rust Joined Separate
+; Documented in common.opt
+
+; This comment is to ensure we retain the blank line above.
-- 
2.25.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [PATCH Rust front-end v2 35/37] gccrs: add compiler driver
  2022-08-24 11:59 Rust frontend patches v2 herron.philip
                   ` (33 preceding siblings ...)
  2022-08-24 11:59 ` [PATCH Rust front-end v2 34/37] gccrs: add lang.opt herron.philip
@ 2022-08-24 11:59 ` herron.philip
  2022-08-24 11:59 ` [PATCH Rust front-end v2 36/37] gccrs: compiler proper interface kicks off the pipeline herron.philip
                   ` (2 subsequent siblings)
  37 siblings, 0 replies; 59+ messages in thread
From: herron.philip @ 2022-08-24 11:59 UTC (permalink / raw)
  To: gcc-patches; +Cc: gcc-rust, Philip Herron

From: Philip Herron <philip.herron@embecosm.com>

Our compiler driver is pretty simple so far, the key piece to enforce is
that a compilation unit in Rust is the whole crate so the process for
compiling rust means pointing the compiler at the main entry point such as
src/lib.rs or src/main.rs where the expansion pass takes over loading the
other source files to include them in the crate.
---
 gcc/rust/rustspec.cc | 191 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 191 insertions(+)
 create mode 100644 gcc/rust/rustspec.cc

diff --git a/gcc/rust/rustspec.cc b/gcc/rust/rustspec.cc
new file mode 100644
index 00000000000..b05f8ae5454
--- /dev/null
+++ b/gcc/rust/rustspec.cc
@@ -0,0 +1,191 @@
+/* rustspec.c -- Specific flags and argument handling of the gcc Rust front end.
+   Copyright (C) 2009-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/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "opts.h"
+
+// satisfy intellisense
+#include "options.h"
+
+/* This bit is set if we saw a `-xfoo' language specification.  */
+#define LANGSPEC (1 << 1)
+/* This bit is set if they did `-lc'.  */
+#define WITHLIBC (1 << 2)
+/* Skip this option.  */
+#define SKIPOPT (1 << 3)
+
+void
+lang_specific_driver (struct cl_decoded_option **in_decoded_options,
+		      unsigned int *in_decoded_options_count,
+		      int *in_added_libraries)
+{
+  unsigned int i, j;
+
+  /* The new argument list will be contained in this.  */
+  struct cl_decoded_option *new_decoded_options;
+
+  /* "-lc" if it appears on the command line.  */
+  const struct cl_decoded_option *saw_libc = 0;
+
+  /* An array used to flag each argument that needs a bit set for
+     LANGSPEC or WITHLIBC.  */
+  int *args;
+
+  /* True if we saw -static.  */
+  int static_link = 0;
+
+  /* True if we should add -shared-libgcc to the command-line.  */
+  int shared_libgcc = 1;
+
+  /* The total number of arguments with the new stuff.  */
+  unsigned int argc;
+
+  /* The argument list.  */
+  struct cl_decoded_option *decoded_options;
+
+  /* The number of libraries added in.  */
+  int added_libraries;
+
+  /* The total number of arguments with the new stuff.  */
+  int num_args = 1;
+
+  /* Whether the -o option was used.  */
+  bool saw_opt_o = false;
+
+  /* The first input file with an extension of .rs.  */
+  const char *first_rust_file = NULL;
+
+  argc = *in_decoded_options_count;
+  decoded_options = *in_decoded_options;
+  added_libraries = *in_added_libraries;
+
+  args = XCNEWVEC (int, argc);
+
+  for (i = 1; i < argc; i++)
+    {
+      const char *arg = decoded_options[i].arg;
+
+      switch (decoded_options[i].opt_index)
+	{
+	case OPT_l:
+	  if (strcmp (arg, "c") == 0)
+	    args[i] |= WITHLIBC;
+	  break;
+
+	case OPT_o:
+	  saw_opt_o = true;
+	  break;
+
+	case OPT_static:
+	  static_link = 1;
+	  break;
+
+	case OPT_static_libgcc:
+	  shared_libgcc = 0;
+	  break;
+
+	case OPT_SPECIAL_input_file:
+	  if (first_rust_file == NULL)
+	    {
+	      int len;
+
+	      len = strlen (arg);
+	      if (len > 3 && strcmp (arg + len - 3, ".rs") == 0)
+		first_rust_file = arg;
+	    }
+	  else
+	    {
+	      // FIXME: ARTHUR: Do we want to error here? If there's already one
+	      // file?
+	      // How do we error here? Do we want to instead just handle that in
+	      // the session manager?
+	    }
+
+	  break;
+	}
+    }
+
+    /* There's no point adding -shared-libgcc if we don't have a shared
+       libgcc.  */
+#ifndef ENABLE_SHARED_LIBGCC
+  shared_libgcc = 0;
+#endif
+
+  /* Make sure to have room for the trailing NULL argument.  */
+  num_args = argc + shared_libgcc * 5 + 10;
+  new_decoded_options = XNEWVEC (struct cl_decoded_option, num_args);
+
+  i = 0;
+  j = 0;
+
+  /* Copy the 0th argument, i.e., the name of the program itself.  */
+  new_decoded_options[j++] = decoded_options[i++];
+
+  /* NOTE: We start at 1 now, not 0.  */
+  while (i < argc)
+    {
+      new_decoded_options[j] = decoded_options[i];
+
+      if (!saw_libc && (args[i] & WITHLIBC))
+	{
+	  --j;
+	  saw_libc = &decoded_options[i];
+	}
+
+      if ((args[i] & SKIPOPT) != 0)
+	--j;
+
+      i++;
+      j++;
+    }
+
+  /* If we didn't see a -o option, add one.  This is because we need
+     the driver to pass all .rs files to rust1.  Without a -o option the
+     driver will invoke rust1 separately for each input file.  FIXME:
+     This should probably use some other interface to force the driver
+     to set combine_inputs.  */
+  if (!saw_opt_o)
+    {
+      generate_option (OPT_o, "a.out", 1, CL_DRIVER, &new_decoded_options[j]);
+      j++;
+    }
+
+  if (saw_libc)
+    new_decoded_options[j++] = *saw_libc;
+  if (shared_libgcc && !static_link)
+    generate_option (OPT_shared_libgcc, NULL, 1, CL_DRIVER,
+		     &new_decoded_options[j++]);
+
+  *in_decoded_options_count = j;
+  *in_decoded_options = new_decoded_options;
+  *in_added_libraries = added_libraries;
+}
+
+/* Called before linking.  Returns 0 on success and -1 on failure.  */
+int
+lang_specific_pre_link (void) /* Not used for Rust.  */
+{
+  return 0;
+}
+
+/* Number of extra output files that lang_specific_pre_link may generate.  */
+int lang_specific_extra_outfiles = 0; /* Not used for Rust.  */
-- 
2.25.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [PATCH Rust front-end v2 36/37] gccrs: compiler proper interface kicks off the pipeline
  2022-08-24 11:59 Rust frontend patches v2 herron.philip
                   ` (34 preceding siblings ...)
  2022-08-24 11:59 ` [PATCH Rust front-end v2 35/37] gccrs: add compiler driver herron.philip
@ 2022-08-24 11:59 ` herron.philip
  2022-08-24 11:59 ` [PATCH Rust front-end v2 37/37] gccrs: Add README, CONTRIBUTING and compiler logo herron.philip
  2022-08-25  9:46 ` Rust frontend patches v2 Philip Herron
  37 siblings, 0 replies; 59+ messages in thread
From: herron.philip @ 2022-08-24 11:59 UTC (permalink / raw)
  To: gcc-patches; +Cc: gcc-rust, Philip Herron

From: Philip Herron <philip.herron@embecosm.com>

This is a wrapper to get out of C land in the rust-lang.cc and into our
class hierarchy for the rust front-end. We expect that the front-end only
support one source file input as the expansion pass will attempt to resolve
that relative pass and parse accordingly.

The main missing piece here is that we are using saw_errors() to return
early which is unnecessary but as our error handling has been improving
over time we will start to take advantage of error node in our type system
as well as error_mark_node from GCC. The caveat being that our lints/checks
expect no errors and will throw an assertion.
---
 gcc/rust/rust-lang.cc            |  452 ++++++++++++
 gcc/rust/rust-session-manager.cc | 1189 ++++++++++++++++++++++++++++++
 gcc/rust/rust-session-manager.h  |  358 +++++++++
 3 files changed, 1999 insertions(+)
 create mode 100644 gcc/rust/rust-lang.cc
 create mode 100644 gcc/rust/rust-session-manager.cc
 create mode 100644 gcc/rust/rust-session-manager.h

diff --git a/gcc/rust/rust-lang.cc b/gcc/rust/rust-lang.cc
new file mode 100644
index 00000000000..c9af790f66b
--- /dev/null
+++ b/gcc/rust/rust-lang.cc
@@ -0,0 +1,452 @@
+// 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/>.
+
+#include "rust-diagnostics.h"
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "target.h"
+#include "tree.h"
+#include "gimple-expr.h"
+#include "diagnostic.h"
+#include "opts.h"
+#include "fold-const.h"
+#include "gimplify.h"
+#include "stor-layout.h"
+#include "debug.h"
+#include "convert.h"
+#include "langhooks.h"
+#include "langhooks-def.h"
+
+#include "selftest.h"
+#include "rust-cfg-parser.h"
+#include "rust-privacy-ctx.h"
+#include "rust-ast-resolve-item.h"
+#include "rust-optional.h"
+
+#include <mpfr.h>
+// note: header files must be in this order or else forward declarations don't
+// work properly. Kinda dumb system, but have to live with it. clang-format
+// seems to mess it up
+/* Order: config, system, coretypes, target, tree, gimple-expr, diagnostic,
+ * opts, fold-const, gimplify, stor-layout, debug, convert, langhooks,
+ * langhooks-def */
+
+// FIXME: test saving intellisense
+#include "options.h"
+
+// version check to stop compiling if c++ isn't c++11 or higher
+#if __cplusplus < 201103
+#error                                                                         \
+  "GCC Rust frontend requires C++11 or higher. You can compile the g++ frontend first and then compile the Rust frontend using that."
+#endif
+// TODO: is this best way to do it? Is it allowed? (should be)
+
+/* General TODOs:
+ *  - convert all copies of expensive-to-copy (deep copy) AST objects into
+ * moves, if possible. Don't remove clone functionality - it may be required for
+ * e.g. HIR conversion.
+ */
+
+#include "rust-system.h"
+#include "rust-session-manager.h"
+
+// Language-dependent contents of a type. GTY() mark used for garbage collector.
+struct GTY (()) lang_type
+{
+};
+
+// Language-dependent contents of a decl.
+struct GTY (()) lang_decl
+{
+};
+
+// Language-dependent contents of an identifier.  This must include a
+// tree_identifier.
+struct GTY (()) lang_identifier
+{
+  struct tree_identifier common;
+};
+
+// The resulting tree type.
+union GTY ((
+  desc ("TREE_CODE (&%h.generic) == IDENTIFIER_NODE"),
+  chain_next (
+    "CODE_CONTAINS_STRUCT (TREE_CODE (&%h.generic), "
+    "TS_COMMON) ? ((union lang_tree_node *) TREE_CHAIN (&%h.generic)) : NULL")))
+  lang_tree_node
+{
+  union tree_node GTY ((tag ("0"), desc ("tree_node_structure (&%h)"))) generic;
+  struct lang_identifier GTY ((tag ("1"))) identifier;
+};
+
+// We don't use language_function.
+struct GTY (()) language_function
+{
+};
+
+// has to be in same compilation unit as session, so here for now
+void
+rust_add_target_info (const char *key, const char *value)
+{
+  sorry ("TODO");
+
+  Rust::Session::get_instance ().options.target_data.insert_key_value_pair (
+    key, value);
+}
+
+/* Language hooks.  */
+
+/* Initial lang hook called (possibly), used for initialisation.
+ * Must call build_common_tree_nodes, set_sizetype, build_common_tree_nodes_2,
+ * and build_common_builtin_nodes, as well as set global variable
+ * void_list_node. Apparently called after option handling? */
+static bool
+grs_langhook_init (void)
+{
+  /* Something to do with this:
+   This allows the code in d-builtins.cc to not have to worry about
+   converting (C signed char *) to (D char *) for string arguments of
+   built-in functions. The parameter (signed_char = false) specifies
+   whether char is signed.  */
+  build_common_tree_nodes (false);
+
+  // Creates a new TREE_LIST node with purpose NULL_TREE and value
+  // void_type_node
+  void_list_node = build_tree_list (NULL_TREE, void_type_node);
+
+  // Builds built-ins for middle-end after all front-end built-ins are already
+  // instantiated
+  build_common_builtin_nodes ();
+
+  mpfr_set_default_prec (128);
+
+  using_eh_for_cleanups ();
+
+  // initialise compiler session
+  Rust::Session::get_instance ().init ();
+
+  return true;
+}
+
+/* The option mask (something to do with options for specific frontends or
+ * something). */
+static unsigned int
+grs_langhook_option_lang_mask (void)
+{
+  return CL_Rust;
+}
+
+/* Initialize the options structure. */
+static void
+grs_langhook_init_options_struct (struct gcc_options *opts)
+{
+  /* Operations are always wrapping in Rust, even on signed integer. This is
+   * useful for the low level wrapping_{add, sub, mul} intrinsics, not for
+   * regular arithmetic operations which are checked for overflow anyway using
+   * builtins */
+  opts->x_flag_wrapv = 1;
+
+  // nothing yet - used by frontends to change specific options for the language
+  Rust::Session::get_instance ().init_options ();
+}
+
+/* Main entry point for front-end, apparently. Finds input file names in global
+ * vars in_fnames and num_in_fnames. From this, frontend can take over and do
+ * actual parsing and initial compilation. This function must create a complete
+ * parse tree in a global var, and then return.
+ *
+ * Some consider this the "start of compilation". */
+static void
+grs_langhook_parse_file (void)
+{
+  rust_debug ("Preparing to parse files. ");
+
+  Rust::Session::get_instance ().handle_input_files (num_in_fnames, in_fnames);
+}
+
+/* Seems to get the exact type for a specific type - e.g. for scalar float with
+ * 32-bit bitsize, it returns float, and for 64-bit bitsize, it returns double.
+ * Used to map RTL nodes to machine modes or something like that. */
+static tree
+grs_langhook_type_for_mode (machine_mode mode, int unsignedp)
+{
+  // TODO: change all this later to match rustc types
+  if (mode == TYPE_MODE (float_type_node))
+    return float_type_node;
+
+  if (mode == TYPE_MODE (double_type_node))
+    return double_type_node;
+
+  if (mode == TYPE_MODE (intQI_type_node)) // quarter integer mode - single byte
+					   // treated as integer
+    return unsignedp ? unsigned_intQI_type_node : intQI_type_node;
+  if (mode
+      == TYPE_MODE (intHI_type_node)) // half integer mode - two-byte integer
+    return unsignedp ? unsigned_intHI_type_node : intHI_type_node;
+  if (mode
+      == TYPE_MODE (intSI_type_node)) // single integer mode - four-byte integer
+    return unsignedp ? unsigned_intSI_type_node : intSI_type_node;
+  if (mode
+      == TYPE_MODE (
+	intDI_type_node)) // double integer mode - eight-byte integer
+    return unsignedp ? unsigned_intDI_type_node : intDI_type_node;
+  if (mode
+      == TYPE_MODE (intTI_type_node)) // tetra integer mode - 16-byte integer
+    return unsignedp ? unsigned_intTI_type_node : intTI_type_node;
+
+  if (mode == TYPE_MODE (integer_type_node))
+    return unsignedp ? unsigned_type_node : integer_type_node;
+
+  if (mode == TYPE_MODE (long_integer_type_node))
+    return unsignedp ? long_unsigned_type_node : long_integer_type_node;
+
+  if (mode == TYPE_MODE (long_long_integer_type_node))
+    return unsignedp ? long_long_unsigned_type_node
+		     : long_long_integer_type_node;
+
+  if (COMPLEX_MODE_P (mode))
+    {
+      if (mode == TYPE_MODE (complex_float_type_node))
+	return complex_float_type_node;
+      if (mode == TYPE_MODE (complex_double_type_node))
+	return complex_double_type_node;
+      if (mode == TYPE_MODE (complex_long_double_type_node))
+	return complex_long_double_type_node;
+      if (mode == TYPE_MODE (complex_integer_type_node) && !unsignedp)
+	return complex_integer_type_node;
+    }
+  /* gcc_unreachable */
+  return NULL;
+}
+
+// Record a builtin function. We just ignore builtin functions.
+static tree
+grs_langhook_builtin_function (tree decl ATTRIBUTE_UNUSED)
+{
+  return decl;
+}
+
+/* Return true if we are in the global binding level (which is never,
+ * apparently). */
+static bool
+grs_langhook_global_bindings_p (void)
+{
+  // return current_function_decl == NULL_TREE;
+  // gcc_unreachable();
+  // return true;
+  return false;
+}
+
+/* Push a declaration into the current binding level.  We can't
+   usefully implement this since we don't want to convert from tree
+   back to one of our internal data structures.  I think the only way
+   this is used is to record a decl which is to be returned by
+   getdecls, and we could implement it for that purpose if
+   necessary.  */
+static tree
+grs_langhook_pushdecl (tree decl ATTRIBUTE_UNUSED)
+{
+  gcc_unreachable ();
+  return NULL;
+}
+
+/* This hook is used to get the current list of declarations as trees.
+   We don't support that; instead we use the write_globals hook.  This
+   can't simply crash because it is called by -gstabs.  */
+static tree
+grs_langhook_getdecls (void)
+{
+  // gcc_unreachable();
+  return NULL;
+}
+
+// Handle Rust-specific options. Return false if nothing happened.
+static bool
+grs_langhook_handle_option (
+  size_t scode, const char *arg, HOST_WIDE_INT value, int kind ATTRIBUTE_UNUSED,
+  location_t loc ATTRIBUTE_UNUSED,
+  const struct cl_option_handlers *handlers ATTRIBUTE_UNUSED)
+{
+  // Convert integer code to lang.opt enum codes with names.
+  enum opt_code code = (enum opt_code) scode;
+
+  // Delegate to session manager
+  return Rust::Session::get_instance ().handle_option (code, arg, value, kind,
+						       loc, handlers);
+}
+
+/* Run after parsing options.  */
+static bool
+grs_langhook_post_options (const char **pfilename ATTRIBUTE_UNUSED)
+{
+  // can be used to override other options if required
+
+  // satisfies an assert in init_excess_precision in toplev.cc
+  if (flag_excess_precision /*_cmdline*/ == EXCESS_PRECISION_DEFAULT)
+    flag_excess_precision /*_cmdline*/ = EXCESS_PRECISION_STANDARD;
+
+  /* Returning false means that the backend should be used.  */
+  return false;
+}
+
+/* Rust-specific gimplification. May need to gimplify e.g.
+ * CALL_EXPR_STATIC_CHAIN */
+static int
+grs_langhook_gimplify_expr (tree *expr_p ATTRIBUTE_UNUSED,
+			    gimple_seq *pre_p ATTRIBUTE_UNUSED,
+			    gimple_seq *post_p ATTRIBUTE_UNUSED)
+{
+  if (TREE_CODE (*expr_p) == CALL_EXPR
+      && CALL_EXPR_STATIC_CHAIN (*expr_p) != NULL_TREE)
+    gimplify_expr (&CALL_EXPR_STATIC_CHAIN (*expr_p), pre_p, post_p,
+		   is_gimple_val, fb_rvalue);
+  return GS_UNHANDLED;
+}
+
+static tree
+grs_langhook_eh_personality (void)
+{
+  static tree personality_decl;
+  if (personality_decl == NULL_TREE)
+    {
+      personality_decl = build_personality_function ("gccrs");
+      rust_preserve_from_gc (personality_decl);
+    }
+  return personality_decl;
+}
+
+tree
+convert (tree type, tree expr)
+{
+  if (type == error_mark_node || expr == error_mark_node
+      || TREE_TYPE (expr) == error_mark_node)
+    return error_mark_node;
+
+  if (type == TREE_TYPE (expr))
+    return expr;
+
+  if (TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (TREE_TYPE (expr)))
+    return fold_convert (type, expr);
+
+  switch (TREE_CODE (type))
+    {
+    case VOID_TYPE:
+    case BOOLEAN_TYPE:
+      return fold_convert (type, expr);
+    case INTEGER_TYPE:
+      return fold (convert_to_integer (type, expr));
+    case POINTER_TYPE:
+      return fold (convert_to_pointer (type, expr));
+    case REAL_TYPE:
+      return fold (convert_to_real (type, expr));
+    case COMPLEX_TYPE:
+      return fold (convert_to_complex (type, expr));
+    default:
+      break;
+    }
+
+  gcc_unreachable ();
+}
+
+/* FIXME: This is a hack to preserve trees that we create from the
+   garbage collector.  */
+
+static GTY (()) tree rust_gc_root;
+
+void
+rust_preserve_from_gc (tree t)
+{
+  rust_gc_root = tree_cons (NULL_TREE, t, rust_gc_root);
+}
+
+/* Convert an identifier for use in an error message.  */
+
+const char *
+rust_localize_identifier (const char *ident)
+{
+  return identifier_to_locale (ident);
+}
+
+/* The language hooks data structure. This is the main interface between the GCC
+ * front-end and the GCC middle-end/back-end. A list of language hooks could be
+ * found in <gcc>/langhooks.h
+ */
+#undef LANG_HOOKS_NAME
+#undef LANG_HOOKS_INIT
+#undef LANG_HOOKS_OPTION_LANG_MASK
+#undef LANG_HOOKS_INIT_OPTIONS_STRUCT
+#undef LANG_HOOKS_HANDLE_OPTION
+#undef LANG_HOOKS_POST_OPTIONS
+#undef LANG_HOOKS_PARSE_FILE
+#undef LANG_HOOKS_TYPE_FOR_MODE
+#undef LANG_HOOKS_BUILTIN_FUNCTION
+#undef LANG_HOOKS_GLOBAL_BINDINGS_P
+#undef LANG_HOOKS_PUSHDECL
+#undef LANG_HOOKS_GETDECLS
+#undef LANG_HOOKS_WRITE_GLOBALS
+#undef LANG_HOOKS_GIMPLIFY_EXPR
+#undef LANG_HOOKS_EH_PERSONALITY
+
+#define LANG_HOOKS_NAME "GNU Rust"
+#define LANG_HOOKS_INIT grs_langhook_init
+#define LANG_HOOKS_OPTION_LANG_MASK grs_langhook_option_lang_mask
+#define LANG_HOOKS_INIT_OPTIONS_STRUCT grs_langhook_init_options_struct
+#define LANG_HOOKS_HANDLE_OPTION grs_langhook_handle_option
+#define LANG_HOOKS_POST_OPTIONS grs_langhook_post_options
+/* Main lang-hook, apparently. Finds input file names in global vars in_fnames
+ * and num_in_fnames From this, frontend can take over and do actual parsing and
+ * initial compilation.
+ * This hook must create a complete parse tree in a global var, and then return.
+ */
+#define LANG_HOOKS_PARSE_FILE grs_langhook_parse_file
+#define LANG_HOOKS_TYPE_FOR_MODE grs_langhook_type_for_mode
+#define LANG_HOOKS_BUILTIN_FUNCTION grs_langhook_builtin_function
+#define LANG_HOOKS_GLOBAL_BINDINGS_P grs_langhook_global_bindings_p
+#define LANG_HOOKS_PUSHDECL grs_langhook_pushdecl
+#define LANG_HOOKS_GETDECLS grs_langhook_getdecls
+#define LANG_HOOKS_GIMPLIFY_EXPR grs_langhook_gimplify_expr
+#define LANG_HOOKS_EH_PERSONALITY grs_langhook_eh_personality
+
+#if CHECKING_P
+
+#undef LANG_HOOKS_RUN_LANG_SELFTESTS
+#define LANG_HOOKS_RUN_LANG_SELFTESTS selftest::run_rust_tests
+
+namespace selftest {
+
+void
+run_rust_tests ()
+{
+  // Call tests for the rust frontend here
+  rust_cfg_parser_test ();
+  rust_privacy_ctx_test ();
+  rust_crate_name_validation_test ();
+  rust_simple_path_resolve_test ();
+  rust_optional_test ();
+}
+} // namespace selftest
+
+#endif /* !CHECKING_P */
+
+// Expands all LANG_HOOKS_x of GCC
+struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER;
+
+// These are for GCC's garbage collector to work properly or something
+#include "gt-rust-rust-lang.h"
+#include "gtype-rust.h"
diff --git a/gcc/rust/rust-session-manager.cc b/gcc/rust/rust-session-manager.cc
new file mode 100644
index 00000000000..6d7f1a85f19
--- /dev/null
+++ b/gcc/rust/rust-session-manager.cc
@@ -0,0 +1,1189 @@
+// 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/>.
+
+#include "rust-session-manager.h"
+#include "rust-diagnostics.h"
+#include "rust-unsafe-checker.h"
+#include "rust-lex.h"
+#include "rust-parse.h"
+#include "rust-macro-expand.h"
+#include "rust-ast-resolve.h"
+#include "rust-ast-lower.h"
+#include "rust-hir-type-check.h"
+#include "rust-privacy-check.h"
+#include "rust-const-checker.h"
+#include "rust-tycheck-dump.h"
+#include "rust-compile.h"
+#include "rust-cfg-parser.h"
+#include "rust-lint-scan-deadcode.h"
+#include "rust-lint-unused-var.h"
+#include "rust-hir-dump.h"
+#include "rust-ast-dump.h"
+#include "rust-export-metadata.h"
+#include "rust-imports.h"
+#include "rust-extern-crate.h"
+#include "rust-attributes.h"
+
+#include "diagnostic.h"
+#include "input.h"
+#include "selftest.h"
+#include "target.h"
+
+extern bool
+saw_errors (void);
+
+extern Linemap *
+rust_get_linemap ();
+
+extern Backend *
+rust_get_backend ();
+
+namespace Rust {
+
+const char *kLexDumpFile = "gccrs.lex.dump";
+const char *kASTDumpFile = "gccrs.ast.dump";
+const char *kASTPrettyDumpFile = "gccrs.ast-pretty.dump";
+const char *kASTExpandedDumpFile = "gccrs.ast-expanded.dump";
+const char *kHIRDumpFile = "gccrs.hir.dump";
+const char *kHIRPrettyDumpFile = "gccrs.hir-pretty.dump";
+const char *kHIRTypeResolutionDumpFile = "gccrs.type-resolution.dump";
+const char *kTargetOptionsDumpFile = "gccrs.target-options.dump";
+
+const std::string kDefaultCrateName = "rust_out";
+const size_t kMaxNameLength = 64;
+
+Session &
+Session::get_instance ()
+{
+  static Session instance;
+  return instance;
+}
+
+static std::string
+infer_crate_name (const std::string &filename)
+{
+  if (filename == "-")
+    return kDefaultCrateName;
+
+  std::string crate = std::string (filename);
+  size_t path_sep = crate.find_last_of (file_separator);
+
+  // find the base filename
+  if (path_sep != std::string::npos)
+    crate.erase (0, path_sep + 1);
+
+  // find the file stem name (remove file extension)
+  size_t ext_position = crate.find_last_of ('.');
+  if (ext_position != std::string::npos)
+    crate.erase (ext_position);
+
+  // Replace all the '-' symbols with '_' per Rust rules
+  for (auto &c : crate)
+    {
+      if (c == '-')
+	c = '_';
+    }
+  return crate;
+}
+
+/* Validate the crate name using the ASCII rules
+   TODO: Support Unicode version of the rules */
+
+static bool
+validate_crate_name (const std::string &crate_name, Error &error)
+{
+  if (crate_name.empty ())
+    {
+      error = Error (Location (), "crate name cannot be empty");
+      return false;
+    }
+  if (crate_name.length () > kMaxNameLength)
+    {
+      error = Error (Location (), "crate name cannot exceed %lu characters",
+		     (unsigned long) kMaxNameLength);
+      return false;
+    }
+  for (auto &c : crate_name)
+    {
+      if (!(ISALNUM (c) || c == '_'))
+	{
+	  error = Error (Location (),
+			 "invalid character %<%c%> in crate name: %<%s%>", c,
+			 crate_name.c_str ());
+	  return false;
+	}
+    }
+  return true;
+}
+
+void
+Session::init ()
+{
+  options.target_data.insert_key_value_pair ("target_pointer_width",
+					     std::to_string (POINTER_SIZE));
+  options.target_data.insert_key_value_pair ("target_endian", BYTES_BIG_ENDIAN
+								? "big"
+								: "little");
+
+  // setup singleton linemap
+  linemap = rust_get_linemap ();
+
+  // setup backend to GCC GIMPLE
+  backend = rust_get_backend ();
+
+  // setup mappings class
+  mappings = Analysis::Mappings::get ();
+}
+
+/* Initialise default options. Actually called before handle_option, unlike init
+ * itself. */
+void
+Session::init_options ()
+{}
+
+// Handle option selection.
+bool
+Session::handle_option (
+  enum opt_code code, const char *arg, HOST_WIDE_INT value ATTRIBUTE_UNUSED,
+  int kind ATTRIBUTE_UNUSED, location_t loc ATTRIBUTE_UNUSED,
+  const struct cl_option_handlers *handlers ATTRIBUTE_UNUSED)
+{
+  // used to store whether results of various stuff are successful
+  bool ret = true;
+
+  // Handles options as listed in lang.opt.
+  switch (code)
+    {
+    case OPT_I:
+      case OPT_L: {
+	// TODO: add search path
+	const std::string p = std::string (arg);
+	add_search_path (p);
+      }
+      break;
+
+    case OPT_frust_crate_:
+      // set the crate name
+      if (arg != nullptr)
+	{
+	  auto error = Error (Location (), std::string ());
+	  if ((ret = validate_crate_name (arg, error)))
+	    {
+	      options.set_crate_name (arg);
+	      options.crate_name_set_manually = true;
+	    }
+	  else
+	    {
+	      rust_assert (!error.message.empty ());
+	      error.emit_error ();
+	    }
+	}
+      else
+	ret = false;
+      break;
+
+    case OPT_frust_dump_:
+      // enable dump and return whether this was successful
+      if (arg != nullptr)
+	{
+	  ret = enable_dump (std::string (arg));
+	}
+      else
+	{
+	  ret = false;
+	}
+      break;
+
+    case OPT_frust_mangling_:
+      Compile::Mangler::set_mangling (flag_rust_mangling);
+      break;
+
+      case OPT_frust_cfg_: {
+	auto string_arg = std::string (arg);
+	ret = handle_cfg_option (string_arg);
+	break;
+      }
+
+    case OPT_frust_edition_:
+      options.set_edition (flag_rust_edition);
+      break;
+
+    case OPT_frust_metadata_output_:
+      options.set_metadata_output (arg);
+      break;
+
+    default:
+      break;
+    }
+
+  return ret;
+}
+
+bool
+Session::handle_cfg_option (std::string &input)
+{
+  std::string key;
+  std::string value;
+
+  // Refactor this if needed
+  if (!parse_cfg_option (input, key, value))
+    {
+      rust_error_at (
+	Location (),
+	"invalid argument to %<-frust-cfg%>: Accepted formats are "
+	"%<-frust-cfg=key%> or %<-frust-cfg=key=\"value\"%> (quoted)");
+      return false;
+    }
+
+  if (value.empty ())
+    // rustc does not seem to error on dup key
+    options.target_data.insert_key (key);
+  else
+    options.target_data.insert_key_value_pair (key, value);
+
+  return true;
+}
+
+/* Enables a certain dump depending on the name passed in. Returns true if
+ * name is valid, false otherwise. */
+bool
+Session::enable_dump (std::string arg)
+{
+  if (arg.empty ())
+    {
+      rust_error_at (
+	Location (),
+	"dump option was not given a name. choose %<lex%>, %<parse%>, "
+	"%<register_plugins%>, %<injection%>, %<expansion%>, %<resolution%>,"
+	" %<target_options%>, %<hir%>, or %<all%>");
+      return false;
+    }
+
+  if (arg == "all")
+    {
+      options.enable_all_dump_options ();
+    }
+  else if (arg == "lex")
+    {
+      options.enable_dump_option (CompileOptions::LEXER_DUMP);
+    }
+  else if (arg == "parse")
+    {
+      options.enable_dump_option (CompileOptions::PARSER_AST_DUMP);
+    }
+  else if (arg == "ast-pretty")
+    {
+      options.enable_dump_option (CompileOptions::AST_DUMP_PRETTY);
+    }
+  else if (arg == "register_plugins")
+    {
+      options.enable_dump_option (CompileOptions::REGISTER_PLUGINS_DUMP);
+    }
+  else if (arg == "injection")
+    {
+      options.enable_dump_option (CompileOptions::INJECTION_DUMP);
+    }
+  else if (arg == "expansion")
+    {
+      options.enable_dump_option (CompileOptions::EXPANSION_DUMP);
+    }
+  else if (arg == "resolution")
+    {
+      options.enable_dump_option (CompileOptions::RESOLUTION_DUMP);
+    }
+  else if (arg == "target_options")
+    {
+      options.enable_dump_option (CompileOptions::TARGET_OPTION_DUMP);
+    }
+  else if (arg == "hir")
+    {
+      options.enable_dump_option (CompileOptions::HIR_DUMP);
+    }
+  else if (arg == "hir-pretty")
+    {
+      options.enable_dump_option (CompileOptions::HIR_DUMP_PRETTY);
+    }
+  else
+    {
+      rust_error_at (
+	Location (),
+	"dump option %qs was unrecognised. choose %<lex%>, %<parse%>, "
+	"%<register_plugins%>, %<injection%>, %<expansion%>, %<resolution%>,"
+	" %<target_options%>, or %<hir%>",
+	arg.c_str ());
+      return false;
+    }
+  return true;
+}
+
+/* Actual main entry point for front-end. Called from langhook to parse files.
+ */
+void
+Session::handle_input_files (int num_files, const char **files)
+{
+  if (num_files != 1)
+    rust_fatal_error (Location (),
+		      "only one file may be specified on the command line");
+
+  const auto &file = files[0];
+
+  if (options.crate_name.empty ())
+    {
+      auto filename = "-";
+      if (num_files > 0)
+	filename = files[0];
+
+      auto crate_name = infer_crate_name (filename);
+      rust_debug ("inferred crate name: %s", crate_name.c_str ());
+      // set the preliminary crate name here
+      // we will figure out the real crate name in `handle_crate_name`
+      options.set_crate_name (crate_name);
+    }
+
+  CrateNum crate_num = mappings->get_next_crate_num (options.get_crate_name ());
+  mappings->set_current_crate (crate_num);
+
+  rust_debug ("Attempting to parse file: %s", file);
+  compile_crate (file);
+}
+
+void
+Session::handle_crate_name (const AST::Crate &parsed_crate)
+{
+  auto mappings = Analysis::Mappings::get ();
+  auto crate_name_changed = false;
+  auto error = Error (Location (), std::string ());
+
+  for (const auto &attr : parsed_crate.inner_attrs)
+    {
+      if (attr.get_path () != "crate_name")
+	continue;
+      if (!attr.has_attr_input ())
+	{
+	  rust_error_at (attr.get_locus (),
+			 "%<crate_name%> accepts one argument");
+	  continue;
+	}
+
+      auto &literal
+	= static_cast<AST::AttrInputLiteral &> (attr.get_attr_input ());
+      const auto &msg_str = literal.get_literal ().as_string ();
+      if (!validate_crate_name (msg_str, error))
+	{
+	  error.locus = attr.get_locus ();
+	  error.emit_error ();
+	  continue;
+	}
+
+      auto options = Session::get_instance ().options;
+      if (options.crate_name_set_manually && (options.crate_name != msg_str))
+	{
+	  rust_error_at (attr.get_locus (),
+			 "%<-frust-crate-name%> and %<#[crate_name]%> are "
+			 "required to match, but %qs does not match %qs",
+			 options.crate_name.c_str (), msg_str.c_str ());
+	}
+      crate_name_changed = true;
+      options.set_crate_name (msg_str);
+      mappings->set_crate_name (mappings->get_current_crate (), msg_str);
+    }
+
+  options.crate_name_set_manually |= crate_name_changed;
+  if (!options.crate_name_set_manually
+      && !validate_crate_name (options.crate_name, error))
+    {
+      error.emit_error ();
+      rust_inform (linemap->get_location (0),
+		   "crate name inferred from this file");
+    }
+}
+
+// Parses a single file with filename filename.
+void
+Session::compile_crate (const char *filename)
+{
+  RAIIFile file_wrap (filename);
+  if (!file_wrap.ok ())
+    {
+      rust_error_at (Location (), "cannot open filename %s: %m", filename);
+      return;
+    }
+
+  // parse file here
+  /* create lexer and parser - these are file-specific and so aren't instance
+   * variables */
+  Lexer lex (filename, std::move (file_wrap), linemap);
+  Parser<Lexer> parser (lex);
+
+  // generate crate from parser
+  std::unique_ptr<AST::Crate> ast_crate = parser.parse_crate ();
+
+  // handle crate name
+  handle_crate_name (*ast_crate.get ());
+
+  // dump options
+  if (options.dump_option_enabled (CompileOptions::LEXER_DUMP))
+    {
+      dump_lex (parser);
+    }
+  if (options.dump_option_enabled (CompileOptions::PARSER_AST_DUMP))
+    {
+      dump_ast (parser, *ast_crate.get ());
+    }
+  if (options.dump_option_enabled (CompileOptions::AST_DUMP_PRETTY))
+    {
+      dump_ast_pretty (*ast_crate.get ());
+    }
+  if (options.dump_option_enabled (CompileOptions::TARGET_OPTION_DUMP))
+    {
+      options.target_data.dump_target_options ();
+    }
+
+  if (saw_errors ())
+    return;
+
+  // setup the mappings for this AST
+  CrateNum current_crate = mappings->get_current_crate ();
+  AST::Crate &parsed_crate
+    = mappings->insert_ast_crate (std::move (ast_crate), current_crate);
+
+  /* basic pipeline:
+   *  - lex
+   *  - parse
+   *  - register plugins (dummy stage for now) - attribute injection? what is
+   * this? (attribute injection is injecting attributes specified in command
+   * line into crate root)
+   *  - injection (some lint checks or dummy, register builtin macros, crate
+   * injection)
+   *  - expansion (expands all macros, maybe build test harness, AST
+   * validation, maybe macro crate)
+   *  - resolution (name resolution, type resolution, maybe feature checking,
+   * maybe buffered lints)
+   *  TODO not done */
+
+  rust_debug ("\033[0;31mSUCCESSFULLY PARSED CRATE \033[0m");
+
+  // If -fsyntax-only was passed, we can just skip the remaining passes.
+  // Parsing errors are already emitted in `parse_crate()`
+  if (flag_syntax_only)
+    return;
+
+  // register plugins pipeline stage
+  register_plugins (parsed_crate);
+  rust_debug ("\033[0;31mSUCCESSFULLY REGISTERED PLUGINS \033[0m");
+  if (options.dump_option_enabled (CompileOptions::REGISTER_PLUGINS_DUMP))
+    {
+      // TODO: what do I dump here?
+    }
+
+  // injection pipeline stage
+  injection (parsed_crate);
+  rust_debug ("\033[0;31mSUCCESSFULLY FINISHED INJECTION \033[0m");
+  if (options.dump_option_enabled (CompileOptions::INJECTION_DUMP))
+    {
+      // TODO: what do I dump here? injected crate names?
+    }
+
+  Analysis::AttributeChecker ().go (parsed_crate);
+
+  // expansion pipeline stage
+  expansion (parsed_crate);
+  rust_debug ("\033[0;31mSUCCESSFULLY FINISHED EXPANSION \033[0m");
+  if (options.dump_option_enabled (CompileOptions::EXPANSION_DUMP))
+    {
+      // dump AST with expanded stuff
+      rust_debug ("BEGIN POST-EXPANSION AST DUMP");
+      dump_ast_expanded (parser, parsed_crate);
+      rust_debug ("END POST-EXPANSION AST DUMP");
+    }
+
+  // resolution pipeline stage
+  Resolver::NameResolution::Resolve (parsed_crate);
+  if (options.dump_option_enabled (CompileOptions::RESOLUTION_DUMP))
+    {
+      // TODO: what do I dump here? resolved names? AST with resolved names?
+    }
+
+  if (saw_errors ())
+    return;
+
+  // lower AST to HIR
+  std::unique_ptr<HIR::Crate> lowered
+    = HIR::ASTLowering::Resolve (parsed_crate);
+  if (saw_errors ())
+    return;
+
+  // add the mappings to it
+  HIR::Crate &hir = mappings->insert_hir_crate (std::move (lowered));
+  if (options.dump_option_enabled (CompileOptions::HIR_DUMP))
+    {
+      dump_hir (hir);
+    }
+  if (options.dump_option_enabled (CompileOptions::HIR_DUMP_PRETTY))
+    {
+      dump_hir_pretty (hir);
+    }
+
+  // type resolve
+  Resolver::TypeResolution::Resolve (hir);
+  if (options.dump_option_enabled (CompileOptions::TYPE_RESOLUTION_DUMP))
+    {
+      dump_type_resolution (hir);
+    }
+
+  if (saw_errors ())
+    return;
+
+  // Various HIR error passes. The privacy pass happens before the unsafe checks
+  Privacy::Resolver::resolve (hir);
+  if (saw_errors ())
+    return;
+
+  HIR::UnsafeChecker ().go (hir);
+  HIR::ConstChecker ().go (hir);
+
+  if (saw_errors ())
+    return;
+
+  // do compile to gcc generic
+  Compile::Context ctx (backend);
+  Compile::CompileCrate::Compile (hir, &ctx);
+
+  // we can't do static analysis if there are errors to worry about
+  if (!saw_errors ())
+    {
+      // lints
+      Analysis::ScanDeadcode::Scan (hir);
+      Analysis::UnusedVariables::Lint (ctx);
+
+      // metadata
+      bool specified_emit_metadata
+	= flag_rust_embed_metadata || options.metadata_output_path_set ();
+      if (!specified_emit_metadata)
+	{
+	  Metadata::PublicInterface::ExportTo (
+	    hir, Metadata::PublicInterface::expected_metadata_filename ());
+	}
+      else
+	{
+	  if (flag_rust_embed_metadata)
+	    Metadata::PublicInterface::Export (hir);
+	  if (options.metadata_output_path_set ())
+	    Metadata::PublicInterface::ExportTo (
+	      hir, options.get_metadata_output ());
+	}
+    }
+
+  // pass to GCC middle-end
+  ctx.write_to_backend ();
+}
+
+void
+Session::register_plugins (AST::Crate &crate ATTRIBUTE_UNUSED)
+{
+  rust_debug ("ran register_plugins (with no body)");
+}
+
+// TODO: move somewhere else
+bool
+contains_name (const AST::AttrVec &attrs, std::string name)
+{
+  for (const auto &attr : attrs)
+    {
+      if (attr.get_path () == name)
+	return true;
+    }
+
+  return false;
+}
+
+void
+Session::injection (AST::Crate &crate)
+{
+  rust_debug ("started injection");
+
+  // lint checks in future maybe?
+
+  // register builtin macros
+  /* In rustc, builtin macros are divided into 3 categories depending on use -
+   * "bang" macros, "attr" macros, and "derive" macros. I think the meanings
+   * of these categories should be fairly obvious to anyone who has used rust.
+   * Builtin macro list by category: Bang
+   *      - asm
+   *      - assert
+   *      - cfg
+   *      - column
+   *      - compile_error
+   *      - concat_idents
+   *      - concat
+   *      - env
+   *      - file
+   *      - format_args_nl
+   *      - format_args
+   *      - global_asm
+   *      - include_bytes
+   *      - include_str
+   *      - include
+   *      - line
+   *      - log_syntax
+   *      - module_path
+   *      - option_env
+   *      - stringify
+   *      - trace_macros
+   *  Attr
+   *      - bench
+   *      - global_allocator
+   *      - test
+   *      - test_case
+   *  Derive
+   *      - Clone
+   *      - Copy
+   *      - Debug
+   *      - Default
+   *      - Eq
+   *      - Hash
+   *      - Ord
+   *      - PartialEq
+   *      - PartialOrd
+   *      - RustcDecodable
+   *      - RustcEncodable
+   * rustc also has a "quote" macro that is defined differently and is
+   * supposedly not stable so eh. */
+  /* TODO: actually implement injection of these macros. In particular, derive
+   * macros, cfg, and test should be prioritised since they seem to be used
+   * the most. */
+
+  // crate injection
+  std::vector<std::string> names;
+  if (contains_name (crate.inner_attrs, "no_core"))
+    {
+      // no prelude
+      injected_crate_name = "";
+    }
+  else if (contains_name (crate.inner_attrs, "no_std"))
+    {
+      names.push_back ("core");
+
+      if (!contains_name (crate.inner_attrs, "compiler_builtins"))
+	{
+	  names.push_back ("compiler_builtins");
+	}
+
+      injected_crate_name = "core";
+    }
+  else
+    {
+      names.push_back ("std");
+
+      injected_crate_name = "std";
+    }
+
+  // reverse iterate through names to insert crate items in "forward" order at
+  // beginning of crate
+  for (auto it = names.rbegin (); it != names.rend (); ++it)
+    {
+      // create "macro use" attribute for use on extern crate item to enable
+      // loading macros from it
+      AST::Attribute attr (AST::SimplePath::from_str ("macro_use", Location ()),
+			   nullptr);
+
+      // create "extern crate" item with the name
+      std::unique_ptr<AST::ExternCrate> extern_crate (
+	new AST::ExternCrate (*it, AST::Visibility::create_error (),
+			      {std::move (attr)},
+			      Linemap::unknown_location ()));
+
+      // insert at beginning
+      // crate.items.insert (crate.items.begin (), std::move (extern_crate));
+    }
+
+  // create use tree path
+  // prelude is injected_crate_name
+  // FIXME: Once we do want to include the standard library, add the prelude
+  // use item
+  // std::vector<AST::SimplePathSegment> segments
+  //   = {AST::SimplePathSegment (injected_crate_name, Location ()),
+  //      AST::SimplePathSegment ("prelude", Location ()),
+  //      AST::SimplePathSegment ("v1", Location ())};
+  // // create use tree and decl
+  // std::unique_ptr<AST::UseTreeGlob> use_tree (
+  //   new AST::UseTreeGlob (AST::UseTreeGlob::PATH_PREFIXED,
+  //     		  AST::SimplePath (std::move (segments)), Location ()));
+  // AST::Attribute prelude_attr (AST::SimplePath::from_str ("prelude_import",
+  //     						  Location ()),
+  //     		       nullptr);
+  // std::unique_ptr<AST::UseDeclaration> use_decl (
+  //   new AST::UseDeclaration (std::move (use_tree),
+  //     		     AST::Visibility::create_error (),
+  //     		     {std::move (prelude_attr)}, Location ()));
+
+  // crate.items.insert (crate.items.begin (), std::move (use_decl));
+
+  /* TODO: potentially add checking attribute crate type? I can't figure out
+   * what this does currently comment says "Unconditionally collect crate
+   * types from attributes to make them used", which presumably refers to
+   * checking the linkage info by "crate_type". It also seems to ensure that
+   * an invalid crate type is not specified, so maybe just do that. Valid
+   * crate types: bin lib dylib staticlib cdylib rlib proc-macro */
+
+  // this crate type will have options affecting the metadata ouput
+
+  rust_debug ("finished injection");
+}
+
+void
+Session::expansion (AST::Crate &crate)
+{
+  rust_debug ("started expansion");
+
+  /* rustc has a modification to windows PATH temporarily here, which may end
+   * up being required */
+
+  // create macro expansion config?
+  // if not, would at least have to configure recursion_limit
+  ExpansionCfg cfg;
+
+  // create extctxt? from parse session, cfg, and resolver?
+  /* expand by calling cxtctxt object's monotonic_expander's expand_crate
+   * method. */
+  MacroExpander expander (crate, cfg, *this);
+  expander.expand_crate ();
+
+  // error reporting - check unused macros, get missing fragment specifiers
+
+  // build test harness
+
+  // ast validation (also with proc macro decls)
+
+  // maybe create macro crate if not rustdoc
+
+  rust_debug ("finished expansion");
+}
+
+void
+Session::dump_lex (Parser<Lexer> &parser) const
+{
+  std::ofstream out;
+  out.open (kLexDumpFile);
+  if (out.fail ())
+    {
+      rust_error_at (Linemap::unknown_location (), "cannot open %s:%m; ignored",
+		     kLexDumpFile);
+      return;
+    }
+
+  // TODO: rewrite lexer dump or something so that it allows for the crate
+  // to already be parsed
+  parser.debug_dump_lex_output (out);
+  out.close ();
+}
+
+void
+Session::dump_ast (Parser<Lexer> &parser, AST::Crate &crate) const
+{
+  std::ofstream out;
+  out.open (kASTDumpFile);
+  if (out.fail ())
+    {
+      rust_error_at (Linemap::unknown_location (), "cannot open %s:%m; ignored",
+		     kASTDumpFile);
+      return;
+    }
+
+  parser.debug_dump_ast_output (crate, out);
+  out.close ();
+}
+
+void
+Session::dump_ast_pretty (AST::Crate &crate) const
+{
+  std::ofstream out;
+  out.open (kASTPrettyDumpFile);
+  if (out.fail ())
+    {
+      rust_error_at (Linemap::unknown_location (), "cannot open %s:%m; ignored",
+		     kASTDumpFile);
+      return;
+    }
+
+  AST::Dump (out).go (crate);
+
+  out.close ();
+}
+
+void
+Session::dump_ast_expanded (Parser<Lexer> &parser, AST::Crate &crate) const
+{
+  std::ofstream out;
+  out.open (kASTExpandedDumpFile);
+  if (out.fail ())
+    {
+      rust_error_at (Linemap::unknown_location (), "cannot open %s:%m; ignored",
+		     kASTExpandedDumpFile);
+      return;
+    }
+
+  parser.debug_dump_ast_output (crate, out);
+  out.close ();
+}
+
+void
+Session::dump_hir (HIR::Crate &crate) const
+{
+  std::ofstream out;
+  out.open (kHIRDumpFile);
+  if (out.fail ())
+    {
+      rust_error_at (Linemap::unknown_location (), "cannot open %s:%m; ignored",
+		     kHIRDumpFile);
+      return;
+    }
+
+  out << crate.as_string ();
+  out.close ();
+}
+
+void
+Session::dump_hir_pretty (HIR::Crate &crate) const
+{
+  std::ofstream out;
+  out.open (kHIRPrettyDumpFile);
+  if (out.fail ())
+    {
+      rust_error_at (Linemap::unknown_location (), "cannot open %s:%m; ignored",
+		     kHIRPrettyDumpFile);
+      return;
+    }
+
+  HIR::Dump (out).go (crate);
+  out.close ();
+}
+
+void
+Session::dump_type_resolution (HIR::Crate &hir) const
+{
+  std::ofstream out;
+  out.open (kHIRTypeResolutionDumpFile);
+  if (out.fail ())
+    {
+      rust_error_at (Linemap::unknown_location (), "cannot open %s:%m; ignored",
+		     kHIRTypeResolutionDumpFile);
+      return;
+    }
+
+  Resolver::TypeResolverDump::go (hir, out);
+  out.close ();
+}
+
+// imports
+
+NodeId
+Session::load_extern_crate (const std::string &crate_name, Location locus)
+{
+  // has it already been loaded?
+  CrateNum found_crate_num = UNKNOWN_CREATENUM;
+  bool found = mappings->lookup_crate_name (crate_name, found_crate_num);
+  if (found)
+    {
+      NodeId resolved_node_id = UNKNOWN_NODEID;
+      bool resolved
+	= mappings->crate_num_to_nodeid (found_crate_num, resolved_node_id);
+      rust_assert (resolved);
+
+      return resolved_node_id;
+    }
+
+  std::string relative_import_path = "";
+  Import::Stream *s
+    = Import::open_package (crate_name, locus, relative_import_path);
+  if (s == NULL)
+    {
+      rust_error_at (locus, "failed to locate crate %<%s%>",
+		     crate_name.c_str ());
+      return UNKNOWN_NODEID;
+    }
+
+  Imports::ExternCrate extern_crate (*s);
+  bool ok = extern_crate.load (locus);
+  if (!ok)
+    {
+      rust_error_at (locus, "failed to load crate metadata");
+      return UNKNOWN_NODEID;
+    }
+
+  // ensure the current vs this crate name don't collide
+  const std::string current_crate_name = mappings->get_current_crate_name ();
+  if (current_crate_name.compare (extern_crate.get_crate_name ()) == 0)
+    {
+      rust_error_at (locus, "current crate name %<%s%> collides with this",
+		     current_crate_name.c_str ());
+      return UNKNOWN_NODEID;
+    }
+
+  // setup mappings
+  CrateNum saved_crate_num = mappings->get_current_crate ();
+  CrateNum crate_num
+    = mappings->get_next_crate_num (extern_crate.get_crate_name ());
+  mappings->set_current_crate (crate_num);
+
+  // then lets parse this as a 2nd crate
+  Lexer lex (extern_crate.get_metadata ());
+  Parser<Lexer> parser (lex);
+  std::unique_ptr<AST::Crate> metadata_crate = parser.parse_crate ();
+  AST::Crate &parsed_crate
+    = mappings->insert_ast_crate (std::move (metadata_crate), crate_num);
+
+  // name resolve it
+  Resolver::NameResolution::Resolve (parsed_crate);
+
+  // perform hir lowering
+  std::unique_ptr<HIR::Crate> lowered
+    = HIR::ASTLowering::Resolve (parsed_crate);
+  HIR::Crate &hir = mappings->insert_hir_crate (std::move (lowered));
+
+  // perform type resolution
+  Resolver::TypeResolution::Resolve (hir);
+
+  // always restore the crate_num
+  mappings->set_current_crate (saved_crate_num);
+
+  return parsed_crate.get_node_id ();
+}
+//
+
+void
+TargetOptions::dump_target_options () const
+{
+  std::ofstream out;
+  out.open (kTargetOptionsDumpFile);
+  if (out.fail ())
+    {
+      rust_error_at (Linemap::unknown_location (), "cannot open %s:%m; ignored",
+		     kTargetOptionsDumpFile);
+      return;
+    }
+
+  if (features.empty ())
+    {
+      out << "No target options available!\n";
+    }
+
+  for (const auto &pairs : features)
+    {
+      for (const auto &value : pairs.second)
+	out << pairs.first + ": \"" + value + "\"\n";
+
+      if (pairs.second.empty ())
+	out << pairs.first + "\n";
+    }
+
+  out.close ();
+}
+
+void
+TargetOptions::init_derived_values ()
+{
+  // enable derived values based on target families
+  if (has_key_value_pair ("target_family", "unix"))
+    insert_key ("unix");
+  if (has_key_value_pair ("target_family", "windows"))
+    insert_key ("windows");
+
+  // implicitly enable features - this should not be required in general
+  if (has_key_value_pair ("target_feature", "aes"))
+    enable_implicit_feature_reqs ("aes");
+  if (has_key_value_pair ("target_feature", "avx"))
+    enable_implicit_feature_reqs ("sse4.2");
+  if (has_key_value_pair ("target_feature", "avx2"))
+    enable_implicit_feature_reqs ("avx");
+  if (has_key_value_pair ("target_feature", "pclmulqdq"))
+    enable_implicit_feature_reqs ("sse2");
+  if (has_key_value_pair ("target_feature", "sha"))
+    enable_implicit_feature_reqs ("sse2");
+  if (has_key_value_pair ("target_feature", "sse2"))
+    enable_implicit_feature_reqs ("sse");
+  if (has_key_value_pair ("target_feature", "sse3"))
+    enable_implicit_feature_reqs ("sse2");
+  if (has_key_value_pair ("target_feature", "sse4.1"))
+    enable_implicit_feature_reqs ("sse3");
+  if (has_key_value_pair ("target_feature", "sse4.2"))
+    enable_implicit_feature_reqs ("sse4.1");
+  if (has_key_value_pair ("target_feature", "ssse3"))
+    enable_implicit_feature_reqs ("sse3");
+}
+
+void
+TargetOptions::enable_implicit_feature_reqs (std::string feature)
+{
+  if (feature == "aes")
+    enable_implicit_feature_reqs ("sse2");
+  else if (feature == "avx")
+    enable_implicit_feature_reqs ("sse4.2");
+  else if (feature == "avx2")
+    enable_implicit_feature_reqs ("avx");
+  else if (feature == "fma")
+    enable_implicit_feature_reqs ("avx");
+  else if (feature == "pclmulqdq")
+    enable_implicit_feature_reqs ("sse2");
+  else if (feature == "sha")
+    enable_implicit_feature_reqs ("sse2");
+  else if (feature == "sse2")
+    enable_implicit_feature_reqs ("sse");
+  else if (feature == "sse3")
+    enable_implicit_feature_reqs ("sse2");
+  else if (feature == "sse4.1")
+    enable_implicit_feature_reqs ("sse3");
+  else if (feature == "sse4.2")
+    enable_implicit_feature_reqs ("sse4.1");
+  else if (feature == "ssse3")
+    enable_implicit_feature_reqs ("sse3");
+
+  if (!has_key_value_pair ("target_feature", feature))
+    {
+      insert_key_value_pair ("target_feature", feature);
+
+      rust_debug ("had to implicitly enable feature '%s'!", feature.c_str ());
+    }
+}
+
+// NOTEs:
+/* mrustc compile pipeline:
+ *  - target load (pass target spec to parser?)
+ *  - parse (convert source to AST)
+ *  - load crates (load any explicitly mentioned extern crates [not all of
+ * them])
+ *  - expand (AST transformations from attributes and macros, loads remaining
+ * extern crates [std/core and any triggered by macro expansion])
+ *  - implicit crates (test harness, allocator crate, panic crate)
+ *  - resolve use (annotate every 'use' item with source [supposedly handles
+ * nasty recursion])
+ *  - resolve index (generate index of visible items for every module [avoids
+ * recursion in next pass])
+ *  - resolve absolute (resolve all paths into either variable names
+ * [types/values] or absolute paths)
+ *  - HIR lower (convert modified AST to simpler HIR [both expressions and
+ * module tree])
+ *  - resolve type aliases (replace any usages of type aliases with actual
+ * type [except associated types])
+ *  - resolve bind (iterate HIR tree and set binding annotations on all
+ * concrete types [avoids path lookups later])
+ *  - resolve HIR markings (generate "markings" [e.g. for Copy/Send/Sync/...]
+ * for all types
+ *  - sort impls (small pass - sort impls into groups)
+ *  - resolve UFCS outer (determine source trait for all top-level <T>::Type
+ * [qualified] paths)
+ *  - resolve UFCS paths (do the same, but include for exprs this time. also
+ * normalises results of previous pass [expanding known associated types])
+ *  - constant evaluate (evaluate all constants)
+ *  - typecheck outer (checks impls are sane)
+ *  - typecheck expressions (resolve and check types for all exprs)
+ *  - expand HIR annotate (annotate how exprs are used - used for closure
+ * extractions and reborrows)
+ *  - expand HIR closures (extract closures into structs implementing Fn*
+ * traits)
+ *  - expand HIR vtables (generate vtables for types with dyn dispatch)
+ *  - expand HIR calls (converts method and callable calls into explicit
+ * function calls)
+ *  - expand HIR reborrows (apply reborrow rules [taking '&mut *v' instead of
+ * 'v'])
+ *  - expand HIR erasedtype (replace all erased types 'impl Trait' with the
+ * true type)
+ *  - typecheck expressions (validate - double check that previous passes
+ * haven't broke type system rules)
+ *  - lower MIR (convert HIR exprs into a control-flow graph [MIR])
+ *  - MIR validate (check that the generated MIR is consistent)
+ *  - MIR cleanup (perform various transformations on MIR - replace reads of
+ * const items with the item itself; convert casts to unsized types into
+ * 'MakeDst' operations)
+ *  - MIR optimise (perform various simple optimisations on the MIR - constant
+ * propagation, dead code elimination, borrow elimination, some inlining)
+ *  - MIR validate PO (re-validate the MIR)
+ *  - MIR validate full (optionally: perform expensive state-tracking
+ * validation on MIR)
+ *  - trans enumerate (enumerate all items needed for code generation,
+ * primarily types used for generics)
+ *  - trans auto impls (create magic trait impls as enumerated in previous
+ * pass)
+ *  - trans monomorph (generate monomorphised copies of all functions [with
+ * generics replaced with real types])
+ *  - MIR optimise inline (run optimisation again, this time with full type
+ * info [primarily for inlining])
+ *  - HIR serialise (write out HIR dump [module tree and generic/inline MIR])
+ *  - trans codegen (generate final output file: emit C source file and call C
+ * compiler) */
+
+/* rustc compile pipeline (basic, in way less detail):
+ *  - parse input (parse .rs to AST)
+ *  - name resolution, macro expansion, and configuration (process AST
+ * recursively, resolving paths, expanding macros, processing #[cfg] nodes
+ * [i.e. maybe stripping stuff from AST])
+ *  - lower to HIR
+ *  - type check and other analyses (e.g. privacy checking)
+ *  - lower to MIR and post-processing (and do stuff like borrow checking)
+ *  - translation to LLVM IR and LLVM optimisations (produce the .o files)
+ *  - linking (link together .o files) */
+
+/* Pierced-together rustc compile pipeline (from source):
+ *  - parse input (parse file to crate)
+ *  - register plugins (attributes injection, set various options, register
+ * lints, load plugins)
+ *  - expansion/configure and expand (initial 'cfg' processing, 'loading
+ * compiler plugins', syntax expansion, secondary 'cfg' expansion, synthesis
+ * of a test harness if required, injection of any std lib dependency and
+ * prelude, and name resolution) - actually documented inline
+ *      - seeming pierced-together order: pre-AST expansion lint checks,
+ * registering builtin macros, crate injection, then expand all macros, then
+ * maybe build test harness, AST validation, maybe create a macro crate (if
+ * not rustdoc), name resolution, complete gated feature checking, add all
+ * buffered lints
+ *  - create global context (lower to HIR)
+ *  - analysis on global context (HIR optimisations? create MIR?)
+ *  - code generation
+ *  - link */
+} // namespace Rust
+
+#if CHECKING_P
+namespace selftest {
+void
+rust_crate_name_validation_test (void)
+{
+  auto error = Rust::Error (Location (), std::string ());
+  ASSERT_TRUE (Rust::validate_crate_name ("example", error));
+  ASSERT_TRUE (Rust::validate_crate_name ("abcdefg_1234", error));
+  ASSERT_TRUE (Rust::validate_crate_name ("1", error));
+  // FIXME: The next test does not pass as of current implementation
+  // ASSERT_TRUE (Rust::CompileOptions::validate_crate_name ("惊吓"));
+  // NOTE: - is not allowed in the crate name ...
+
+  ASSERT_FALSE (Rust::validate_crate_name ("abcdefg-1234", error));
+  ASSERT_FALSE (Rust::validate_crate_name ("a+b", error));
+  ASSERT_FALSE (Rust::validate_crate_name ("/a+b/", error));
+
+  /* Tests for crate name inference */
+  ASSERT_EQ (Rust::infer_crate_name ("c.rs"), "c");
+  // NOTE: ... but - is allowed when in the filename
+  ASSERT_EQ (Rust::infer_crate_name ("a-b.rs"), "a_b");
+  ASSERT_EQ (Rust::infer_crate_name ("book.rs.txt"), "book.rs");
+#if defined(HAVE_DOS_BASED_FILE_SYSTEM)
+  ASSERT_EQ (Rust::infer_crate_name ("a\\c\\a-b.rs"), "a_b");
+#else
+  ASSERT_EQ (Rust::infer_crate_name ("a/c/a-b.rs"), "a_b");
+#endif
+}
+} // namespace selftest
+#endif // CHECKING_P
diff --git a/gcc/rust/rust-session-manager.h b/gcc/rust/rust-session-manager.h
new file mode 100644
index 00000000000..99dd107239b
--- /dev/null
+++ b/gcc/rust/rust-session-manager.h
@@ -0,0 +1,358 @@
+// 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/>.
+// #include "rust-session-manager.h"
+
+#ifndef RUST_SESSION_MANAGER_H
+#define RUST_SESSION_MANAGER_H
+
+#include "rust-linemap.h"
+#include "rust-backend.h"
+#include "rust-hir-map.h"
+#include "safe-ctype.h"
+
+#include "config.h"
+#include "rust-system.h"
+#include "coretypes.h"
+#include "options.h"
+
+namespace Rust {
+// parser forward decl
+template <typename ManagedTokenSource> class Parser;
+class Lexer;
+// crate forward decl
+namespace AST {
+struct Crate;
+}
+// crate forward decl
+namespace HIR {
+struct Crate;
+}
+
+/* Data related to target, most useful for conditional compilation and
+ * whatever. */
+struct TargetOptions
+{
+  /* TODO: maybe make private and access through helpers to allow changes to
+   * impl */
+  std::unordered_map<std::string, std::unordered_set<std::string> > features;
+
+public:
+  // Returns whether a key is defined in the feature set.
+  bool has_key (std::string key) const
+  {
+    return features.find (key) != features.end ();
+  }
+
+  // Returns whether a key exists with the given value in the feature set.
+  bool has_key_value_pair (std::string key, std::string value) const
+  {
+    auto it = features.find (key);
+    if (it != features.end ())
+      {
+	auto set = it->second;
+	auto it2 = set.find (value);
+	if (it2 != set.end ())
+	  return true;
+      }
+    return false;
+  }
+
+  /* Returns the singular value from the key, or if the key has multiple, an
+   * empty string. */
+  std::string get_singular_value (std::string key) const
+  {
+    auto it = features.find (key);
+    if (it != features.end ())
+      {
+	auto set = it->second;
+	if (set.size () == 1)
+	  return *set.begin ();
+      }
+    return "";
+  }
+
+  /* Returns all values associated with a key (including none), or an empty
+   * set if no key is found. */
+  std::unordered_set<std::string> get_values_for_key (std::string key) const
+  {
+    auto it = features.find (key);
+    if (it != features.end ())
+      return it->second;
+    return {};
+  }
+
+  /* Inserts a key (no value) into the feature set. This will do nothing if
+   * the key already exists. This returns whether the insertion was successful
+   * (i.e. whether key already existed). */
+  bool insert_key (std::string key)
+  {
+    return features
+      .insert (std::make_pair (key, std::unordered_set<std::string> ()))
+      .second;
+  }
+
+  // Inserts a key-value pair into the feature set.
+  void insert_key_value_pair (std::string key, std::string value)
+  {
+    auto existing_set = get_values_for_key (key);
+    existing_set.insert (std::move (value));
+    features[std::move (key)] = std::move (existing_set);
+  }
+
+  // Dump all target options to stderr.
+  void dump_target_options () const;
+
+  /* Creates derived values and implicit enables after all target info is
+   * added (e.g. "unix"). */
+  void init_derived_values ();
+
+  /* Enables all requirements for the feature given, and will enable feature
+   * itself if not enabled. */
+  void enable_implicit_feature_reqs (std::string feature);
+
+  /* According to reference, Rust uses either multi-map key-values or just
+   * values (although values may be aliases for a key-value value). This seems
+   * like overkill. Thus, depending on whether the attributes used in cfg are
+   * fixed or not, I think I'll either put each non-multimap "key-value" as a
+   * separate field and have the multimap "key-values" in a regular map for
+   * that one key, or actually use a multimap.
+   *
+   * rustc itself uses a set of key-value tuples where the second tuple
+   * element is optional. This gets rid of the requirement to make a
+   * multi-map, I guess, but seems like it might make search slow (unless all
+   * "is defined"-only ones have empty string as second element). */
+  /* cfg attributes:
+   * - target_arch: single value
+   * - target_feature: multiple values possible
+   * - target_os: single value
+   * - target_family: single value (or no value?)
+   * - unix: set when target_family = "unix"
+   * - windows: set when target_family = "windows"
+   *  - if these are just syntactic sugar, then maybe have a separate set or
+   * map for this kind of stuff
+   * - target_env: set when needed for disambiguation about ABI - usually
+   * empty string for GNU, complicated
+   *  - seems to be a single value (if any)
+   * - target_endian: single value; "little" or "big"
+   * - target_pointer_width: single value, "32" for 32-bit pointers, etc.
+   * - target_vendor, single value
+   * - test: set when testing is being done
+   *  - again, seems similar to a "is defined" rather than "is equal to" like
+   * unix
+   * - debug_assertions: seems to "is defined"
+   * - proc_macro: no idea, bad docs. seems to be boolean, so maybe "is
+   * defined"
+   */
+};
+
+// Defines compiler options (e.g. dump, etc.).
+struct CompileOptions
+{
+  enum DumpOption
+  {
+    LEXER_DUMP,
+    PARSER_AST_DUMP,
+    AST_DUMP_PRETTY,
+    REGISTER_PLUGINS_DUMP,
+    INJECTION_DUMP,
+    EXPANSION_DUMP,
+    RESOLUTION_DUMP,
+    TARGET_OPTION_DUMP,
+    HIR_DUMP,
+    HIR_DUMP_PRETTY,
+    TYPE_RESOLUTION_DUMP,
+  };
+
+  std::set<DumpOption> dump_options;
+
+  /* configuration options - actually useful for conditional compilation and
+   * whatever data related to target arch, features, os, family, env, endian,
+   * pointer width, vendor */
+  TargetOptions target_data;
+  std::string crate_name;
+  bool crate_name_set_manually = false;
+  bool enable_test = false;
+  bool debug_assertions = false;
+  bool proc_macro = false;
+  std::string metadata_output_path;
+
+  enum class Edition
+  {
+    E2015 = 0,
+    E2018,
+    E2021,
+  } edition
+    = Edition::E2015;
+
+  bool dump_option_enabled (DumpOption option) const
+  {
+    return dump_options.find (option) != dump_options.end ();
+  }
+
+  void enable_dump_option (DumpOption option) { dump_options.insert (option); }
+
+  void enable_all_dump_options ()
+  {
+    enable_dump_option (DumpOption::LEXER_DUMP);
+    enable_dump_option (DumpOption::PARSER_AST_DUMP);
+    enable_dump_option (DumpOption::AST_DUMP_PRETTY);
+    enable_dump_option (DumpOption::REGISTER_PLUGINS_DUMP);
+    enable_dump_option (DumpOption::INJECTION_DUMP);
+    enable_dump_option (DumpOption::EXPANSION_DUMP);
+    enable_dump_option (DumpOption::RESOLUTION_DUMP);
+    enable_dump_option (DumpOption::TARGET_OPTION_DUMP);
+    enable_dump_option (DumpOption::HIR_DUMP);
+    enable_dump_option (DumpOption::HIR_DUMP_PRETTY);
+    enable_dump_option (DumpOption::TYPE_RESOLUTION_DUMP);
+  }
+
+  void set_crate_name (std::string name)
+  {
+    rust_assert (!name.empty ());
+
+    crate_name = std::move (name);
+  }
+
+  const std::string &get_crate_name () const
+  {
+    rust_assert (!crate_name.empty ());
+    return crate_name;
+  }
+
+  void set_edition (int raw_edition)
+  {
+    edition = static_cast<Edition> (raw_edition);
+  }
+
+  const Edition &get_edition () { return edition; }
+
+  void set_metadata_output (const std::string &path)
+  {
+    metadata_output_path = path;
+  }
+
+  const std::string &get_metadata_output () const
+  {
+    return metadata_output_path;
+  }
+
+  bool metadata_output_path_set () const
+  {
+    return !metadata_output_path.empty ();
+  }
+};
+
+/* Defines a compiler session. This is for a single compiler invocation, so
+ * potentially includes parsing multiple crates. */
+struct Session
+{
+  CompileOptions options;
+  /* This should really be in a per-crate storage area but it is wiped with
+   * every file so eh. */
+  std::string injected_crate_name;
+
+  /* extra files get included during late stages of compilation (e.g. macro
+   * expansion) */
+  std::vector<std::string> extra_files;
+
+  // backend wrapper to GCC GENERIC
+  Backend *backend;
+
+  // backend linemap
+  Linemap *linemap;
+
+  // mappings
+  Analysis::Mappings *mappings;
+
+public:
+  /* Get a reference to the static session instance */
+  static Session &get_instance ();
+
+  Session () = default;
+  ~Session () = default;
+
+  /* This initializes the compiler session. Corresponds to langhook
+   * grs_langhook_init(). Note that this is called after option handling. */
+  void init ();
+
+  // delete those constructors so we don't access the singleton in any
+  // other way than via `get_instance()`
+  Session (Session const &) = delete;
+  void operator= (Session const &) = delete;
+
+  bool handle_option (enum opt_code code, const char *arg, HOST_WIDE_INT value,
+		      int kind, location_t loc,
+		      const struct cl_option_handlers *handlers);
+  void handle_input_files (int num_files, const char **files);
+  void init_options ();
+  void handle_crate_name (const AST::Crate &parsed_crate);
+
+  /* This function saves the filename data into the session manager using the
+   * `move` semantics, and returns a C-style string referencing the input
+   * std::string */
+  inline const char *include_extra_file (std::string filename)
+  {
+    extra_files.push_back (std::move (filename));
+    return extra_files.back ().c_str ();
+  }
+
+  NodeId load_extern_crate (const std::string &crate_name, Location locus);
+
+private:
+  void compile_crate (const char *filename);
+  bool enable_dump (std::string arg);
+
+  void dump_lex (Parser<Lexer> &parser) const;
+  void dump_ast (Parser<Lexer> &parser, AST::Crate &crate) const;
+  void dump_ast_pretty (AST::Crate &crate) const;
+  void dump_ast_expanded (Parser<Lexer> &parser, AST::Crate &crate) const;
+  void dump_hir (HIR::Crate &crate) const;
+  void dump_hir_pretty (HIR::Crate &crate) const;
+  void dump_type_resolution (HIR::Crate &crate) const;
+
+  // pipeline stages - TODO maybe move?
+  /* Register plugins pipeline stage. TODO maybe move to another object?
+   * Currently dummy stage. In future will handle attribute injection
+   * (top-level inner attribute creation from command line arguments), setting
+   * options maybe, registering lints maybe, loading plugins maybe. */
+  void register_plugins (AST::Crate &crate);
+
+  /* Injection pipeline stage. TODO maybe move to another object? Maybe have
+   * some lint checks (in future, obviously), register builtin macros, crate
+   * injection. */
+  void injection (AST::Crate &crate);
+
+  /* Expansion pipeline stage. TODO maybe move to another object? Expands all
+   * macros, maybe build test harness in future, AST validation, maybe create
+   * macro crate (if not rustdoc).*/
+  void expansion (AST::Crate &crate);
+
+  // handle cfg_option
+  bool handle_cfg_option (std::string &data);
+};
+
+} // namespace Rust
+
+#if CHECKING_P
+namespace selftest {
+extern void
+rust_crate_name_validation_test (void);
+}
+#endif // CHECKING_P
+
+#endif
-- 
2.25.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

* [PATCH Rust front-end v2 37/37] gccrs: Add README, CONTRIBUTING and compiler logo
  2022-08-24 11:59 Rust frontend patches v2 herron.philip
                   ` (35 preceding siblings ...)
  2022-08-24 11:59 ` [PATCH Rust front-end v2 36/37] gccrs: compiler proper interface kicks off the pipeline herron.philip
@ 2022-08-24 11:59 ` herron.philip
  2022-08-25  9:46 ` Rust frontend patches v2 Philip Herron
  37 siblings, 0 replies; 59+ messages in thread
From: herron.philip @ 2022-08-24 11:59 UTC (permalink / raw)
  To: gcc-patches; +Cc: gcc-rust, Philip Herron

From: Philip Herron <philip.herron@embecosm.com>

We still need to write out documentation section but these README's will
help in the mean time.
---
 gcc/rust/CONTRIBUTING.md | 130 +++++++++++++++++++
 gcc/rust/README.md       | 264 +++++++++++++++++++++++++++++++++++++++
 gcc/rust/logo.png        | Bin 0 -> 70864 bytes
 3 files changed, 394 insertions(+)
 create mode 100644 gcc/rust/CONTRIBUTING.md
 create mode 100644 gcc/rust/README.md
 create mode 100644 gcc/rust/logo.png

diff --git a/gcc/rust/CONTRIBUTING.md b/gcc/rust/CONTRIBUTING.md
new file mode 100644
index 00000000000..20e499c29e1
--- /dev/null
+++ b/gcc/rust/CONTRIBUTING.md
@@ -0,0 +1,130 @@
+## How to contribute to GCC Rust
+
+#### **Did you find a bug?**
+
+* **Ensure the bug was not already reported** by searching on GitHub under [Issues](https://github.com/Rust-GCC/gccrs/issues).
+
+* If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/Rust-GCC/gccrs/issues/new). 
+  Be sure to include a **title and clear description**, as much relevant information as possible, and a **code sample** 
+  or an **executable test case** demonstrating the expected behavior that is not occurring.
+
+#### **Do you want to submit a patch?**
+
+* Open a new GitHub pull request with the patch.
+
+* Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable.
+
+* Before submitting, GCC development requires copyright assignment or the Developer's Certificate of Origin sign-off.
+   Please see the [Contributing to GCC](https://gcc.gnu.org/contribute.html) guide or [Developer's Certificate of Origin (DCO) Sign-off](https://gcc.gnu.org/dco.html) guide.
+
+* Patches sent to the [`gcc-rust` mailing list](https://gcc.gnu.org/mailman/listinfo/gcc-rust) are likewise welcome.
+These will be imported into a GitHub PR to follow the normal review process, 
+and the link to the GitHub PR sent to the submitter.
+
+#### **Do you intend to add a new feature or change an existing one?**
+
+* Suggest your change in the [Zulip](https://gcc-rust.zulipchat.com/) and start writing code.
+
+* Do not open an issue on GitHub until you have collected positive feedback about the change. 
+  GitHub issues are primarily intended for bug reports and fixes.
+
+#### **Do you have questions about the source code?**
+
+* Ask any question about how to use GCCRS in [Zulip](https://gcc-rust.zulipchat.com/).
+
+### **PR Policy**
+
+* The PR policy: Everything has to go through a PR
+  - An exception to this rule will be the merge commits of updating the repo against upstream GCC
+
+* Reviewers/Maintainers of the project (aka people who have bors rights) should be pinged for reviews/questions.
+
+* A PR can have one or several commits (split should have a technical/logical reason, ie. no fixup-ish commit)
+
+* Avoid PR's with merge commit unless there's a good reason
+
+* Where possible please add test cases to `gcc/testsuite/rust/` for all PRs. 
+  Some issues may not be testable via dejagnu/automation such as debug dump changes.
+
+* Follow the [GCC coding style](https://gcc.gnu.org/codingconventions.html) (see `clang-format` below).
+
+* PRs won't be merged until the build and tests pass.
+
+* Please take the time to create good git commit messages. 
+  See the existing format of them in the git log or refer to something like: https://chris.beams.io/posts/git-commit/
+
+#### Running `clang-format` locally
+
+* on all files using python scripts
+... corresponding to what the _Clang Format Lint_ (`.github/workflows/clang-format.yml`) 
+is doing, with `clang-format-10` being available locally, and avoiding the Docker overhead.
+
+```shell
+$ wget 'https://github.com/DoozyX/clang-format-lint-action/raw/v0.11/run-clang-format.py'
+$ cp contrib/clang-format .clang-format
+$ python3 run-clang-format.py --clang-format-executable clang-format-10 --recursive --extensions h,cc gcc/rust/
+```
+
+* on a given patch using python scripts
+See the [clang-format documentation](https://clang.llvm.org/docs/ClangFormat.html#script-for-patch-reformatting) :
+
+    $ git diff -U0 --no-color HEAD^ | clang-format-diff.py -i -p1
+
+* using `git` interface
+
+At least on Debian and its derivative, each `clang-format` packages also comes
+with `git-clang-format` command that can be used easily. It applies on staged
+changes, and any modification can be seen as unstaged changes:
+
+```diff
+$ git diff --cached
+diff --git a/gcc/rust/rust-abi.h b/gcc/rust/rust-abi.h
+index bd3043295ce..9559374ce60 100644
+--- a/gcc/rust/rust-abi.h
++++ b/gcc/rust/rust-abi.h
+@@ -22,10 +22,10 @@ namespace Rust {
+ enum ABI
+ {
+   UNKNOWN,
+-  RUST,
++     RUST,
+   INTRINSIC,
+   C,
+-  CDECL,
++     CDECL,
+   STDCALL,
+   FASTCALL,
+ };
+ 
+gccrs/gcc/rust on  dkm/clang_format [$!+?]
+❯ git clang-format
+changed files:
+    gcc/rust/rust-abi.h
+ 
+gccrs/gcc/rust on  dkm/clang_format [$!+?]
+$ git diff rust-abi.h
+diff --git a/gcc/rust/rust-abi.h b/gcc/rust/rust-abi.h
+index 9559374ce60..bd3043295ce 100644
+--- a/gcc/rust/rust-abi.h
++++ b/gcc/rust/rust-abi.h
+@@ -22,10 +22,10 @@ namespace Rust {
+ enum ABI
+ {
+   UNKNOWN,
+-     RUST,
++  RUST,
+   INTRINSIC,
+   C,
+-     CDECL,
++  CDECL,
+   STDCALL,
+   FASTCALL,
+ };
+```
+
+Also note that you can use a given version of `clang-format` by using `git clang-format-10` if you have 
+installed that particular version.
+
+Thanks! :heart: :heart: :heart:
+
+GCCRS Team
diff --git a/gcc/rust/README.md b/gcc/rust/README.md
new file mode 100644
index 00000000000..fe38402f27a
--- /dev/null
+++ b/gcc/rust/README.md
@@ -0,0 +1,264 @@
+![C/C++ CI](https://github.com/Rust-GCC/gccrs/workflows/C/C++%20CI/badge.svg)
+[![GCC Bootstrap Build](https://github.com/Rust-GCC/gccrs/actions/workflows/bootstrap.yml/badge.svg)](https://github.com/Rust-GCC/gccrs/actions/workflows/bootstrap.yml)
+[![Build Docker image](https://github.com/Rust-GCC/gccrs/actions/workflows/docker.yml/badge.svg)](https://github.com/Rust-GCC/gccrs/actions/workflows/docker.yml)
+![Docker Pulls](https://img.shields.io/docker/pulls/philberty/gccrs)
+[![project chat](https://img.shields.io/badge/zulip-join_chat-brightgreen.svg)](https://gcc-rust.zulipchat.com/)
+[![Bors enabled](https://bors.tech/images/badge_small.svg)](https://app.bors.tech/repositories/32890)
+# GCC Rust
+![GCC Rust](logo.png?raw=true "GCC rust Logo")
+
+Please note, the compiler is in a very early stage and not usable yet for compiling real Rust programs.
+
+gccrs is a full alternative implementation of the Rust language ontop of GCC with the goal
+to become fully upstream with the GNU toolchain.
+
+The origin of this project was a community effort several years ago where Rust was still at version 0.9;
+the language was subject to so much change that it became difficult for a community effort to play catch up.
+Now that the language is stable, it is an excellent time to create alternative compilers. The developers of
+the project are keen “Rustaceans” with a desire to give back to the Rust community and to learn what GCC is capable
+of when it comes to a modern language.
+
+## Build Farm Status
+
+- [Debian i386](https://builder.sourceware.org/buildbot/#/builders/gccrust-debian-i386) [![Debian i386](https://builder.sourceware.org/buildbot/badges/gccrust-debian-i386.svg)](https://builder.sourceware.org/buildbot/#/builders/gccrust-debian-i386)
+- [Debian ppc64](https://builder.sourceware.org/buildbot/#/builders/gccrust-debian-ppc64) [![Debian ppc64](https://builder.sourceware.org/buildbot/badges/gccrust-debian-ppc64.svg)](https://builder.sourceware.org/buildbot/#/builders/gccrust-debian-ppc64)
+- [Debian testing-x86_64](https://builder.sourceware.org/buildbot/#/builders/146) [![Debian testing-x86_64](https://builder.sourceware.org/buildbot/badges/gccrust-debian-testing-x86_64.svg)](https://builder.sourceware.org/buildbot/#/builders/146)
+- [Fedora arm64](https://builder.sourceware.org/buildbot/#/builders/179) [![Fedora arm64](https://builder.sourceware.org/buildbot/badges/gccrust-fedora-arm64.svg)](https://builder.sourceware.org/buildbot/#/builders/179)
+- [Fedora ppc64le](https://builder.sourceware.org/buildbot/#/builders/gccrust-fedora-ppc64le) [![Fedora ppc64le](https://builder.sourceware.org/buildbot/badges/gccrust-fedora-ppc64le.svg)](https://builder.sourceware.org/buildbot/#/builders/gccrust-fedora-ppc64le)
+- [Fedora s390x](https://builder.sourceware.org/buildbot/#/builders/gccrust-fedora-s390x) [![Fedora s390x](https://builder.sourceware.org/buildbot/badges/gccrust-fedora-s390x.svg)](https://builder.sourceware.org/buildbot/#/builders/gccrust-fedora-s390x)
+- [Fedora X86_64](https://builder.sourceware.org/buildbot/#/builders/gccrust-fedora-x86_64) [![Fedora X86-64](https://builder.sourceware.org/buildbot/badges/gccrust-fedora-x86_64.svg)](https://builder.sourceware.org/buildbot/#/builders/gccrust-fedora-x86_64)
+- [OpenSUSE Leap X86_64](https://builder.sourceware.org/buildbot/#/builders/104) [![OpenSUSE Leap X86_64](https://builder.sourceware.org/buildbot/badges/gccrust-opensuseleap-x86_64.svg)](https://builder.sourceware.org/buildbot/#/builders/104)
+- [OpenSUSE tw X86_64](https://builder.sourceware.org/buildbot/#/builders/103) [![OpenSUSE tw X86_64](https://builder.sourceware.org/buildbot/badges/gccrust-opensusetw-x86_64.svg)](https://builder.sourceware.org/buildbot/#/builders/103)
+- [Rawhide X86_64](https://builder.sourceware.org/buildbot/#/builders/132) [![Rawhide X86_64](https://builder.sourceware.org/buildbot/badges/gccrust-rawhide-x86_64.svg)](https://builder.sourceware.org/buildbot/#/builders/132)
+
+## FAQ
+
+Please find the answers to frequently asked questions over on: https://github.com/Rust-GCC/gccrs/wiki/Frequently-Asked-Questions
+
+## Development Environment
+
+### Building
+
+Fetch dependencies for Ubuntu:
+
+```bash
+$ apt install build-essential libgmp3-dev libmpfr-dev libmpc-dev flex bison autogen gcc-multilib dejagnu
+```
+
+Clone the repository
+
+```bash
+$ git clone https://github.com/Rust-GCC/gccrs
+```
+
+#### Linux
+
+It is important to remember that GNU toolchain projects are designed to be built outside of their source directory
+which is why a build directory is created.
+
+```bash
+$ mkdir gccrs-build
+$ cd gccrs-build
+$ ../gccrs/configure --prefix=$HOME/gccrs-install --disable-bootstrap --enable-multilib --enable-languages=rust
+$ make
+```
+
+#### MacOS
+
+The path of header dir and sysroot should be specified when you configure the project.
+```bash
+$ mkdir mac-build
+$ cd mac-build
+$ ../gccrs/configure --prefix=$HOME/gccrs-install --disable-bootstrap --enable-multilib --enable-languages=rust --with-native-system-header-dir=/usr/include --with-sysroot=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk 
+$ make
+
+```
+
+#### Running GCC Rust
+
+Running the compiler itself without make install we can simply invoke the compiler proper:
+
+```bash
+$ ./gcc/rust1 test.rs -frust-debug -frust-dump-parse -Warray-bounds -dumpbase test.rs -mtune=generic -march=x86-64 -O0 -version -fdump-tree-gimple -o test.s -L/lib/x86_64-linux-gnu -L/lib/../lib64 -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib64
+```
+
+To invoke the compiler driver (gccrs) we need to:
+
+```bash
+$ make install
+```
+
+Then invoke the compiler from the installation directory:
+
+```bash
+$ $HOME/gccrs-install/gccrs -g -O2 -c test.rs -o test.o
+$ $HOME/gccrs-install/gccrs -o test test.o
+```
+
+You can also setup your shell to automatically find the installed compiler. For example for `bash`, 
+add the following in your `$HOME/.bashrc`:
+
+```bash
+export PATH=$HOME/gccrs-install/bin:$PATH
+
+```
+
+## Testsuite
+
+Invoke the full testsuite from the build directory (`gccrs-build` in the previous commands):
+
+```bash
+$ make check-rust
+```
+
+Invoke a subset of the testsuite. For example, to only run tests that are currently known/expected to fail:
+
+```bash
+$ make check-rust RUNTESTFLAGS="xfail.exp"
+```
+There are the following sets of tests:
+- `compile.exp` : compilation tests
+- `execute.exp` : execution tests
+- `xfail.exp` : tests that are currently known/expected to fail
+
+Invoke only a specific test :
+
+```bash
+$ make check-rust RUNTESTFLAGS="--all compile.exp=continue1.rs"
+```
+
+Logs (with corresponding commands) can be found in : `gccrs-build/gcc/testsuite/rust/rust.log`.
+
+See [GCC Testing documentation](https://gcc.gnu.org/install/test.html) for more details.
+
+Test cases are located within [`gcc/testsuite/rust/`](gcc/testsuite/rust/).
+Please contribute your specific
+test cases referencing any issues on Github.
+
+## Debugging
+
+### Enabling internal checks
+
+GCC has several internal checks that can be enabled during configuration. In the case of `gccrs`, 
+you can enable the following:
+```bash
+$ ../gccrs/configure --prefix=$HOME/gccrs-install --disable-bootstrap --enable-multilib --enable-languages=rust --enable-checking=gimple,tree,types
+```
+
+### GDB
+You can directly invoke `gdb` on the `rust1` compiler process (you can find the
+exact command adding `--verbose` to your `gccrs` invocation):
+```bash
+$ gccrs test.rs -O0 -S -o arithmetic_expressions1.s --verbose
+...
+ /some/path/../../rust1 test.rs -quiet -dumpbase arithmetic_expressions1.rs -dumpbase-ext .rs
+ -mtune=generic -march=x86-64 -O0 -w -version -fdiagnostics-color=never -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-urls=never -fdiagnostics-path-format=separate-events -o test.s -L/lib/x86_64-linux-gnu -L/lib/../lib64 -L/usr/lib/x86_64-linux-gnu
+...
+$ gdb --args  /some/path/../../rust1 test.rs -quiet -dumpbase arithmetic_expressions1.rs -dumpbase-ext .rs
+ -mtune=generic -march=x86-64 -O0 -w -version -fdiagnostics-color=never -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-urls=never -fdiagnostics-path-format=separate-events -o test.s -L/lib/x86_64-linux-gnu -L/lib/../lib64 -L/usr/lib/x86_64-linux-gnu
+```
+
+Or simply add the `-wrapper gdb,--args` option. 
+This will call each subcommand in `gdb` and you simply have to break/debug in `rust1`:
+```bash
+$ gccrs test.rs -O0 -S -o arithmetic_expressions1.s -wrapper gdb,--args
+```
+
+## Docker image
+
+There is a docker image hosted over on: 
+
+https://hub.docker.com/repository/docker/philberty/gccrs
+
+```bash
+$ docker pull philberty/gccrs
+```
+
+Or you can build your own image:
+
+```bash
+$ docker build . -t gccrs-dev
+```
+If you want to build an object file: 
+
+```bash
+$ docker run --rm -v "$PWD":/usr/src/myapp -w /usr/src/myapp \
+    gccrs-dev:latest gccrs -g -O2 -c \
+    gcc/testsuite/rust/compile/torture/type_infer1.rs -o type_infer1.o
+```
+
+If you want to build an executable file:
+```bash
+$ docker run --rm -v "$PWD":/usr/src/myapp -w /usr/src/myapp \
+    gccrs-dev:latest gccrs -g -O2 \
+    gcc/testsuite/rust/compile/torture/type_infer1.rs -o type_infer1
+```
+
+To emit assembly :
+```bash
+$ docker run --rm -v "$PWD":/usr/src/myapp -w /usr/src/myapp \
+    gccrs-dev:latest gccrs -g -O2 \
+    gcc/testsuite/rust/compile/torture/type_infer1.rs -S -o type_infer1.s 
+```
+
+To emit Rust front end debug output, you may add options like `-frust-debug`, `-frust-dump-all`.
+
+
+## Contributing
+
+If you want to contribute to GCC Rust, you can find more information in [CONTRIBUTING.md](https://github.com/Rust-GCC/gccrs/blob/master/CONTRIBUTING.md).
+
+Please be aware this project is designed to be pushed upstream to GCC when we reach some milestones, 
+and this means we require copyright assignment or the Developer's Certificate of Origin sign-off. 
+Please see the [Contributing to GCC](https://gcc.gnu.org/contribute.html) guide or [Developer's Certificate of Origin (DCO) Sign-off](https://gcc.gnu.org/dco.html) guide.
+
+Not all contributions must be code; we would love to see new test cases or bugs and issues to be reported. 
+Feel free to add any comments on open PRs
+
+
+## Continuous Integration
+
+When submitting (or updating) a [GitHub Pull Request](https://github.com/Rust-GCC/gccrs/pull/),
+several automated checks are run.
+Generally, a "green status" is necessary before merge.
+
+
+### Compiler Diagnostics
+
+That is, here, diagnostics emitted by the "initial" compiler used to build GCC/Rust.
+
+If building a native toolchain,
+GCC by default does a 3-stage bootstrap build (<https://gcc.gnu.org/install/configure.html>).
+In addition to making sure that GCC is able to reproduce itself bit-by-bit,
+this also means that stages 2+ are built with `-Werror`
+(turning most _warning_ into _error_ diagnostics; see `--enable-werror`,
+possibly enabled by default).
+This helps to catch a good number of bugs, because it enforces that GCC compiles without compiler diagnostics;
+it's a requirement for upstream patch submission (<https://gcc.gnu.org/contribute.html#testing>).
+
+GCC generally is only expected to be "warning-clean" without `--disable-bootstrap`
+(that is, default `--enable-bootstrap` for a native build),
+and not for the initial stage where it's using the "initial" compiler -- otherwise
+we're at the mercy of whatever "initial" compiler we're using.
+Doing a `--disable-bootstrap` build is much faster, of course, so we're often doing that:
+for example, per the instructions above, or in the standard CI.
+With that, we're missing out on the aspect that _enforces that GCC compiles without compiler diagnostics_.
+
+To encounter that, the default CI has a [_check for new warnings_ step](https://github.com/Rust-GCC/gccrs/pull/1026)
+that verifies in the CI `--disable-bootstrap` build configuration that no new warnings are introduced.
+If that step fails, it usually points out a new _warning_ you've introduced erroneously, and should address.
+Occasionally it means that simply the `.github/bors_log_expected_warnings` file needs to be updated,
+for example if due to any kind of "environmental changes" (for example, CI "initial" compiler changes).
+Unless diligently reproducing the CI configuration (in particular "initial" compiler, GCC version),
+it's not really feasible to reproduce this check locally.
+If in doubt, do a local `--enable-bootstrap` build, or submit your changes, and wait for the CI system's results.
+
+
+## Community
+
+We can be found on all usual Rust channels such as Zulip, but we also have our own channels:
+
+* GCC Rust Zulip: https://gcc-rust.zulipchat.com/
+* Twitter: https://twitter.com/gcc_rust
+* GCC Mailing List: https://gcc.gnu.org/mailman/listinfo/gcc-rust
+* irc: irc.oftc.net - gccrust
diff --git a/gcc/rust/logo.png b/gcc/rust/logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..e5b22ad646f87e9ad00427f891d2f93baaf83520
GIT binary patch
literal 70864
zcmeFYWmH|w(k_a-yR(o4cXxLU5CQ~WXmEGe;O_1T?j9hxy9W2*?s8U=cklh~d%pXf
z`;BqWzYPP%nmwzZu70|zx~t|~pOqD*kP!$Fz`($eWu(Pbz`!8)!N9<;;9x*471Xc{
zU|_h9?rIv2Dh4h9TYDQ5b4z1@qnoWUz}VH?1Psh|sXWcxo}v{!@O2AI5MmC(mZ%K<
zn_u)PQ+fh>erjV+ZDEY`yHRVf#6-ljlc&A4`RDLT!xe4D`g60a%4LV>0IJcBZ@ZU#
zcTZ2=-xM!@J|8Gmwx+KsQAG1@JEfeSd)0Iw@}r)1JU)_dY~4j;dhx>tnC={s0^9Vm
zg*pV+s3MLZKd(|qZ#Wrutl79`Q}^D6UL2lYlf+)D`=l-Dp3?cqS9?8gtL0-Oa$NFB
z>6JQVHSRkY3pB#Ge>fWdJY*pvq<44sxHU15vMEF6=G~8_moHzL-E|nd#`31$kL=Bh
z^k7!<Q0-Zk3M-W4(i*P2();lHttHR$Nm9?}O!Ibwi_Z1hPuY7f!O^JFULjJ%rxa6%
z*Oi!yds|Az&#nRqHkHNW!~NNkGfz~^ThSD0Tlr_JK9vXUtt%c`0Xig$DYflX*DSqz
z_^^uQbC;Z3trx1Uy4j~=E|=X*jP;p%jx*cQB;*|5$U=xEn>P5P>F9$~wmv8J<9(1H
zw}{lbYKr@mb$$FjlH<BgHUA2+j8Zo$8wfw{^ytuxP0+OFM*&<n`aZGO_;?=uy7QQf
zxl)=1FA{#w2(}d_%HFNKbCsS?Ez)$#QJ8n4xp~$+A>{p~>N7%;X(}Nm5ok?V<nF$!
zt7OhT2b~`2NuW4&D0O1k9e>vs&4XAKrS0;sWSK+L=$G_@p@^pG$6rRsO=!Y59;qYj
z%|&eDJpHo4!<lsFj(<~t7L-+%P*f~ZZO_<Qyt~^uLH%W0B`i@(>!+~>t5b?5$K+Yb
zmtC!|vsxdIo$6-n=|;7z%C9-yj|N4i3#X&ouUGrm^8(mVR|U8YBk)br+>*>|$2cE}
z5bw&`hHBn__~;c$>|&oTx!Q1O-Fj;G9bA?w+x}=~T2W{VqCVaF+s;yX<BE5`>FnzJ
z=jKmqbhmr&5l_GN7Ap^)mwKy5CR;pnOQ2<9%|?Xv5|y~VhX!+08}&rwk$P`E#euSt
zODAiTAkn)hf}QVwAE;q{SGMwD$NM8!@(-b}AD?u2il?51X!q1wY!V;qtvkLfCE=A7
z8!F+pn7;GMd*)uRL;n#I;Vo$0>dlsIcSviHA)sW+vX@!vQ2&j)x{zp@@mSGE$;u$x
zEx5y5`&y~_TXLU$jV{q>+M3tUn6~~k9$sQltqP%qZT;qjv0;9)vfNSxtrnpR(r&ew
z1iR8Q1J0buxC(C>{=*U<mEu%ktX1S<w?c&^SORa-TY5RK<!`679)aI4u<#nFDzGI}
z%OOLdGx7!@0v8p(qE^k1qNP{HOMIeXD_g?Tg@my}X?^o9l6wpvGYAi0vo?4y#j#s&
zK?JltcQ>hBZ>c;cU?!MWzBw<eecg!NvE-_N@w6&)P@jXRb{lb})00g%rE+N8U4a~k
z^2pZbxT?_<9YS7Kg1^6E<*K?60MFHsi~xqHn4a)0r5283Tk-`*ls(F16IDpK86Nq%
zhB#O=I>2;_h4*q$&7Xyk&1lxV)trX@VtT?^Su0%6%apCc(?Fk4pTN?sxW!7nL~rL<
z?weI%9{UwHX@1X~Z#2k#5v7#JbDjWB2L(uklUn<}(?6=ud73_7ohU}#vhF4Q&6BI6
z&sFpu3$f$`#25wZfyv$s#3#?=E7hZ!Y>7P6XPJtXwxUd@N+hw2+Po#VsHE@}wA=MH
zgD#>O`~ij*L3=pe+g+A}Ym4;p#%6?B*SAK8lGwXhh{TCsFauVRz0}eMlhYNHM@%bG
zR_Yrd&OSsZA=fskSPywr4!59;eTvr&#2X*rV^sen0ZCAdcQA)TRc=fiO3j4?2^4|F
zRXF;tohPNijV#;>YYD4Q_5-r`3OX69++T=qH{OE2UXFJohk+|a(HGv-8MZ)ssg)2&
zZT6&#>Cd(bRq5`Q_&zt4bsv4d%JEagPHc;%868PJf)^h*Cb`XoPw)aQ$4?i0vtidw
z{Cej69$pjPdR5qT^$$hq(@<x~52&^ZfuEkk0_IQ=i*oCE2!y8|`2%Z|5?@Rwui<TH
z1<xp!gY&f>mA8L-6v4O4<+jxzwj+uZ!Vu?DJgtm>m6pl#QqQG|$aas%+g}DpR#jWT
zZEXQh=~f~%p&<4ZH9&c<-#k9Dxe(LZW&hDf!7nq>A8kIB8GSDHjSw<9go>hN0LB7K
z6Gtxc$kQgVX;{NnXz6yHd%IVG6YT;%iGLZa!?a!k`YBR=>x?|kXGVEb#$n$Emg@$Z
zpE<`7s>FSY*~`*O5wVmi+9*Vh`hyYujL^c?j4=9u?DSZ0ZH9SyhwOkR&h(zQUwzAp
z@HIDsOuAOE(ccW^5;#yj`5sc#=@>K;@?2BXQ52_2f>rOi1{0q|P490yjbr_c2v;GO
zYGAUFk%rH)&tnJ+io<EL3?B~Ff?H(Cfy>RCDk}0vq=S^Miq0&Ny+>JUY=KpDspE2s
zXvF6B*R4<nB<9@mrK$ex6m=Q>goq<ed?S3bpVs0LSdMoG|GfngDT8>kJ^ss&=_q#E
zI#_!Q0w+H}Z!s5MquwD*DmsL)A8ltDcbH+Tn{vJDejyK~PBECbN^4)&MGs`(gJBX0
z@wXkvbB{8MVg$bEw9_}~bw7EZWzv`8rT6#TqszA@KMq(Y4tCOzdEp_;7`?&gyqV`p
z_;LY>NvSPn%Z*v^X7CN*;Q)&#(Lxz<B#(3<Fs<Uo9;cP~k(jMjDCd(8p{K{xF4pI-
zIW%~7BdCT+1F|fR^6N()<Iqwh4ypYXM6#Gr3y^!P2%Uq<YWnxnn-k{DiRa<a0(cw*
z1Nr$^2)snDDlA7DC}(4VY-;Z><O$zst2KrXUg}(u@wHfrpu(Ag(NrJ1j%_${e*!La
ztzgZ6b&g9LY2uE=WGNt07V*r(g=nka7Y<bDQ&fj<#Pn^<{&Xb&RACLHJ*yCmZA|GX
z%Fzi$&cB^byA^zxGIjvip3&giN)2Ni=qd5AhH46+NQ(B=tellx(|uyU@84=6(AI;E
z?}4wi7A8Z!ixrv-iP~AopTub*9Oz)cvnN{|fs{k2xhld--PlB<EhvP2az(@f6SHip
zgR#Pa3I5DBRqDhWuA5JP=49hR%su{<RwQ31!3f=RQVv0U$8udHl^W@5S%`<(MGSge
zfKd%08WK7m-7CpxsYk#v_9eo%(%^vEE&)figdG!R4HE+9X{axZbx<P!u?e{8gU^!j
z!C=lBVS8Y$gqXAxeqB9bm?U;A1Zmruotcb4?e#1u!FNkAL+c2hZ^zK^-)d-F+dn5;
z+P%}8Sq92;Sjn$IF;e18GPE;A2R2}xW>ush3oUF`IzJanF~g5LE|H)vspedD<JL^^
zirGW8SILSLOv|JWuviQ?aejqvRDdTK1vixGjnN%N>;sTekoz_v_gqapx4|!nuxoln
z4UzGoH9BLbVj)F}cQ#xp=hrOiVMVbk_?tPx_`x;gu*6cRxXKiS*&>CEZI#K8%&a@c
zjjU(nD2`>LbZ!~8-FiCmZzpEM_AGhv-@k+^PPnt9$dJsV%5Qx42WllAC`Y~96IiN8
zk51gmstO@}^XU^^MP^aEiixUGD-*c=G{XeGCQL;oWVkpav?D?vj#?+##epo**~{(|
zH5LxlG)uu-3`{8DimoyKmpI1BQiH<M>8-J9TsW(-<BY}j?!KJVGTOs5O2X>8$jTo^
zT@wK448WTyjPG)kG0VkBgxm6vzGpBW$$fh0?NxiTHpk7YMw%EfhtC}`!xJarFUxrA
zP}iKcW6ql1e!-^;l`ta!1V&W|C<lAsX}NE^dCcNim7|SG3>yRxn-dItyvb}B0Ao@R
z<uBJc>xA0|*qg(1aiZD?AHB)B?k+Kduz%lFU&~7DP>&-`7XYK%LvMLU$*BP0zk#)W
zBswOw56_ysW73pxS4cI1f%HPvyb&UGdoAkwE?1VeXC1RI^U4WN`)G58w&-IGipFsj
z`@2_!7;K}X0~iGIHAtfM3sbJ#fVVhN1F*|jscu45h<CBRt;7R}7y-OJ8SX6~JU&2Y
zM?QBtSneohVdXP?i~;5jBlVN8=ESZo<K7&*iWPRD^SXf*ap|y1HfuqO9+)J>xNm9r
zndJ2|zZ}o1yfD%1ic&<^s2vp>gonD&ecz-{4pwMoRYf8-K$i=oNJHb^=GG%SLsR)o
zR4!k5)KC>gtojkyxe`OBnL<5(UK4H>BDtcQiPvX51Hf^dnM&tX_KgTzkV^+0Iegs>
zrf=I~$9T7S5Mso4Q`&<~fHOZQV5POecttn(si~$-=pEnFcU#A#;I?tYj&Y2XF++`Z
z10~`ynTPO;Bt!_-FOYz-8@yYzOoE?oWW+`Z1S6XIdR>Gv`9tBv>tPxkW4CcIoIc82
ze3AznX8iyqCGta*SRNf0K9^Y4Z~JjsR0Nr29Uf>zPkkJ8`!OyN^Dr=A3H^C1)yf0j
zD1?arEwt>Mw-b5vaE(Y*nGmKJndV~{PL=$Vj2=08xAPYB4C>l+2nF;Bj+xJ^rTwE+
z5EFZ~$igT#0jfLU4UQf_6>?zUJY$l8B3>UYC<M4T51yx)s`3@gO*4y9I1f8Dut|&q
z#*!n29wt+Dt%%?21HE5_lYYoYogiE?-hCZ6U+yGwb1TZY2&zJP_nnx&eq~=7j>I+^
z7)3%<SU${tLSDy~aMvbZ1pqVEVux^FF#LxZcK|vbA;10h3bbUNL~rjM?>3)siw8;W
zTmzr7$cM_|ubnh^qLk1MV!~uw7<5b~#MN&%V@wL5V4xkpi$&;I1P5u?@-We{K~DRE
zb201KqjUyRiClHxDbN6EN@UNHMU~(LoG3!TZK)4j_efhw{E#+mD{3T<Fsp+NZ1RH8
zmtoR@9rh!94e)QebwaIl)A{Q}iky{+8bT;EW`bTtE52JL`1|DtB<rrKWJk#zp-U#g
zLuhSlW!z5%6ZL$_;mRxsVb8NhLSX&y7-EYP<8|Jk1Zf(PET55*VeRK*y(tVJvuX~k
z^_-2u;g|lZ2)W@ee=M`+Eh&6Usp!uT9^pTvDH;htmN$HOcNn&0j_NeJC7sG{AGFWp
z+XcX0@W+ur3Jpdl4T!N({9tKIoPpgwGZ;(BO^s40y2Iy7e}}ytx5bR&(YeZllkIm{
z<Zt*=LX22u4lYZ7N=I>j9qF}Fd4(v>ywwLCBb_8WCO_6_bf1hexeG=U-wdfZwf2>-
zYDIH$F8&T}KDe0*m%J1P$nsg+x{&2reLwIA${4In@*||622;8pyQJkR3Twfd=G$dx
z7>r>omWicWaU+1Ida8MY=^D^{Z5M;)^TQn+cho-nS!%m8F6H3JV1-*V`qM-6-c>+I
zmmSN?VyJNR<2=d`$wJ2)EY;|UCI8mzly@_Eb91Frq=V$)s9xQ%*3&1{7pd&q43uHY
z`^^g83dp#AEocsSSCpdqHN)a)NTHNwekewnxRX-p6zl-mkg3oI)FvZW>5nJ9VxD;7
z?ll-I7r<vNf{d@9AQ2cY+KACvG2ziAG$sc=+j4OultmWc0|TQEHSqK4E?Ii7--sr(
zZr~@$AVxbP?t+`b*KrHq%I)I>*EKeLWhM?RbrmNpb>?SmNy-|{Dxq<&oD$f@v}Q=~
zTXNUbI)IT%w}eb6yi3z>6%nNgqxOT+oW*MSx$wXb+k$1xR{ZHhpO}j?w&**=IEv3j
z4;doVI?IqrZr|S_vh#fvn!Sd6r;z>y84H6;(YMM^R9~SUmVmmJh=)ut$uxT#9cG!a
zXOE_m&f+KKPg=ce7&2##ez+}Yzm4N8-FohGVt5Iv9k=blA(D_PF{5L4mei3+It09R
zV|NnTF?>6dS*B_nRXI=dr5o7vrBieJGkNqT_L_Tc<>zg^l}VN!Jd+^8rR&ZI>9^$X
zvIuGLGP<x0<(CZi%O;LSlCE>Dl*R8cMH}i5>STf|C}5L^@$+Ku^?0=t%(ahkHtTP@
z>adP;sT>ohB2Clr`n2z?l~ah{-T3KtNsP2_2(pS4XaurrQ=FR!!^E`bo#x!NNCZ;?
z)LV8GRs5~(1foj0%yQ%}g0*p$YR^7_Dc>3a(#XO?#2dwjOts1RMe*><^3qa9+u=J`
zu7~JVEWA)**o%F-?94*~t}GXleX~T(;E(|q2}u3t%VQGb@{-6(RmAU5gt2HlH&055
znuQs6VbZN&Dwzmw>331@s!i?;LmO4Kf~XLesB*?LGjugt%ERj<t#0U7no<Pgn+@kV
z-KP{NlK?h9=`Z4p=Tvd3$$~lw;<w&5K)T|5!Qgxwcl&jImkv2B6@76zxVO&p%7ucQ
z#Fd@HQRkenUmE7ed~UjL@W_j@kPIT}b@B~SM<$-2x+;0qpgZ>~PaeZsBtHb<#)%|g
z;j?$CjjN@4Ot0KX@kG3)%UgkPsiyB)keoo;CMj4%mz5O$7R`VMrShMKXVg63SY%Dk
z)0RAQ1T$?*TWLqg4NxakUdV*8q~13QxdPxy#b_=?DbRxS$lv*~Pjto*e7<AXGxuS~
zyNz6tPF?>(b!go$%IOJTVM<}x@&OCpf{884NU99E;y#c}Hz3gXIkrVYPRoa%OY%&Z
zf~5=bE>o_hMeO6Z^`gF6NS?H^6|w1J!#V?&I=yj!QJRv*$O3`p8M&!UM1G^v5$u~;
zuNps}I`O{#N!iH#ecxOSdKPcyALZkMyy^~Y4-2f=JuP)r#Lq@&u*{;#=z^l~rD}Ek
z!HRK`!sv{;q^bbI>59DDoZ}><yEh-8l<{VerF%+qCzIsBgz(;K98uSsz>^WFOx2<@
z{J;kEm-2v3tU4wKmqb1&O7%fjuKS(YW1ytR0^h)YU*Z;jo1;OHn4-iY!#UuXc7dfW
ztm9lC(t)}YlKM^x>D&TV{6#?&`DsEtMNzv`qSHd`1`>-eiF2pEOOm2STzAW<4dzE}
z4-Oy1LA;tDn}p0Gj1xDCQl+dvwITItO@q#9zTrr-QW=A9k2KOT@1eInG$#VX*2eo|
zm(4FCyR24shG2EEp70s>;t8;~QQjiJyX@Z28u{hXj$IqNq*U)pwfA}Vv)m0u&gU0s
zUz&Nibl;Ow4>=+<^-eiC!h7=WL^#(jW>PE7>{_V$OQKV7q{Fek_cQy6E)hb2c!L)w
zl0f%a$O1R2Y6n0>{w`4isNqPOEZM=LQu@wm986)d0@)ws8RLjYz^2z<P3*Qxu5+I`
zFI<yxDOryC^NDmU-jQUF!mZ%CIO%CHSMXx(v3S|V((D@pf}r)GCF(ws_~iP^<Ln=$
z`==2<_mkxHqVJciq#)4nx|3?kM6%(*dJ4?E0>R8li!#e(d0U43RcmvuAyej1YF<{5
ziHo@~t9xwuxVa)2Rqidf>9-5$M3En;#rsdcDGH<8AS*SHE{zB)1B~6sktB)Ff<zn_
zVmP)-H+v;q`SqsxQl#t@ve$u}(5JTD<dvQE>8uvX3DP*oF^5~EXt{W?>}Xm%vXl<z
z2o;`Te4x{-a3l91k!oXdN$CyFdM#(Xy{V5NMNXAE?ciLfA(T3=X10|wj@l{>O0;oH
z{F57%N#?~ZXZMx#0g5(Zi!+%n3p=<75hVE5*Yy3kiwS%qy8JE`h{7_1{U%v$oI2rn
zO7uN>yhX=4bPn{sKvm74vdD98;sE4AM<Q}TcB|u<$%3lplNFdVZBrwdZEHErbes8Q
zhM+OJEln@wsJ9(ODf6iA+!2Gx61TCVXO<IOHzu(qtw(f+$6%b`nK@6(S!(L?Eh|(X
z)vl{ygH#3x(e@SJ)GSKv9)I!O)HGJPs%?`q6$n5MdsdSnrR9en4j7&H%A8sv&zv5G
zu0m<qk}$#>VLtZ(G&hC{VL%&H@>Z7{rAUy@)wdh4d`Gm=?wQ^bd{`<tknVt17r710
zeM7L(91B+35^xT+GI80A*Y<VWwM8&TPMUoE4)@jLXT5Xf$GH$e%_N2lgSl2g+C;u^
z<WDj|5-#UE)u^dsrai5rrjQU_Gp4B()*atC_lM^iNZdyf)Tcvu;ohLsP8f%BXEU*p
zhr%m;A90))GII!qQP)Aw%Wu_e!9`?wFRlj=&SYxmUv?NqOlPp`3<@%>1mltkwmiO4
z?AAxVI)A?mHRBS``v|nWNUS{Oxpivwg`9x8)x%%BM6Wkvf&@JcF!?VZ1DM-70)IUQ
z5D2SA_GLzce9ZPo$5_ME&!ASGcvsbN=jq4rG0Cbm+hrl{%;bns=!u@>iEQ>=4^B13
zP=X>Y0`4}iVS8QxiX>2XzRg&o7WLW;*APbc)(8v?HQXHZ;6Xz{o)>6i#b{t;V`$9i
zYGn&j1q_T|$ko;WXkqLKFf=wbw-z8jZS5cjm>UU@f8bDHR<IQ_HZzxYw>MUGS5yPK
zTL5{C$b|$E_+5EH09MA11^`zpOKS&SR{`?hxV)gxznYoI0l!5YEd<Cl6qEsCHulB<
zc1CtaW(EmYb7xj^K?DH5y^#s8in!!o5TGjoax+IqTV5t67Z(>s7dA#4ds8MB9v&Vh
zW>zLvRtAs+gM*v3qk$`fwFAX3h(8$O#tuMx5P#-2)_`A_28K3HjsoQ5p!<Noz<;qT
z;R<T}Yk*(Zzu_GmjhJLW7wn+<fCQLWnVGp6m{}QEd6@p*A9Pni;h(*&9sXhw#3z%h
zfh`jYBQukg)xY&{aFlTV*LeSB4+k~S^B*P^V+R{2d!Vs|v$3@!#oq_DwRCd$`<PA+
z#=n|=>uqUd!UR(4_sD<mBPFAt{LenWXf!pqvi;rTm-OEyje!4@vvsn!{4HYyWHPoi
zwgM^Q077Q@H+e^MlYcGHzs={@o&QY{&~X36|2OG>@bz1l-@NjQ+W?(@4JsopK>kZ#
zULzZzxe@R0kDROqKqF2d4}%Gpp&<i13lBR3Hx~;l0}C4$(8z#?+sKI9=x<Om)((ya
z)<EN5P#|zda}W*-vmpzYu>lVQyMd7b1G_OdD}x~ikc$Cmz|O|b!)d_DV#xD12qk-S
zkS7>e{#~nIP(~mqZf-ViBOX?61_L0-NZ3u-SQ)svxi}bjIE|RO*@0}vtZatAp?<9c
zuZXgY068lo^S@e@Ee#w^Z0xNB$mPwgom~HQL(SaESk=+s7ilb<%-mdT%pBaDTx{GN
ztgQbMQa84D02%o&OcrLwzwQ_Tc_l%J1|VZIw=ys_X0o+5{oUo)vhac?10vSomv4ZO
zf476?0xD7&8#vn7tJ&CC3XuQO74Qr4w@U%||8f>yc^lyGj=zPCjehytUmhoFV9NCS
zlAr0n6aL?jRLyK$tpC5^{7dwoC?fWbE;jZSO7=>IpNxTy|2@usCHzk$6;P~maI|-m
z`9DnR{~^c!$6TdBeQoUB{x-g<vE84mKLN?o{5Mqq!0#ZyYXJO1zk`9Z@gG|S@%ZN!
z(9FQv)EE?P|8li|w447IX3P#`H8kPjG-lvp<^h?pfgu+I4+kd~13L#NyAdlF(3sPN
z<M#sp6Wzha#L>mT-dMyGWCNge2D#Snbp}xXwmzDFmv%8T2H6N5GczwUGx?w81@JTd
z3aI~>9{;b9qoBb1cLMl-g(O~?UzVrpWNT|_ZfyVW#Qeih{$J$&*8eY~{6BU7yV#%J
zVm7vJAg48RRC2NYZ-@Uc1pgq&n*)ul9c=zP)&DN?hnBzXJ0P9^w1M_B(Dumm&;9W)
zv-ssX{}+G$GPnPWBY>d)Gsypl-~Z6{AG-cW4E&FT|6^VMq3eId!2d}2Ki2jC8(j$h
z%CL>CK?gk-Q2spjR%{;ha$umjn3%GRnAktw2LuD7j`fNaknR>H>eW+~m<$VmF61^U
zQN$6KAgV`@C_(!aq)sf)`0W1{Stcwze_hM=3|M`zzFAfpPzr$a_T}c`rD>7={3?gQ
zV93vX(%<S4-Rk12%)~Z&fCtOI!m0YXDP^!COs5jg0IfZgjGS#aCBA^tvwvOy!m~a6
zoc`6pCrXgRlm!657-#LS`^*(u%w-jDDQK61ZJc<Q;W|=)h#_RJbAA`;&}Hmr8a(Rw
zDT`a49%o6z9H`FDu}QO8^7Ur|lM%l<kB!~j(EGVIDjx&t;!1jl%w1*-jUEijP-SHa
zc;khc5AeJ+I4X6dx93vY9pa2Z+HaWc>2n~EYYt&w1J+iw?g!zSXs}>;^@#R>Q2R^J
z1RVYMg3cLz@MxYe6<0|eDzGMkGL`jp`{)xL$lBVC?8c}~%izITNQ((NG}5XAdw#cU
z4@LCMwuo0{ZtWbmb`M|$UrM);ZEfKqaHu_2_EuJo+n+s(7S~Xts_bh*2ipBWwGbFX
zIVo|l*I&Om?Zxq+7I<4}O$RVAMv7m5;I4&2&Y(tEM;QeP*li?SWJ&@BtA`9w6TYK_
zhNGB`)vqKL?5}hQ;9~A*2Kbd*eegrU1p@<s$%u=nxh@^8xVRGSG{3&yvemXOz5jS^
z?KEf~&v3iSY^~L-EKiMc_6ALKJy36GqX5%>Kn7A#5dog3po<J#)X5Jo0vy-2tNoUC
zvBt>hShkgK{4_eT3x-ge(!%Ow-ic<TrhY4RF?Hphf5%%Gjw}#O^gsXp8xFL&-xLdL
z8z2GTrJ<I65t+e}>%hx}F()R@r{5OK|3w|RPH1?C_pvZ4^i~GSk2C7x_T+98Ic9}!
z^Ryxz8e=vjG4t~J2KwPCUNj^vZ@I%8eddK-R4Ao8$gXY%E5DVbxgWlxon2Au6@1?R
zSP+O7G}Cn`6<>I_pP#vKjf+u7rDa0t_hYTE#>QD$RWaUc%VXJ9$1ccU<_~LIX#H3+
zGyBy0V@}aam5##+`N2$~ebwWx<o()=;VcIyY}DKw>2h_Yy@W%$4|)|r33wW~oMQW5
z^9Bu-n|lE17Z#UM$23ADRA;7t>gm2?9GjUH!N-d+GbpE(-G2Jf>u72|O?V=&x;gQn
z1%|d%sj8zUZ3XtLMz!7Jq|4li22vY%8HBU6GVULfk`~6zHht5%!t(`wy2JB%`#TNA
zu$rPf>v{DgUJ-apykwpW)RQnk7+9MO!gD_2``+fF?vd@eb4=XX+Bw~+1)#PG|EG#~
zZQ9v(!77bdx6=2G=J~KYBjtEDOoO96_y$s6<Q(o{{6*TVyg?`<;o)ny9SxRND=$L_
zZr2P}(Gaa*9c}ObvUB+T&7n4CY?N{uIcF*Fc{Xe5`;5W-2)lsC^)Oq<L+|>>XUj#0
zlpr~wiSQ5BB{~;fH~oIaz5)XiE`OY*M}m0=E3~SW8aYwl{M!^A*KzErkK0%bUuCma
zV&b+z<6?`?ntc4#HWbV)qw;h3B{^H%R-e_1lfJk`x*ld)He>tGf9VpYoWs%4ZZ{Ol
z=lyd15wns&OV&_(<PG7Ncf}DWA^sqILgF?{Tb*fNbs8oRm8krum3cdSNn+!>f0~|l
zlA4*o<>y`jm8@^CyBD!*EmjTr+*Ft$D>&aC%R2El08DI&=K+u06h|hf2TLt1^^pvG
zz7zv7f6XTl%F#|!ConjyhP^TRK<`Rut;I?GM0*@6F$0g#xqa6Z^#H*|9!u;2>+4m)
z-PPBFC7O`Z!zj95X8_=|>YwY=MAY+XG+<sH@9A`{!$NU$mI4OtXIK+GD&=>BVs5H{
zEVopaRum$9m;Hs$Ik_{iyzIRC0A{Rx$O0FrN6E2oTCIPeHXS6tHie%g5Ny@G?zRk4
z2&|>>O>4#_e>NvbeiniD7vVhS9e5UcUF&n1KhD^5Zbfvkx3_0wV<U!#haWn;hM}dS
z`xs5k*RVAZOW|W_W0MVgw{8DVHPMo9@>HqIX;_iBsvm9AIPX`P<QsQPinQPL?^{^x
zkybKoeYl+4XVUg5PZRXwg@%Ua&Q$d80?f|Nw%x9JJbb3gI%CppyL(rDjaoOS0<Qae
z9YV)h(la3JzL+Zrg{*jP=eF+2w>a&JPsMFkl=!V`S*TmB_NIS~J`c}+5sxGhASj-`
z-&i9I7BMk-JFo2!85SMwa<tNVJ0{?9QwF7*R4VotDpP6zBV&C{*>)3^x9$8j&^p@<
zn+E=b?r=ZBaBJ-O$$M4y<o*FNf`|trq}wh@@4b<U2?j3ip8NIe2Tb2L!tvVV|MbN?
zxqLe1r1Z42Z7(hb(yKxXs?xiK61o#W>8AtN&BDf1uOA=q1>D(-?teC>jG9mg2<Q;=
zyE1zm&Xs%kA`e_x{tF4Q+pVKVW8S92tG`~X!|~V%n{cOP6QySMN8hClJyp9U1~xYK
zg5@4(03|uOe@Y6T!_TQgiO9&va9@IEqdyTY#+On219fP$^UFzBrKiEsa@cuOK1F2{
zWL@3v3Ti`D_39w5O?zZYO3L($AR1hi_p`fth4$z9!#BLVX`!JL_Vz{QaVaU%a&mH0
zOZ67DPu{N>ITDP7f1H#u2XQiWD&COd^{PHyVCyu6L*`9We&s;GvdcDJhr2nn!|5Gs
zP*4!{^&|v*l5w^B^`~!-q^M_mug?M&^EO70H^)ilX$3_^w5=<OMn;sWsi{Xx^`-jr
z6*@ef9s{0^{~AUH!&h&`>A2wM>ly>LiM{6$wp$FtDyR4Lh$sSVbpO`5r=i(nmVn3I
zPISk()rVWt@9TnP8qIyLoBk4|Yo3>*OWh%;{RIUD+=f7)jGP>WkkDG$*RKcl9=9i}
zohUgda07*ue>j73Bx4NCRZ9qdZF)>AwSJwmswKjwf&Z{7LK`%f_NJ7p+H4E`<@lvC
zJ|V$zsd8p!=4>H9n)wT*AO2RzR55e<_MOIMBsM8KNh!~H`)uilN{@{oTx~yLzzVy<
zM_pZ#09aJcn%dg%C?X!#!iEM;0`9O8Ib6{|vQsC#wQ-8;)?Ope%D})=Y~YJ2%PQ${
zf&&FeGi~Lb7*#+Ec{Zerw#)2<6iM4-aLOvQfFj|ASM#)%L)*95p#w~{dCoH*#3W{`
z6R(j}HrQ@v!6$+2@qUMKf&Q=aSvyp>-hS~x=UQG?{I#}UzFqeFxZ>Zju0!<gY;P|M
zS}oO0K0jR7nwpqER9P<8tY^PI?QzuC*RMe&QDCnIp&8`VLlhgZyrZM_g#a{cU48*y
zKxxybRp;H^NUnVYQWCTqt)z}6rg7)hoh^o(G_#m5uM4IW2hI&^PSli?33d(3sn=MJ
zzKzN@%W3)cInFQt?wRz(R_nCcSkrR3DYo_GTl#CWDNOeRcIKQk=$i(9{aNlDcl)ZB
zq?V{!La_eA?92cLhU49~wzkr|{QP(f42%(MI(6IqiCihaY%sy*?}2D5y<Cv07P1qb
zh$Z`QyL~j{m@QkEWhi$-$ge;=BHM3aNwriUT|GHL(PT0e3#(s3ivOl?F+Q#@;Tqx5
zwxWJlT#T(ASpiQLo%km?FDpbc8^rX}e(Lo6+*sOZ>;1-QR&GdjW|$z11^sB{oQ3zp
z;g8SZ;e1xDAm@9!uJoa>-DIwwjJ<;tAwuEXpJ4kW>z~dfPYkqPsDAaEUt6n_v3N$v
z%_??@A`y5Po6MKW_ru76oq#KgwPg(4SJXufo<owlWYZUyVixM22ZM=$ST4=cu=sY|
z8Z|oRBP;!D>lX|k5y8!LVZo=JoO+sfIcYNaZpB7L5L*#ig}O)Re6n}Gr@L2l`h?o?
z(r<`DTv}Aql5~K1J;Tv3F|{PQbFPKc(Gg4fv};~Lv)WG#v`jF;r(eP-c-`h}`q@XQ
zgt)I|fy1EHqxW*@okx+T(b7ziZjN>w2H@nq=r%Z$Ij>%@ECLP=Ar}hztb64gB&=A#
z(!wKSufWzhZRYa%^+b_UP0)U*MBA+OtfYEl<_#UOyDYh$8W`MB{1a@LWR-?a^;h`K
zmT;etFs+7V@ai6&meln)jDT-gVAea-w$NzwA{Ba^4kub_jx&A97Ln14ae`Gp>tL$T
zS1OmWR>T2q+jaGgn3FBpA#XkK(`=}CA?8S`G*mNt8&6u(>gwvI8@#Ue-(}qo$9n=f
zk(g@EUrw(1?`NEK2aozwM7;2X3P^(%&BYgv6Qc2ZYRrMM-nUPLYvA7yhz<+H)QE+u
zi_C8vT2}%x6-`OgN0V$Ce{5}SZW6tF_ip!mt3Lo485x$2j*b*^73}8*hN!2C1$3<0
z3o@Q_783_}v_JBTy|<yBu<}^ZuWb=r$Fbd8%wx~%m2O_iDafPOxt!xMifD_=M{l6o
ztJKxDXz&aIvJfD&sINl0pkwt;(+92)B{oxEIi@VsISmhC39`yHy{O8Pl0q_~lU<24
zw8m%<Bky3I)u0X8rNFRHm@wbtL9@mQIecw#zyT3wvbKb3x*y3%c%0uvfym_P<etj}
zg3fmh?LeTTN*$wh<?QkK93LNF%;V<B4QOwF5!lnyQxF%2QQOqCFfuTp{N>9RT5@uT
zZe}<rh%&v7Z}ZQ$YuR<?(?tP+?ngmrxVcs^LkOFq=!Km{V#s<{T9bxmPh<DVdSWS4
z@-P+PN4RGfaO;d6hVWHK_I%EIJhFu1)Cvje+zBw{=JPNNl2BHxj(Q*yfO15)#;8@@
z^mcwME1iP5p@n;N+vB)acsdOa*KFM+emO*ZOTqh{$Jbm~6borsXb0~~YGnvv;X}Tr
zL+4=qT8gZ*^Ucp66<-W`!?5L#btJS4KEohE#u;MGqq5=8(}C1`Z^`2rSp4#UQ_Iyk
zv3gH++G_(*3I2oeY=X$U!)FtjYx%6c8rZM9(&6L7Mnx5pkbp&m(yyt<ZrB6oG_AmO
z{czLRp+gNyKoIw?b9}Le4e`S4gj0i!0ANJQX}6PYhnZtA^P*ae>AqSbsO9&pxxi-S
zp9m+{NwuT(zDvW@8Ljn(%xTpG!8wC7xl94Hh4;dbuKj$OvlT%zgTw$(FoI}-!@Z3n
zS@1Y2gYUL&CNdC^UOKec`+0wQc67;fd6_PF!1K!Ol>{_H{QeLCPOufs>U1!p^z`fp
z3CmsQB_LKzXv&ZLr8+D@@GV7{d<F5>X49{|@p3mcU$?+<2HHY>Nl=$nKYZY-FE5Y$
z`juAe<41FA>msRW5^)KM%yWrggP7RZ4G40+B|rg}bSNU?9uQvPeX4XxU-uUnFl?=A
z)ZXw_1QM4~;F__mwsU#vX!AIq5FuuYuVL7Hw?oA}6k`C8X*T!PfgsmEvzQjT2&~cI
ztfetyEl2ZIi#j_S<O7sB;U^rtbUfB4UR;VU1#f$Uoazu^7<9c%v6VWWc-OA*A{aQU
zvQ1Na(%z1?%`)E-HpXUnx|_xsYZ84aE10P#a@kIm@;itk4S`Pzo^3U-+DC84z9ZFV
zLxa5oXWDB#aYMG<%aaJdn6K1ZqvPdm898=Fl~YtaNsW)k<N@{}{b;h=3jO?<=U76P
zCPT3}sH~c?2O56a_o9&*(-m)ja1pVJATX?GCA=uP(%;8rtaTlxS*{SSDRP1}2A2-{
zjy%`k1h)Iy_lIe|zc&ncG+LU&t2wq3n=ez|e!wQ7E-Ib&<(mRTvOv(et3`_Ftk7nK
zvs7Qd(m(t_>*uGWLd{wyE+5Yg<e~6rw$hQ{Lw-{g82_`ERj^VBf&1P-`0NyqliNog
zyS%6ZGv=98y|twZo>jPEXQ=x_zO>m8(bnYH*lOde{Yjd^!9hg&;h|-aLsl3MCp{y%
z5|1?((@2*LELR^K9Nd<cmcB0AMc=B+t~~S$z1WS7d*9SXRC?~v9Uzgd{FsympeBiC
z*a+JgaGwElaQEhhk06RZMb3Zhf=udqDf#gIboJ8R_EVFNh$fY_GX`^Oz5klz7-EO+
z6%HybUJmLRqW%cax9o-}z$>#YRh8Tc0w|bcO!O9-fNga!yWKgl^`UlIqn^tBly+iJ
zYCD6u-&MCJHl~<!^OjMgS$J=Cei&rCC%_e#mKOG~u;ALwY%9sFQG?re)esu4MlG7#
z)<84RMX*qX>V5y#H-Lw0#6;bk2J{Mw<fl&uF$oC?WS~SOrD$+0lTS6<^I~u_S=aq%
z>FDU_Yn!i_pqs0=e!$#Ay6hJh64!M<J!Dcre$%6cYSQPtxjWBqZ$%Hw5y{4=demb|
zIP?UfZM_d}Pm$QKX**ruB7Nbx;?+uS+t^Y&pNBTR^o<w1s?9IpI2s~$o<gpop-#-x
z$6P3IhAtBSn6w-n=+W;6-@FnDc4A^0-dy`0=v?~pbYX~M>7AavrXPn<I>wV>Au6!;
zk(S}*tf4n;>uf!yOI*_19-jwv?j6q(aAS4GKy9x}=rMy7!>_xeFt6K-oA=>1=1Mxh
zxxE-18X|xD7AARELR=imx%IiOGaE$O@Q$ozqxH%yr_E}c|3un8ZugYdIONdn$%@D0
zLN!pR?H1AxECiLb0-DC2vgh6`<1_hyvOcXT>D-Gsxo>*n_tW$)$gvORD9OOf#Q4Gu
zk{r6EoqfMe!O9Tgiu;+6VHOWMX^Bf_=1gUZtNR*AHilkA?cmvipx=kQWR{T8HsI2}
zAEXpAb(cw+L2GtbPA}+icQN#R{!XM>#Jk2$3+P{%%K-KQxC<cg>Gzp${Qend)K6=4
z2{J9wgM-gPzb6zOlu3CTx<1Mj78def3{Zg1oI#*OCN3@xlyOk7u%H)De^x>0W@bi$
z9ImON57%c79p8IA>mVc`D5<D00R?=}>H7+T1d;^W8IvG*wNL7@S4mJjYxv0yk-MxO
zCCmNv(Nl2M{ji)iN2|h;Y;4P6f?OqFk6g@>(l)%I#JYTaa=?w<$%FfLeIg9sOj284
z`I-K0ZtjU{TJh!he$Yw>r@IBTjM+ED5hx<u>M!zGTOUoU-d?=-v`+@@UBBEs!~bc7
z@^%Ge?;NU_Ti>09>gUQ<z9my-c|;Pz)U&pA1|KI#jqWxmkKg5uil`s?rok+%x(=0Z
zIO?mW`QGN6mNhkTlar6>G7YdR$jZslFfa(n1&C1l&d<*~Jl)xSEd_n^^NyK=g9DTs
zrBg^nRb6E^!okpAIH(NBes(e{#u@#>>z%i$Y<x_x?xl}u3ug=Qg*bf`wk$x&tBK3^
z=~r@h@;EdkdpOFqwhWsQQ~ELV@;-tL84ng$Ae0)XNy_|dJKKHyA@)+sbrXPeR9n<Z
zvPG8Fw)Bg}<hk3N7cvH0=nRevWFy8`R9hCJP7UL8)Ai8hH-GHE!o;k}>tJ6*Kr%To
z@H$GJxN?h$sY4>TI>Z7dO<XE4NoiX6ef#c?6ONC@*ZudL5iGSA)v38zR_W%3wdo7;
znB}=Er^O8wv0ST~3h&=zkB;sMA=J0GXX>;%R~vN0G+6!UJK(Umn?J0KPd~}AIrT0m
zej9%fMb^k3SsJnu)oswvt{~4gvvqo1-<njh%)C1*LDz3q&d|;L>D$Y7B_C*yBmStT
zH%?dCrgA+c$Hc&jw3VD>Y$XWn4pWiPI;?qm^1PgpjNq`CtAjYqee|dfY#NDxxZpMa
zNYm-^KBgt<CqHg|m52KKGdEkaCTP5=*|j%;NjmAX)!W)R=~45+y_ub_^OMR`_zw_o
zR_$qZ5DU(J9s1z+^DM5RhHNrf1j&bX5PoD@p4Xp$-@MhR+h%xqctuiKp~V{GI4Ygw
zuc03Bk6c&fEHW-snQLUOyJ_ZK#4ZxlCe^WsG}bJj>^omm;vXJn>X27dL;;2VY-%hQ
z+H#IA{g_NXmy4&%@ypdP?4!l@%!mR!m?RvwW2sv-Lc*YK6T_6;`vX~;gxs+OV$(F^
zwwt+6FxPafT5ZQPB+()7@+HxcU{YC)Wgkl3<y*U*b-_MzaB=xyYxMr?UbWIXq`{<8
zq#qm|eLh1?PfvF{uAe9H+p#xCv^&uf5XU4aB3}HQK00$&rNZ9mu7St%Iu+trj_0?_
z$Y`nfd~Xk-r<w*LDy6NU4V&B1tf13pqS@hi`hCf{3C#qf%de^<h$6bmWSO<5!Zx4N
zCz2B8awJ?h7&n(|9PhJzy}Q$NB-tqiEVMd(i^AQIN~ncXrD98I+K&yV`MK5jL86()
zMTIH|)9i!eGJAB5qr0<i<MyB<|4GXsqx;Q~jG7vj^P`kS19X50=)A+@e1wOH%eb9d
z{3;nSokz_SZk{ht@U|aF&{rJ%$R;CetD2E4GoEK@Uo)lA{^?Bs4DxsD#`w6n@0l*E
z=Db8ve$fIq3u`yWKU}!Z7)co>lOR3MZ+_UgWVG}wfvoiE*BywD+ZQ;S%QOn8SX-_7
z({8foXcH4B2F+U-!B?E9gGsw<em!!ov<@80r*wPXd;W4CD8~=Gf=v+4z(^Ia588rJ
zQv5dU)|-|Y{_?K4jPxYikG*$fk_B|+BUwp&FH#nQ3oXg8q_2Fpx`inV!#xI~o&NQ(
z^xiL8F~p-XBkkT0=7aF;><tqO%VC8KNY_8>W01OYKD=YJt7a)%tctWSkvAPvXk>N2
zqGn}fwdKvcd-jeZ6&mY{By>AjG#!*@Pi1Z4TGaf~1k6*;)oBAbw?@`@OU!VwvssJN
z9yWS+Vk+^A$l9~IK|>=EaK|(|>|tPHs$QLSqrO^!HXxyfAIIZ&&o5wS^{uDhJSKWR
zV+vWygVG3kofftEnHhfA6}AlZ&|$7zwn938o&MrQu=ReC(GGAzCK-K(ev>Ya3FDxR
zu|6n%EtWODpby%w2M=T$FNg2InYw*R!6kIA#;+iQAuqR0&_N`DGONTdwNxlfhl$BS
z=6EaL*a*(*>$k4!H!N<aCzfT6ui>*i&lMh8Q0<4|;_n8O3vXs0>Ai2XVj6E2@8)zc
ze}0S$!LAE+kaD>)%Km86U_i|}9WNb9I@CFpWw}zFXtR1tX(bNI(osKISS;3>qVnKd
zTemp#_TFoXOYjOEG0fwh@0(LgXm*gRSF8k!zRQRo0@YgvXFhi?wml#_A7>0W&Fl1M
z@HkL2GLAhxpZdIB7H7F1&;qzk$1+lHS6oQV&CN-zPeG+r%Y~{9jrZ?oOMg3=C&@DL
z?2ls|oL2*+uxwYlrZtY}mE@eRT;GZ1%af9)RHWsg$oJY$adFu+6SDYWdSJ#nPo7Sy
z`mBhS;RWDozq6~!d-Cv_xKn(d9Pu~({kj)E%Qf^%&)^4XS_@89VLGt1nxI6x|I?wE
zz<L+h&;=0D9Y4b^<Kr;0iXpZLqWV$w-HV(E=lsI`!Sc_9YX$@cWYaWlHZS_FoFIdY
zHCu;dHvh6KELIgve1WorgEaRWQr;oGEFCEoPj;6zcj9{<{Jl1va*h{a-=X|^ZVDn$
z^!6klp(`Aamdo>;vdut8LFxuDaHJOWaNBz{X*8OkkJr_o2NBvBEmRqR4thOfscd^`
z&yTmNy1Lf8ySv^C%KDl(>6})ZpoG*1T~k*f^fJ$YTKRkL2b))R7-sy@Ze3J8htWMV
zTuyFojuG%+r*jssrRpdNyYkA6H9p++)2}`y<r#)@=c=No1|LYLg*yVgJ7JnF{?xBB
zWxMLW1@U-=T+@l-_w_nKXBVsM5gxi6ZPgne-i=<`#Ozt-+2Bz23V&8bPeiC@lt?4A
z$3(t}!**7kB=N;=W$?a}jVb5g+*OM(Hq(UX{2^5z9fz@u>#e9}rdeMijqA3^n4qGO
zWOC6&w!Kr3?ZAx+6+l!f*mW_RZpVEjPr|ibRy@y+q&+rq1<?%`nSAU1BQ!j+$mf(l
zi7`85Wn=x}u#$?53_QBup-)ul`ue&|tNyD2sA&4?d-5fVzGtUoQ4ESXSh>>XLoFoR
zB+;$A>jX!38sjHhYRGT$^O11exbHeMwVZUixb=7h=<mRpv*c8vn27PVuB#?F^u9L#
zNnm)D0#{6ydCgtFP_S{F#|1n1mV2Pu?FDpf_2kli;}c_P8K4xDMgQpwmrnj2CKzHG
z4%uU#092<il8TXfF|KaF=!@R(9U$`(LTb!hb`^Pl4X>+L6ZcN+k=4ZYCyxTSvh3u{
z@=$QOheq2KB+M<7!N{4ncni6i#qHK*JtiTh+nk}jEDe1*kL?}xm%(#!?jt19<&a*)
zS5zq}X%}vyY}ZU<&7;-!^x|x9cl0WbD^JEiZd?;Hp1Rm%<`K!9J96d2@^$oiegZy+
ztyTL?zem3aX72WOzaQ<>_kI>+44KUvbM`8An?E}1lh4}2p(OL}U>y~<!hUZ|o0sO&
z>h_+mrBX=D5!Tcz73Io!#H)zbc!m^$2O}M(PCfEIV7pdu7i%^eWtIMQlw80N)TWZv
zi&}UsoJSP(^QFziJ5Q^TRG!!#hZG2X%`^(B=*zj9ddoF0Uz)8D=DIR2s_;_|cFx-U
zOb*aK#Aa`+mq(sy3b(%=^?7q=KKb3+f~cV<{hOdgc|6U&Hr$u!?QNrqZx07bP@bTg
zuF&iKI3}os?$KpZI|~gl<;U0((nu5}QX-z~Z+mKd<<872YQ)5NtbB&IF38TIi|XSz
z+CNXz&xRQQAkjxyVXJ#aIwoEA9~9NMD-Lo%ousWByT^rRICiFqF2+*Owcx3bHTd?n
zr4b?srG9}Hy25SBmF`!PC()!!@xjYrDJ5b#V%vJrZ$_Lez<qDpu?pjdsdjbw@!8Z8
zK<J}cbtT!A=^Kf~EBcXx87~AVfA)IU#U>}NN=7Y_Hoo9Py1w}M#D~<bw%J4AVbt@8
z&!qsy7O>gwhVlVx&oGVjx`m3WZ?cs;1Q(}GP7KJV+K2k)?Zeeh5^^_yhbBq@Hfp2F
zrq7y7v)-DJtti6qDhJ}_etB(hcsReR>MViWDQ#ys`D3)u%bo1Ho}aKVNvF6Al%F*F
zpfF6D8J4gA<itPT1wb%6b1zW~)7s<L>UNwz4*Z(oB4y*ie}<NZS<dDrc#){6q4$Fv
z_6`A!P;!qOf0WA$+tPNd^-gz>6ZIX=GW@cX8QybY5^4fXuls&W^EIxMkb9_PY{XmQ
zP<8!Wz7weuCufy_m_O$ohXTm>p?;*A)S^WJz8IqM7(ogC4lm>oC8+((+%fK<Z{OZu
zPHBbfeY4(h=wU7Jp)KV)%Byn$NAcK;Fsq|iu+o9#yCvCOx#g;Io+f#?9ol&aJHMje
zKi~LdLwXu<0Sm#-S+DF?mUJt$FqHM2C{Mr^^WuddG+FDzDt~*b8ft*ocD1Auhp~w#
zP`74&xu?(#7$er2_UxN^*V8L``4IR1>E|h8v)vr!6!5vN!4}-b^QU57p6GaCtIO#w
zSI51Aj)TLc59GDITeutnRFQ$9O0db>T({+BPc@D<l!a1`Q}<!F#R&cQtzPC*@A6PF
zwsyi>07|dVR{$ux{GfBm#D7+=zAoxOm-}W#O(WA7ubhLc-qn@=U^8mc+}s-GB|jZh
z??Y@NaK$l$D}A(sab_2r7!hTI7=T$0?5oQFbGFdfEZ6^LLGp1M6ZCojsIYYAs(j$|
z7-1)dP&@rIYU3Epxi#}#?BQbB0>5mvF?&-D^Af1q7R_b;(n3B^xi&*-Qs8h{R?6+(
zmNZM~jJ}3$$VZWJy5tc$zp6EO!SMr(nr6gku8zgXv}o$!dgHQw3l-`N*59?=s;>U=
zi;ZPbLxmqjV*jGcJ|3o2GK<$Ha~$SY)eof(==8LYi!N1RX)SN%KSEW%bN=SKf^|SZ
zFiA>iwSQf2*Tkyk+YX=#Xj(9<U?3eXebpKpu4FR4xnen5x?>zpx;9*HvLoVk4DBcN
zS=MT{-^q8}JbOnrMuQXxv4I>IOmGV&sEhV}1${v(Ck5Sd?_Fg`vi4<nF_(@e(p=cf
zN*m!~AVTJ>>#*j5mhDR<UO6^(Aq_=cSsEP@Q!=%F1-MP4dKJwDM6lMQiT*Usw66{3
zSHg+Rx8OY5c^t>%5^_A&^xxo?#Y&Q2x1i-V{UG#_((rXrg=Wv(x1Y|4ph3k-P?={h
z2r`AoS^pI7wm<lTo{E-gT=L+13Yd$RrFQMvVF24pzEdx1#>CUZC}6x|in5^CwtsZz
zN?NI?`HdQ|q!yu@1{(g{+8uY#P!oshAe}j(D_u@Ve`E6&9?Jg?61*%YsF1@?7){NL
z+TGKVJplH~Xl&$j6EMGeab&us45ACbcS~frctDIqDDe65qy>Mtrs?*CugP7_2IhuN
z(xRl|%bu&1z#}T@ET1!}+9RrZkS-})UIIA-j~}6l{C4@yA-C-v9^@m=`Sw=y#cjvy
z3a|T>+41gZx{nj6PNWAZ(%jbj262z%h<M|PN|+#f6?jOl_WtREaCM_U6?2JW`&9d-
z!L<=m_GS_rRI1!4*aNtb#Cc*80;hPUsqcFPt~4faHv{*<k|<!Ci1|(DJ$A(9sIVqZ
zKGsuR{$z1$Gdu%u{usR7i#OOfrdSNwxY3zC*mLO_4;~5D`-`y0u-i(#u=>lNkkQ%`
zeWJ&nuVjEqPIPn9uB;GGSQNRN=EDH|_@2YuI{pS=$!mH3qmf?sf6?@nQEhfj*Eq$C
z2XAqTyGwB>PK#^N;;zBni?_H_!8N!9DQ<;O2wvRX;k$Z2@3&T1S^04#N9N4zJ$vS`
z@GZt$!8O;0Qds}{7eM*;!?X%Ga~<>=|0Q2aG!)~+m_*YXK4k@X2(6WQ72^QHDAu38
z+Z{NR5`Li}0a(l|!;Fk?Qg)b0fp}X|N}tnM*iG}I&itHX&OvnG(0h3>9E0%N&z}~5
z>Z}@?8wm)hVm@X4p5s3`%olrWQGbk5Lj?l!u(6r6W++*p<8LQ2j#G_Qd@nw|TZoSJ
z;A!B?v@rqKn=fT_tngxAlJ11J{FKfyg_DPEy_VLt9_KG$Kw5b_Llc+p$EvrH1q<jY
z7*lhWKA(UxbmO8^bMsvzEskGCs1h@?F(TEA_69;?5jwx|VTEjN!$EvFGKTRQC(Ig@
z^2e|X9lWmCoQcz;1&r#b7g+N9lb@eswojiZsJ73{iO_>p&k7m;3uV`0nkq*tyn?)(
zy!>oeOq{$CKvltJV`<RqF~^ghr&nEu6@LsU_LitcYQj@`9UQ3KH+i)PTRg|#uYW8S
z4KM}DM*uC1W}N02#+n~lrbE=$GRK?vkmGSs{=Q+xMn6l<ek|`%!N++9zw^F>hRgET
zO91+_9CbYhHq3#~kM@;R)uilI!=e8|;()a&g-ebGrw?(>W<<ou8LloBCrD>5?x5nm
zR_c0mB4&co*pTw(!kO#olho>Bwm0PkQjo%&1ZigUgiPI>7{Udl`}i9sUrRS<J8}C#
z7OZ8&IhVB((po(ADkmP5`b_799VT{_KZ2#A%6Nh!k#$cqUe~Z5GUv7>&vA2byGZrn
z*F6HBbhEnL4a9-iughd7F{r(fQqaX+VZs-YRL8r3(<Q$-=iV5rx9M{+ph;h03!|<(
z+={WkwpQyZGD@FUa))qcK&>tIe(~V$EsI^)1uJASY4A7@m3FwrB*m2In&Z}T(_CgO
z9tr>~OO3T+?mONA6{4Yif;scv9tD}vTx!Jyy+avLxv{IRFKA*vw(bq%y3g7NNq~i%
zJ8u~!AJ*`u49?RHS(*0@qs_zfiUEyPDaPr=rSv%cd;FWl@U7&GYNO^2`=sZ~*?^#c
zE6@F}nIz|P(~Sp`K19{kxuI}ur!T_X%|X@e3L!1wx|+<cs54oo-Q95yd3$(bKloOz
zeK`V9FLm&S!u=+oj-6LSXz|X?eu{Ua2%<Pqm>>9Pe8`NY3DZ70K^wPEzFH+CKD-2}
z+s4$p5Mae$`*A$-|29x7)OtYj0Wr5nPXr-<2`#Dy*I~Stg$1@?xiX@DQgTI(x?+(f
zL)$qrO?by5=OxbrQ>Pk%1=VH;256CqR!G`@bGBt<V4*JkU5>V1HN8x|s$s<+;j8~8
z(p3Uo&d-1#ThMu{x$BYr`DRcux<(%^4ueL&4RFcfY3$Z0)I_AIp$KljkcD;rSm0Ux
zv<j+Oq-of4TyQlz+R8<@mKCP@#66WfULxDv!7MqF@ioiRx{RsC`%v_uSSLRSxkiV>
zeJWj~_ZOG+zK>}I5ss<$<f36u7^Z*-XV2$rFF*p=b7jMUPNFkKg+^t3zpu5IP?f+_
zo~iYrVzw&eo7cdm<0BPW=V^^E3T{dC%4qhR|Dl3}^47jV%T`9F52>L!l%7wL;kOT2
z;)H;J87Nj|=jP?{o!OBTw{%ZZaB_GCM*GhFOVHebx$4COm!Z|$|DrrAJgq1P+gnpj
zUN8JSH3#2o0A~+cvi2xE_gmT{Ev`Ms!|PC-l6-hs_1>=jD?J0`lhY>#+uVBzkTSQQ
zt>lyNvtu6kOeyk$bA3)&dl$w^V-vnXXfDCeLLD3qB7F|LceqDR68HR2++2w=FKzql
zUSjoi<!!aKv%g7wvv~8KPrzQ;Av(c}%T-wfRjFd5^>qRl{H(SGeL1!qaxvRF{s-Nk
zQ83zV@54q1mIGTfcXOEw&OZutCsqZ8v(OO{BD*D9xKxEsAEFY~>qF@%L2pvjw4&RX
zP=1u}h~()-h_*FEI3{>ciACP9h*S3jq_atEyDBs1i}os<W)r-a?#A$~Y;S9BDGCic
ztfd*pGkl<u;h*fXh9Iv_&D~A5iCLdWg5&%3yOHQ86v@X$R=_)JEU@NZq@k>DShQA-
z){j~9)R=N(tr!eJl98julFl=7K!c>#s_T2MGOmNNDWKy4z64&eB^Ef<`L4pn@lXYZ
zjDA;u=^BlRkyx}w@;7aon}_c4M4H#FaoHM4hBj*DTH%rHl4WLQKciM;$G0^y1OD26
z40a9wp&wPRw@R^BOns&({Z_n=uG@4xr~7vz4Fcz+^0!uh*1|0EmdqYkMkEUrJp?}Q
zS;=+Q@^2PZw>VPLOwP+FXH!Bzg%NOSJ`3-xtDFVr$=s2UDMy$p#F;afB~U9KA7Eb{
zRQEa6C+B|k3zQiVO}6@kUAnW#O5>={77#?}c(~p9zCtTD90J00=T%gSm9!ATd!(iz
zY0nmS6*~ID%!tG3=@lNu+~Oc7bn2@?wI<dX_Y~u?YJ!^NKSFkJ>7f}`UltYllOuJ8
zTHTg8P+eO52}4&(NRm)jVSWrv`2{g&TqpLGAtK?_c6(Nj?`g)yNd~VP>k}!Y1FQFT
z$U?E^eWruqe`y$35(eIZjLq#5hNwf_)?&Jr>w(=0{jS`-tX8ZAdBg1I_Rxzzy|)97
z{s6jU6y{F+Gc$Nr-K<+{w<Pcoa-L0nub=Q*nPq)LdZIwdbxO!V;Eu$ax6X8p2yzio
zAAU1B=rdc2NG06?-3&(>a&Q*THJF;-e_QyawDk(qxBZTrYkGSZzRkJfL7)K-T~`nP
z(eG^qjW-<fPW+#uqk_U^HvB8Z$tGOf!CQ+(?s?tFI*Dquja4wuds0Wa_S%eLMAxxs
zDQN9=M@wlso`A!);s+dDAT`)27f+@$EV<nQZB1yi?fQiloa8Pqw6nsL|0BzfFAJ#A
zEQ^0Lg7W6`@BwYbktR{=2=blMV~A<tO=5HCftpyPthC%<_zoK**P>Rg_Slkc;+Y@w
z6dE347s?rD4>u$^nBzYqXCTDE-q%e_Sh9}1Oq6e-J&C7~hMtNv9g4ZUHNWoQH=J+0
zenBdd)>S$?_#~rndMmAcx(=UG>9P|7l9^)ndxq`TJsELFrIP7T^YW5caqMk)MU4Yg
zR6~=U5osjWOOe>_q~`Q5`_yb-oR$9YOu>Di$Xf9e$3F4p=v?<3ByepxbBa~di7dPB
zcPjA|5s;2}4#We;yc3+JBl(r~uUZsfBJbz|7ow+M1Jx-Yc^^}1WfnKy6tcPMXnqPW
zGyQE7B3*mckL}st6q#ro;33tOk4V(Z{Hp9GO~=A+2Cl7t`-HYk1i!+mcf}qj<k}k@
z4pAMS;}MsfnCSRpW(~cj)*X>cceKB#PUOTR5tGj`%ld#@m}wz=9lqtUC}qXI^3cZ0
zJgof8gUeNVzH2f0W!0%69-{bb_0Afbnqc(J^Ow~`&G!8(7y(zN!?9HY1yK|3a&v<J
z0ZrO=9?r=iDJ2svu)~E_EtKCg!C}!qyb#$x*E>vFMTK6UjD=DR9B(nWKkg5Pl=7>N
zRU(uVf75<LH0}IFIN6mB?F9bwon9T7%4DUVw4Gly4498vgO9L=K8`I<Dj_g%$E2D?
z>mdfV-spM?B)=Vh)hVo-gwW>`zhAB0hn9o}P~%P}IUC-Pp`I7(<JS>mGa65mTE3Ff
zr&!Yc2yGUA+D*96={WyYOh6uxTW6fIzhr(@R(XFT7=MraR^V9TR}Uc$(cheB4<Q*A
zfMC=-pJ@I_oXvyXZ6Yj8^Le9OBnjd&qp~Qu`Y<M0UNvyH-&Jo`th7@Ez4DnF%*`QE
z@X~oR0&_;kiTuQ4C&f8KMa+#-QdjjN+nJO!x9#c7cjZktEM%q0544x-d$i)+Ne1eE
z`3n8};j7lwHj<rk{>SlcxkG9Pfz+688V}nge{Vr*83BW`4;cSyU8ZIo<o<lOsGmu#
zSv)lFsvoN~+3Ko~tcAXF6`lz4$e+u~8RY}GxscD_d0#vkm~Cj!WQ||c#^{X0#>e6o
zKe(B-3Uxog7>oDx*bVl19k4>*#x1m>8FV<>Jlv<kYc(xP%WE8#1nelX5O%aD!@e-<
zV&<$M=f5-_pe@cuMhVZ@c%yql|JLbDC&%5qN>O@*bYO@AXjc*As%&|H+c7j`Y&5NM
z-~=HsA|yI3KdYo&06zG?k)PGiq`0|HYxf2!G|nA}X!NHfEV7J_3GFYe<=<W$xp6tk
z2F*Flzjc4N@7tkMD1p6|KPR>7CeLMOYsxd7;&c6o({QhD8d$kRv+82S&&dIfGJUwB
zaai<3sN`p_l(5?;7dG<Dfu_&?&ao|6Iv&(O(<d8x594>)vBg2aII<nU+a+Z}G!73-
z<e!X#c%ZIJBw$=1Qp=nhS4XLs_A~kSchqwkuts0`{er!%V&Ho=>ZFs?bw!v_k&A%I
zIQ@)bJ|@dBIZwCnVP)2c&9}Y5C|o;S<N85It}4IpL1s?-A0^1t<l+;Sm`l#1#50Jx
z>_kI)YvO0mU=eu%4ZuI&Oy}8|;Z5p5PjdZsCN$@Z?JlSq7T-Y6qqXr}#4B{TnEy9F
z-_+dE;52zDkunQk)|L*<VW4F7!@l?%QRzmctSm$QT0h>XJ-ROeGva?qE_;BY!op#@
zy!>l{pI*P+8~8IeDU!~A(U_PW_Y^Y|?|pT4IMp;>L<k1rc8R*Vr2=!^$ShlHDz5Qz
zKh>I$io~)aQYWHVnYrybw#wmKUvYZif^Gt{s_6$xAB=uNkBJB0(_1af2V@`39`%sy
zcH)amKk3=|#WD+6?5S-a7F_N+Nm2L%N-QNbZCmkm_>O}B0O{$YmM9nK*#E>QxJ6ti
zzUl8Awp%u7BcAv@1!Q4K>sf$&wvlK6(K}tI5^vXWVZ2OT<taM4h{Y`xtEcQ^b6(Z;
zjUoU{5CQ~IQ~(g@K7G2Zvp+T$gWmD}(0xtOeDs=Al*-vq)VX?I>TTB)91uw885z$2
z74v5Ay4C`QIG~;l9TQv%Upe01Ezc{f{GKxUTeczcMHH2DTYj0&an$`ZuOOkcyY-S(
zg3(k_HnOWZ2=yX0r@PVCFDuIi{1PP+Dd06*<lnf7{1*ik8`~s&3c+I%ct0y9r@uD8
zwDb934q0h2c!$^eU-UEXkM|O}ODEX|4RiIjqZ^-(XR$*zEPf3ucJ5TR<lqk95uXn}
zXHQ%$p_5IPpX9*gnim}}j<u!UIx+g=>D0Mjzs1$jnSrmDw)DT#uFnMNqTsFg^YSv`
zZayZ6-rj0guU|H58^WYMRA%M}-Hre?&p_m{ZYJa+@@flvyuYgTIy6`gxTd;tHXRvd
zp^J`5r01gpuI>Fg@7R>cSEBeXHcVr4*9Xu`4UaYCDyB=Kc8|!y%R(Ot=q`kMYhJ$v
zOZ0m_q}hI%I<-;jt=B00a~eP=zVknQEV<!DlsG>pEBk>Vbf3cT#%$l|?qZZqWY}|{
z$bMUfMNUoUjYZ_uMq{&1N?wYTf%1Bb<RJxGEq5~Zj`pu$iHIBU;cr2HcgUI0qBo7@
z#+LET!eT8M<L%e)^Vo7gYL;6w8It1#mY_ihkwU0$Ce{rP>uqNP)Ue@|z^-KYfyXct
zE-lZQrB#4j+lso;d;0G&M~N1}KHhv5BYxKHZB9`{O$1BI5pTXRMP73#hp^%|sdRgi
z9oV2{feLh%c>uE%n?7Sv8?OB-|K3l_$mcqbwZE{KpHfWc9oT3z%04bX=QyCyNDZAX
z>Y{(wrN-q{IT1EzdeW4mhQ7;!fqk@+s;{l!K60sjb6>Q{=I2ax^hL3Vme$t6u7`?3
z?l|=Q6TqX~{{F&WSX>m=^hYu^6a_+r%2%#%GHnH3OQ|82HL;Q_S-M(H4xlYaSbt;5
zynd=nNU)F&G*lxq6~M=0PCW@!MRHOxuu{EvEo+lX?cu8BRsBG%d9-tK{9a^gQpH$?
z@caoVVsqg8#DW3HU<5#ZL_h@SDNqZChK%eAZBJJ5(=+tBI`#_%J~!+7n)kGgW(h?8
zE`eZSJ~1r{6%`v@unf^HQ$YziIm>2Y<1vwpc4~YYMw4OFCbty^UAa*-PwJ6)G(DY<
z_rgWlyUEGYN%2GnUW-ifFK!+Yuc&@lYE*-#Q`aSp!p59<cat-0nfNm2Uh#G5L?3;Q
zWz%HrBc_>5hi^;2nA){m&K26-i>(bSQ=<@{v<9t-*U<96POLLQrEe*AWsYe6Gcr{?
z_@~a1^XHt?Mnb7Q;NKJY1pSrN;-Q=ILgk+;iTdOfQnJ$rh8qp_+deVr@3Qm_$ew-L
z?wigkurk*tuY60k*KlMn!}9SXZA?tUmO5v9&yR#Q#dk5~uLM)0U!aqHk3k8zWHY_O
zU06|`tebCjm$Vl+SnXbWlSoxvz9ox%nJ@<Mn;NS@iK}DrUcpYG)Zcv&ZE{q;d)B1$
zvy@qiU@Jhb@V3oPEF_)@{<$eCAI+LY=ySQ2dyt_G6s+Yc_)YdJvHaN`!r$WoksZ2;
zfOHH-Yo<RgEG&Gobt<hbO}`r;$ubQ1Eg13iEcJokxuY!-!vZZi$Hyt*A<|sqb9Rqv
zJ=5u4SH|g$sp-~TEXKvLaq!Hp5c{Zq$04Pprn<2VFMyJO)u*M)?Q1JmQi>m>qL`iH
zD){7lz7g;8IgKc)l%JyM$lT?b0Mm`P=8uXWiM95Z{0P{hJQt{M-@d$M_ML?V7LCKg
z(6TW)qy;QC)+tc8#BKCky3dU9!SQ*=T0nK(qWIPOA>+{G9g>U~Dpd)d2<2$jT%_0q
zAdgs!z+Mitx&Y90d;^65Xp`c!CciG7<px^8%$XQ^RENKd4`mEYlK>#}Nx&JyxxFLn
z>&(E*%UWM}KG}42OBO7%VqHDRRI2}Pay->?{`?o_hB-<xqp=^Ci+WvFcx~4@S`vN;
zF|M@^EA821_qe>aQtHb7#H(&1LYgam$v?=isJ~h6S0SUYj~zWK`1<#Yq!26tQbX9>
z?>t+x%e%wEx@r8vfnX{B;I$6Sn`QXd3-)0~TYZ4z6d9>uZ(InD@Zo$crhEI{p&<jP
zxQe6tl^P6OWJz0)iIS%Bo#20EBuK5I9E`CeO-=JG=MCvd+GKtGvaVwv==54Xk+Q;^
ztl-gzU$$DT&`OEE4`CQ>lPMS|BVjFrljs!ezlfMKD<sa<9duk;_O;!PSJs2H=i<`(
zgs^4SfOaL$>h}I1@mnMshxrhwQ#sI?H^6utwS<MityY#u8C|_J5h}QBvGseZPgX(8
zvm!&zxq9Gt)~U?>8ky*q*8#DLsvdwez%^mM_tqgTj{7mLE~9GYQ|8Fs@CNJPq?t^i
z$IcHt*|9;)pN3Ptr9|SNr6Z)l)M+4dz4N~P9u$>QjSPAFIH}*gCwCP@|0cUWBa2WQ
za|HTLe5IJ1p7qNZrncpu^c)7K{`A+XJl}mvuvA)Zc_4`m3V@6A^X0X%(*3UjMQLr;
zszu;~0*&M*ZAcD2_JOgKmJ!<`E~WUp;3;fQc59zc++7d*tZaG@62!VmeRX`e*$Hh`
zU;A{oR5qnCujEAN6r&T=m>QbuA<{l0F|x_!GW*`7$hDZ{!Bi%A*=g2Y?ug4S^SY(K
z5Q;X93D;xGgDHs(><(qUk5zu6m%bhjmX^*>k1*KK(kRDn@)LLX;4&$kFW|fXAHCar
zIay3~#d_`rGBFq((^lX<e#9xE?<b`4bQ&3XgBlq0`G*BRP=sPZjwab|PXaUI&oQ|b
zuUQ-yi*4WYp-N!4nNap*cWmzjvlkEhxgIBMf^Q{hnCKXi0atRgqj*QWxtf@OwEt{#
z+zH))9Qi+YO&oC4w0%RF$GB39O;h|KXwdO8t<qZGjbPFB;FnTWb@e+3rlyT-`<0c1
zRK4Vlc~_$iS_YkBjNrYrZ_-*NeY83?g!GC?ucR&x-2;QL_2gm@V(Q`^+w;)zP+!cu
zSzFskxR2lF<*DzEV4<>HMoMF&?={%lG-b4L3u9O#2VsLjU&3`wyAkH32<EMDIAw=#
z!y+!%lnv0%_zgkMuhb3I=~=^#q2VF8JGq-g6p+#GfbOyGdF?7!_Rk1SdU^yb+uWpA
ze?QnQw?{J<yt~6hc*|LU*QxovMHunOz(;t$`EYZ|v8~1JiEaGD_hnCGTo`|yHPmZ)
zsWtmkag;XDtO5<yPi1+5zS2x8K@wLV;LPqhxFB(Inpp|XTr2Gd?@!RI+UK`MJwKk8
zf06heZ<d&k&mfm`Y$d}bDD~Fl7Xo3h>Ov#0?g1T0uqU@$Fy!5dQ%UadYC_tV2-wfS
zq!8j{H)`HUVkthosb|sxw1L84juiILfi_o&6M}Th>Dx}M<~7vwpQjmQm${quJfvS%
zE`5D-@t#QZevUEGV(z4LA})QV7#$<?kbx&r2z$H;df)lE>v7E@o`N^DP7^wQ=O1I4
zvX$XASFtkwqcQsnw-cYKp9_9mm8sMx%{70_`rPky<i+0`6D;@O+Dm~SPRU7C$p#0V
z+A?f1s;!4uozG7ftF6!Glgr1xAIG$teJ%<W!&Z3@liG@1j&rpDzqr}1Iti$vn@J*#
zr$IWZ$<PfB+Gn#N#ARPAedLwob0t-qwxTpK#=<9iaI0*#lj(|K8*pZq>*nWpU~nPR
zkiEv#MhnSiBa$UO-!(%uY{Zb0MKqpHc9zu)RO7NaQ8V<SgqGHqxjc{Q);iphA9d}c
z#bQF4++1jrOA2cv_Llg?F5wiT*?I${G;g+LGL8GvYKzCoMw8<D31y2;ow}+_AI*iY
zm)!XCTefw!RH!D3_BpK-*KXBUa8STR;WB^GkqH-zFb@Ba4G)lEfr-B;PeeA}1Q&Kc
zb|0esaPj5Q8s9TRj~ZnSSS-$~Ap(KI-az6k<0!E=QUup;GOr!+n|>J^3(^`_37p`s
zdW}0bBqAGGvj-9Ck8aBlBTELE$O1DOw**V%TcG)lZE7%Q-aB_hHX3@>T0aU@b-8g!
z<R>jaaU80a2Nx-tXO`gu%@Ur1g2vpL7{yVk+1WG2%Lj0&U=62htZxcM+zX<`aF2e<
zs!q$+W|f5q%E>^<Qn_2A%_ycOIl~4Un}YHFzh<{wE=#CrOGv90-8kI~w$vlEn`{^#
z+#nMYifj=+GAeTP@2Y5m*G^x`b=OK@ZEwc+Zw3}nLO{$d-BA~a%@!>TZcOUo@7IiF
ziAeMCl>dJ%z$C+xT1{dqs<_D<Pf<fOXV5yG*h5f|H0{^FdhQgnZT8U$AHf%M_BW~h
zG#`6+*8ZYAEQ_5}<MMNk;<^5j)7G!4&AOcBhuAzkA~zcW4N{B3lats!_Ep<x4}Z6|
zK4;H}vKA8)|Ha4LO8eA2J{dNpg$+jz_uqZ3fZ&Y)L(Y#)JGWr_Ffd6}LI3Ax6o`ln
zR9eZLVvM*1iuLC$@a7mW!?-buY{8{LmcWP~+~+>7+)w|wic4v-Gm?!3+_kvl)~P$E
z&u#Sw1RYhfdRVeK+BTH*%h?Nvy4i7rolF^4Z|%LaCcrhB=L+hY7!30^8U)PA=iv`b
z|89@`3YIH&yb>^z9#3u3OV?$%WlTl%SM&X6?`8M#32Psv*QmaQ>s7(9@42EI@q^jk
z%h_P5*ULs@z91dL8`Y8=m=UzI9kZyz9jqB|^-Z8q(F@h1og%J-aGVsMC*ynfSQV58
z(cRa1t$F#*C?^a?Q60?j?~v=jeR#gs35X8IqCQBFnE2s8pUtYzz!!RQzHhKlXfF7p
zr%w!iAXT>+hG@dUn?_IfErPxWx9JGj6k5OX1OGDWIL(h68eW}rAHp=X=G)%MN6EZJ
z3Jjt)nW99!z$Vb|JH>nmXi0?7KuGiu+V}M`JUO=Ta2+spa-O*+etiV%4HO}{(*7v}
z$teBBRoVG--t}k2l@u|5q?CkYV~jIDp;=4L^bM-K7sp3+i1J8POf*t!Nz!|J1-`F}
z^z~af=<diy!Zze{(}zed;`|PZuf2`hf>}AThHZ}9BA^P{UwJ*nWH8TfP6es0uY2f>
zB!fsKOpnJon1``aDNeQ#WD1VADBd0K|3FT5zTd7FMlnJ(a$v*LgH9_7cQr;DuVH=^
z59A}Ii9Gcg5r|d1dVyGcx)zqFo*lzM>wv@SdYg7xwh_P9F`|5xPU+_?k{k^T@FL5S
zFF96j9it+m$As<362uemWP%(>Ll=o_uBXI3XFxgF1A<uIp=_y91dU(L`;QagZW6q>
z3Gfj|rrjn$u{QsT2Df2TmmT&c9}%$F!Pd!Z(`C_8Lx7^N>9Q2WS>|%NB!m!8^-}`t
zC|WWo5$~!^WB}>wRS0PdS-Tk1xN^@Es^4r?kR_UAaHkJeWXnTf6E~byYK88X3YITX
zo?P}5heS|z<p;yqC1u(3^((w@`pu-?VBUwYbi;ztx!&tTXOi8PF+NOtHAzNu3_vob
zq=YlnH^zu((W4c;h7h0Td$HLh<I>LCU4_Vv{I0V7r`(j`M48Cf6c8Ne&8Uz&xT2>q
za?HQ*xPW>wZpl-c0YV9FL3DFR!d<5H7@}WhuxsBha(!jmPru4p3&&}vWZA~B&?l}C
zX)$rJ5tgZeo2^cqWQ32v(1?Z%%*|de6WESaj;PaZw)^glbtnqUgxIr*y@0>Kwpw-G
zDGe@R%#ZtL9ND1B<pq4r=i65=&oUf%F;&P|aX-~ERwJh&2BE;6aGd3beMb#4D56^Q
zp5VQeK6Lz)jso~ShOGzCZhk4NhMatZ`6d45hrrbzRTJ2zS?$a8pItoGfSXBoQml)l
z>@D6==<{O)eC8Qgy}qYxzx>D$riVOGrYa>l9@U(2x73X@;DYduW!Y~VbBp#YFrO^H
z!3As5+Kh5pb;F~rTM-fCCx-{Vrc@>$A+o_jtKVd4`S)SzH>gh+!a2aFM)-$F?q$R;
zPKb|vBhdX@Ek|VhT_+8i%&lr}RC?+3)(8CLi^63*MCWMTjVGB)mfWK8FncAZ<<Q0X
zFA|92FS)*Dd7&|SbYtI8df+EJ6w<oV76o&B`q|l3$g*dk%RVJD*9tKdO#Wu1Y-fr?
zj~*!Q+ckl{%R8vv%JM#>A6Jjq8+a`By%Z@vs)v%UXSUazEFE`lx!<Vg>#yBMM)Gb$
z&Pq-T@rOI0c~}V#(pNSBTF_2&YT5Rs{M+rUAC;A&3xB5P3NwWAgPn}I(EL5bhpf67
zPxmn4caezxO2+TDc8lnEzoQ$=mgK8t>f|^7H^M!$H=ACR;&UQqAK;aZkh%3%5}2p@
z?OCAH{kPG*Kl!lxPQ!^^gJZ1ry!{<Sc%ypiHzR4gV~OfuBf;h3#wM6Dg6v1vINe-R
z9`#m|7(#q9kNoC=opT-8>9`+|5m(Pz+4cD+D9&g$f6Gc9U$P2*%z*8u<C3P%<c`Km
zK7r<`-_vRe^vZ8dqEm{%^0-vef1<U9<<nAVaPf!7UawvoPo8_pf26dxw^=C!>TT@`
zYD*IawgeEA<1m?7-k6=u<Hu78r@fB24G(Lv2~rc>ye!CnGaI^ek2I9wchD<G&!k?)
zolyYvTr8m{O}bR1mu(4rjG>o9HIF1A<?{Ng!I((aGr2B(Cf)LPD<>$S*@t!w3IA}8
z?AOadyNj@B#UK=qis6mee^y@po<0L!yWgG6x)YU5Tq_%z1y%Ud;4bN0u{dyS3Qk>!
z%y55N>eDQRdyjlD@;vphR-+t&Xv$!qySI^nv14RY9;5bry|)81s(mwq?jPf}AL<L`
ze8CUn`?r&&_5a+ag#s=Elgn`7hCzk@6~#%DD{@S1*zZWDv!CpZVDF7WcZn65X4A1s
zUE|(|uc=>ztWj=#-}<@TepYUwPhr9GuxlQdA|X{(e~G_)uWlGkk)TKLpG776xSby%
zD`?f~!CO8rFS>shp6;%Lt*qS&3f5Tmi;CYZc0^I!4w~vBrNaYfDV^HExB$q)A(Y31
z=2+N1B}ZC_-4BTTFbRq(&#2zLa;J9OU7Z@t-~9KNN5Qc<zE5(}?FdVuxym4G9^5X-
zXP2Yk0&YQZ5+2USjd(q?mTdR!F?iempe0@G3a(t0FPJL_<6K?NuKsv7uL*Q5EluLb
z0JdRVAm{Ii#skq?w|jw~EI|qb&J%EK8#WEvp7iXq7k^KWe_NB;Iecph0AU!+al@46
z=lU4VqOI*NJ*QyzXp9BKX8@%tOEm1$hJxlx+_mfbXr`0~Z+RgN*sm;A(SO^;uRuQU
z{|z5zkZ0H!v#sN}E|WQC{;*?yYzPrccl*=2{VM6HD%7gF3%XV&&?_8RrVi>qVA+7|
z#<r-CiN^M}zf81<?jzviYpin}lrZtP!MeBG2MWwXO7_n#Se<zrAfNr6TF=rV-8-uB
zRzV=*Q1wyJm?Ius8N9&f=H?#5K+w>nFyGo1DJbYr?&r*Ics)m;TH@mvz%MblHusZb
zR|!H#uKrS;UQ4R@YEMP%Wi9CAf6HMNB9s;OABGqZpmB6^l$`cq^ZtE8;Ky6&E&K6(
z$Cbd!Z4%Rb!{EMTCwNX)0u0@RyB>nA)yUtSAp0HCv_FdA^7EZu%^9-=*%PXFLQ<fr
zjz%KxNHTF)F3aWDV#zAC)-$*g&v=p+#-2C`se{;?8{Qm$$L+V>PcJY~+}+3Tn!Sxg
zlO~qEvFrtp*KgDJeZhb1fAf2DIri?P%D{0zu*<E&d{NQVe38E2j})h>Y*0Z@F~&jv
zBMOeTX!XDwMuFpniE~y#jh2t>_RWXICCvGS{So5xB$2jaYP4V7LW&nkc|UvCenSeP
zr48U*=C4%zhq$M;EF1%7#ux6&rhR!CFBifF)|K9v7(-#yk2v@HiHIjt+10D=c<^zn
z`tr}DxVi2(O&g$LdE8P5Sr%PWu`mjbZ0PfSzxHUhaEuGH&s>*M3&+ou$}Rh(r!85b
zE{8eUr=ztbw?l?{<~O7F-P`;F8V2tHfHi9IG^%9eK7`$Qq5eD+J_Q}ZKv66C4otOe
zP@szGRvLwZeNfVygOL*H6=&KAv3ohM|Cta8$Ct6<uAe0hkZ!CUaft3T;N|E^k`OO}
zxpC)Pu#>L?-h1%(|Mv0<4hZAv==<c=@k}Y@q|%}*_4$qM7jyDYPfT4lK}+0}7Lenz
zk=r>a9+$;B2%I=`*fKj9<m{vLkN9=>)!|pHG-bEf6UEfe)zxot0oI%HA=?YF#^njS
z^SbLP99S@dll!*kcH@Z_pEav^N&^)qHQXodpDpEDpOvpF%nh1T4VEz~*>9>L1lsR?
z*2TA?!i+?N80jKO0DIsF?a86;nA>W?{dnp_F<D|Pf?N&i5Z?K364&qh0ah`|&`}@K
z*xvc9@kqKTLYGEDY;=O`v|>1d7pObb^r(PvGZ{(--l)F@7H1yI51td8-!e16Yp?Z3
zKU2bfKl-HY2eDWqm>mJjI)0lXza#&amuc-fcje3hAoo?BK8z&(3*hVbJW6k<clCGu
zk!9@^EB!DRrxJtKaD-%H)hpf}yHl1W(LmGcG{rHuLEr2h%1Xtxq9T=S=q+kVP=TP_
z*|~+dt|J4NqaPEpLlWMwZ1g=p%G%1f!ZI!VS;^bWJED6&aCe9hPnj|SlMLVlh%-!7
zw3-_rJ;&ddohq$n+h)PrHWLV34P1TY5@0%*5zj_D^fqSOBObON6kF`6pyZWfBi>|5
zcoZr5+31PDDFFJ(91e2#Qx6b?C}kE88rbgdz^j&oW$>h(A*D#I(TXc^qKUve5W)TI
zzk>sycdY}~M*dI5ofVDmk1IF7X6nw6T}K85J#B0naoa2iXUeo`B(OU^45o7C+&oSH
z(Z2napi8iK<hLt6xlFd%T@?VSrQ3df<-~N<Ht`v}D-4H5`OjGd{ySUHS^@IKBEv>j
z8$0R2qA5J!bUlCiN&TOH#hyf~_)}Y*VaemUj7JdD?`!cWu)N$!6Fg$Awpzxzm8U1#
zehkuas@OVYZ&Q2tOX$7Sy<;xJ!F<!1ALg0x+W{V3OX?HYi~*<O{QS*8`p4CzaHivZ
z0R>tDd!?Se;3wVj`FHZ9!~j6p#d9tGKX}A}*4}1k;YpvTn0FmUX@9tSYGZos#bcr8
zv+Gb6Pc^&Uu}ziyf_2Vm%S;9f;k+zi;|{g{?yoQ55Ru_EWr6#%wcqC25~#@<b;xSs
zvxwe4asGbc)*LSGte__n`Fh@W?52I8_iMDk=39h7>@W0L|KZ6eri#7i3h<$sjQjqr
zcyQyvymvIe;WZK$SdqG(Dak_7Jdg&R0vbY>a3JDx(|7W4@6;KaBH_2JC)Q)X-;_)k
zyd3w;an+5PGNJof#HD6Xd{?#d5cn2b^vNH_!WKzVV{b|EcWGRUUmyL$SY&42e0lH=
zUz;&s^o!=^Jj#y}ezq9(j7j}M0gL{Ljg%Fl!vlK(Oo0t%bC%9ZjS`b{_-p7B9U&`t
z9ZPSW?M+_a9uR2pqjw)Jk3CxQ^cO}lHXVqz<0{$;j!%LE>i|S2dv~%y0`MTfluC%%
z>ltlkuq*sM56dWyAzmhG>a6)p+8ost;=-t{5U!+Jx+u3Q{I;q_N?||j=Io9Exm#bN
zzY%I+8g+W%-;;e$trpAAc-uhZ3I@R1R-)><d+y{NEU-E;0z5WdY#D~ENa}Z}b84+t
z!6*083<L)fE+}S|NPb&x*j2M4JG8Wquc?1%{D)+RusN&YkVp3@ip0%$D*gtAdh_f>
zk#CjGBBuf7fnQRJ9^5754Hd$pI`4pO&iB8&*qN5j5WDl_XtUjmS$D4W38hoWR(}}p
zeP@##q#S&_bm8z_815pbafwgr|CgO$<QxO;$27j9Ni+X}CT76bf22ysjN<b&N6y!Z
zxPIn>xaf<^LH5?K?6jX0W#F5S6~c4VCih3<j~BL)jUnT<@wNw+_<PeuNN&LYoP&ex
za0Mr)m!~Sr$qbBs^>Ndw{T$6xDHWQV4qU%aDGHUKSjKgM>{|z~Tx?NZ@N&|m%r8x(
z>OCPj89$=KIct6kHlEyPE-#?|E<&;(8z~U}w;0{1DRyZ1N`iZDGp}a?Yo6w@_M49K
z0?vuuRh`rd7Eg=B2^f5pQP0*x+npXa3-1^Q5-EpkUd!27d(+km9Xl+!e^kA+-<~KD
zk_0atGo-MPC+1cp=)A<8npwLQ*EC%DrO^!=)ZXVd^OjJb7P@inBDC3=tVg>TYqLWT
zKvrnD53Kpe`5%HeT5l3$dQYwOON?C3f1n+qMcLPESrU5}S2EB}GijB)#q+boM+*z<
ziR#aEdvRk#AcZNTC37N2G%2UH6U~q}-*mldIw!!lsYcFQv>24B(pt_geB@RB>uV!h
z`vGm$SHbg++uz;G3w}=z$3dgRswSr`tlMf(VGF00eFKrADZ-U|Fm6EOk8FMb>}g7w
z;<`#+Vx&UabUuTNgS&AA&0{gu?^jMOV>4I$1Tibn7aFfSCn7Y2Te|62MOHb?GB#c|
zOgfk&N9y7@?6GY}*Yd5s>`!^|;c5j{3%<H#P_$qGIJGx1yU)SYEQ38S5qkBMLo~a=
zY*-<}5}EB)=Qn<<c*$}V0xqxgO4n`xXFyxyJGPZeaZM@Yb_>LI*VeCIGA)SU@{+ko
zi%LEJBjxR$9E6<8@?{Wt_9EcD#ZHU$QwKWB?WaH|`8*{ZjUDF=c@N8$Y$#xf&;g3|
zWlpV1$Z@A^UBS!e54z^PObRan(XQ?w!J9ldeZg^pqcz%`gc?O6Vl!o?soXn+kI-k*
zM~<NR<9vvfs0I~_4W0(YkCNJIQyW)Q0WCEmRc!#sF9t8GqY587e$fkFo8v+I5^P1z
z)wbWxi!8LL0sXFbG<XsjVG5)zMBaaAcn|Cr!#?lW(L%9$#Y*S(8L$CNl9UGn`oB_0
zF;fUn@(Csl0RkrU>^WI!HPIuomu-OXMbMG5I4aX@T=4<1D+!;0DQfUo5-vqgeq2Am
zLZPpmcvwz-u|aTIqNDpU^z}ogDOv~u2>K&owuz2l5vTp<(p3j-9Oxs?3K}(1>_mEU
z$j`P;S}80Iyo0#3dctbur#F;#GVlDmK&HB!&z5}c00rgTM6xJ~J3;^H=gr~g!975K
zvSyKD4-vsU4`BffAj^ax6^MRtz*&y2{v@Sek`sjS!7eQa;9!>Z^e4m9#P&(xiza({
zFV+I&(tcEc0mfI}C+57^5w*v4zs>nnPVBkwWfs#9upFs?I9OQc$@HJU5zF@mNQjcq
zcj{;tnJ}l#lPPO}`-RM)v-mxHGru|AD!8^!iKV8$m1iI#S<e@bYf_Ad!JJi_yawL9
zl__4CG1~mwno&)D;{ij}R}XLPlt&<48`F<Rpn^EWq@5Eie~Xam<}QFr@#y&m_K3Ge
zOWWUSW9KW#iOI>{X(65v+uO#leN_4uJAUr_dE5<b9Jbp_qRbWFoUvb-Z+Keve$zA8
zA8Z_Q59T0~M=(X(6CJsggJ|_1{)1Q9hyE7OL_q<E6@OEJA%n+Tcx9Ykbr<cEo;=t&
zvtJnVEBFK~>1?TGui{u77g|&vQvPu6^@_L`)*j5ClL?|#+T7xD9ewd25oI}?&GWFg
zpgNk(o!qW*<SW=cwWaeQK?8&VrpR3ii!a=+*uobw$&mU_D@u0#?m`g}@0;cS)IMvt
z4>wNOdW+`<AQ0}a3Rv9CoWIyE7YWqdL_jQf>8$rKCPg`q>VPDmKTpD9!#Qcje!2Fh
zl|P<NOYLh!7s^x7{;8a7$uY=`_T)w6RC50V&-<y4Pb#_IDPd493(yq5Q9#+HU(2>v
zzzQi0CfA*LBJWxW+ad(`-@z;jThLj5x;NbCHm5z*se;uher;f6TO*C8q*Pqlo?fm0
zr+v%b`tHBHWa3PHVozCymlutY{ul827knGzrm^E|-hf_^lO9(pDa-+J6+P*L>(YZh
zTf2u>lKSHJw2=&BKzTT`qi)?0i5>qYZz|B97ID2C?e25i9X@gANw0%#<=qBdHs+r|
zx^HO9<Cs%Qj*vuCzCZco7OZb<dc8ho?u>Lr4!s(86F}IHw_=K%|BAXTa@+@kzF*BY
z4x*XP0I-iBG^DFvi-cvHMkf7t-Yr(LTRit){SmhIwLRX`t!Q<$NY9AvC(tF|pYB20
zxIdIXG^T_A{ywBb;&a*}2s>QV=<vSsJh$<rD}Vz2>UPfNc>CSTGY$globGv2mr<{v
zgui6w=$HA*JRby@=XZO%t<C0UBo}Dt*LY@jiTlr<_Fn~@_^>!TqSGn7C-L7+PEPk+
z2i{^An&D=SXMxAFkx6Q*M%Ic4zxjBwzcV#{r=*Dpq`|M_m9U~h*;;SkfRj3UgMXjb
zVH_M0j~;P7Jl>2y@2Py0Z2lm6K-SjxewE<P83Lhk4qO9nU~x$eQ7b@|VXqM}7`LCR
zn*k-7eX5Q8)9oE%c;dGW7();-au6<dLfZK?+Lghv4K!r8n}5y?M`Mn7KqiOs!)!jd
zn`&SazoF#EuYyf&LPbHoeuD30rE<PlRi<2vyW<P%!v9WmtgYj**~k$xWKPT2{Y(0O
zv({(Qq7n!~{)PvRp`f09?OckR@p=q>vR+Q{m}A9KUEGS~Gvj{KG3sifJ;z#;{g&oS
z34f;ImE&T7F8*Wlt=BIR+VJMIJFZ`$uDvXkLuHB`(Ql8aPW?^<<bAHjLHtqVdS}Dr
zosY-Q{mwsiYJ87hAGPgbR%J`ZnK_Mrn%ESI?A729R<QWncu4&v)<i%pS<=ad)O|ID
zKqtUE>gXE{DeIsf1fyD4L<oU%?WW(*35L1^TE$#7!KOj1g4Q71Ah}dZB+1F5rO|vr
zHd?qmwrP{dmyFTnYruBC(^P}m+)ELkHGp)$-cj;nG1yypbjj|@EmzyljtjSw8B6HO
z+Y?#v%8$;e!}2E)Nt^@Rk)~|MA&oV@6R#ysD`~ic?u1A<+vA6<=vY`db_{yQ2oe~C
z!51LFht^u&8XkyFdn{Ot=ZR$iI><J7x%&A}018qdbx<I2RbzWoXBKQu*L^YF9daAn
zl9oUTWbj_FHF(Mky<h2z-0OI-{zG9y&j$T{T8z;(L3y?QAm%oh(Np*ml8pFAKq3s0
zdZ3QXN5!9&$*CIz`t&-7k#qOA?}3%EY4fLdG0`PB$VRi5=~ba~8fB^s0En*h-9JKp
zXJXoXIm6480SHPt_iQ%U?&S&F$JBwrapkJ3ZIS^gcNw?{b01(eRjp-)>SE@W82Nf&
z<Ytoqj2XuGKy~%2KSn$Ow9ogP)Ru046sZF$Vw<9M%lB_*v(<>I?_ZThyq54kt^_hB
zNE}%riNPz+60axMy|Gz%SEHt1u2$1+I^cb%Q)^?@JC+_v`cW7c{cCj%^e!>$t>wwI
zNZT~wxl^0l_?bfIS$RJ%W4fpRcC8c}i481-?q6o6x?)L1`yB=QVQzz$M~X^WP4J2<
z^C+Hn8ci9O<Y;`1)jz3cYvRl3$uzASatJkhrgdd0FS(7(rAY=`P#hpIPA^awHg=?5
zDH6dvPla)P*Iq^Vnq&699Y`b}01_1&KjO-H{q!(e5TDV<4V$N!hcZnW8@DiAMA7{2
z>4R6uZ|rJPA{|!rMTfa@ABaz$74uCz@bQzercl6)?}ujWFq3<f#CN6%Me|HMc29|q
z9d6+O27a;SRh^fP7}_Fi#Aihb5TwnCqmGC$g+#Y+YaoSeLnZnShT?!knNO~W#Zl>8
zzyP3;M62hy&kaTv*uI5ERV|iix}#r}jV-W%6o;_e1qfJz5+l;Ci9%+{B>Ymjcs8ou
zZ{{-Nt>KI4a8v|A)a~X2PiHKIICi!qw)HOQ4}Ut;>|l=R<ORlv0M$W@QA76sjr|Y2
zAy9Q&JU`uUlNfiIT|x9y{UlI_K{B5-5lj0cjKspOfu`y52L(<N`RjXdJeg+i7yMGD
zWAgTG;oUj-9j|G%YH4@tZe2noQvRSh|MCJ{pY?srq0uOZg>a;9jIOLKeX9Oy^!^f<
zB>AnfJ8!9tXgs&Q;E{;l>3-=*>XhCCk{Wl@&+FUx>J7=`xDO)-_hQxJoBLpDzH|Zc
zoxw4|jOwPP?+k6&zme||brq_kqp?GSJA1f2zOeuyT@JyeG6C_q^KUx;y{Tcd=PR9O
zwJvQdzsmyT8#ef+AiOkKjiTm%Q|`9{RU{2W|NQScwMO5;kUN^EDmS^a)#PG*>dB;u
zx9V+%#HAn!d=?uO0p~n3R82Dq+W;SY{2`x8v?kK7f+u_+j3i(G)w&-c)pa(yI-uC!
zi65vFD=4?xuE2xfLf8%RHX9kS7elP68fj@$)^I=0S6jc-lp+<*o&%DEE4MusAMOQp
zj_Ee7cVB$m|2T8qL5iH;%_GuwE<D?<NmcjnAv>c?1O6}orv1_{W~nhBS(bTz=rEO-
z0a(hH+{#sIn)SS<s;0;`5s7$Cd++xn_-4(x`oJK~Sj;*s_^jRmdA`4o4B*7uu@vHC
zvBy-VJazuYh7fLjlF20xs`bDenw$6y9`2Fr+4KTof8ZBeU`E{r@v(Wa0M!Err`czR
zI6pP*HD0QBG-Qn{^0kq7FL?Z14HTHEh0l1|cwH4my{Sc8MoAI*C?`uV!;dsuh`HoI
z@^6;SkLdf^G*3Mi>F9u$NtfmEcl+Gv)Qabd{tNL4<A!-;j_P->L^~&NU{o4ViN%P4
zhjyzXZoQ!JEt!?O-Jl0=%cUl9y=(?SALISXu)Jk#F2WJVT7NG6mM{a6U~i9-*l5Mb
zh3LW$W3K>*HqAcb+!t`cg9{e0@&3ys8I*Flcy0N1XW!r>`=kFPA|^Y0a{!c)F8b{?
zlEd$0;Cf23Z-nzl^fCW4f>wr%KGT<G<FinfmNs`}$-7IM`SzdIC)Xu5YEtMB0%-fM
zT>betC&jf7Zm<(6V2$}MNxaJAxANnFrSgzOKxJpUu$E_0{QtE86cBUBW>f2jhriA5
z0Ic`&=oRknl@F(%H3%tfgD%antsIr~8)D*)88Ln`wziF#sn1s++kn)n*c|pot{aTd
z6=JB+b9t|G0yUKwn_EIhJErh9c1iu}(awn?Z?pJBcJ*VK8V$E_a}GFH?j^zE-Q4^U
z|DL}_ZiT9}QyU<-Q9-Qu;6AJTPn$OCCv#=FVf9Q)p0Cq2KkS#Awa~uvp2?_+qhxPp
zq^12zi|F9Vj{m<eXeJwRcy;|dA2q;4vFm!CJn$xU(DyO7vX3B)jiN<h2FdRQq|qIJ
z&0$f&I7-0G2G*Y#9j(Y1jWp}~LBu--SdI(&0_gi!E?X0iXe7AqzVlA&u;&YljE*Z@
zz*S*q@mS6Ew~~2}8|?^TPc#MaIkRwN<Vr;)>H(|zQi1zGix)`X#d#Q7;=jhC^Ua?>
z1bq=i=@hC<{2=)tAsMiTdNn)8%PimCM#2LWEH!7?*Sp#9JD_KI{QhSV-*_XJfCK?>
zI-Tkx!38(S-h9ZZn5#jm?kq-6^v~PCMH*X?j(xlVzuNYt-)tcH;bs_WZ~ke98_;%h
z6yOra5&|0n=|gHl&7A&G_V2{*3LyJRX$l4=y%s*HtpnllcMfrO-oJQ0?K=m0pW`jx
zq<?i@_Iyn|*1oaKCGNyL(iG5mLPh{up3`In%<PpFw(~c#e;&#&Ytmh)SPG6l<hr_7
zm{FZ18Ckuq3*M&*96tXkpaGlD@jYH@{k-?9ogJ;XR%K%NakdW0+4x^j@gV81Gx(pO
z&oq$kC`n1?y&Q~K>xnsO*)dqYo$5Nplk`uKY;TP$9^|p4&qUofQ1rB3x28Z+Ib<Hp
z47km?v=s+jgHq5U8>J?1v?Xa*mJ~;tJ@=NF)0JL(>AW=wv@cYk>9^Q~_Hk8tkpn?9
z+yxbjvn?7(<~&HCTTFYjHe`psm#0<|?k37-Kxwd<d9rK2oP;4a@9Y3J%-4h|WAuLm
z>~<G(&c9xX0=s;|FnVt^v9;xA@_cLeBhYXl-Fs=E@!XonS`S0+*8Blca##Cl$?pT4
z_j*3x0cXttYe(-RXb#x^XvM6>RR|E2fR}hCieG7(?_|h`@@f&S(7f;G%5=$XU3HSx
zu64gHQ@)7ZjX^H+3$9qKGwwvPo0_9C9?xvw^T|SlZxdy?v!03~8I@9$&h#OGowf>N
zAE=X6iu}tYPHUSaS(aQ=k5k-9lp-+xh4#u)G~WO8@h^sUZK^T}S@YGGtC=m2#{841
z07+CRaTmClbS|`i8V;AYTtQUkZBCKF;Hq%P_wW7i#~pE1&#0hmf8C}X*9=%`8T4QS
z1t7vA8kY=)%)Yi}3(#U2b1q;;$8<OID;!z+?s)KhabSV!ZD3<buwu{4VQZiP0=_%?
zMQ`9Fg;u&n_$B~1FduJld(}wFr5R9?Gc4c8(S9wWBd`J>PzAh3{POCD=ixK2$E09G
z@B_BEKQ5wj0xUa^pA?}`*`XxIpg`wefS*2{#+=9FLt_Oj@ShR>lA@S|4^D(kNQKaF
zutdI#EpA{c{w+^F-{pzU|MREbA{q{Z1#^cFuEk)2^+B|javT=(FdiYG9t$!_Idvy(
z2mu3=80>MSWHWi8UTUQEornd-KoCc$PX7r|qQ95sA}b`;IUe`)rEEG=E&D@UIcqLc
z>GVV`B%1YnxMT#4@ww>V!Q5;SuMl*LX4wG$)zKH6D^;E){V!gDJ<Mzms{Pvs^BE`b
zX0Y0;Z(i&uzEvp7XbNlOI|^@Ao2Lqi?49D<%ruLO!yx^;c78uJhVhy3WaIj_+fV%O
zsZXe$*ju(_kNxZ{i-a*p900!u)bQrbBfm!$J&DYjO%tJf+MEq9#;6)wJ={W8t{~N@
zw>Nfq#}t|C(@5y%`2w&#15^(vb?S=$4En!%yE?^hNP>0I0icw}4s|NLeMIzFDrQ`t
zm9~h_btZcrhp)A+UdEuvlE2Cdg<vU>rmPCc`kpRQ+%C1E9Qug=9Frn^d5G!a+TQjj
zW<&kYjx>G9ie(^#!kkT_T{`Fw^fP+AbjLOjeG_5Z8B-1TC=nk2+}HV94b?j_<13ay
zA5$B0w<cZORA;iGD-8yF|BO?YUk||&^o!DVf1Qx%A*4PCz|BAZPGKrKc!69%mZYBT
z{aa;K+7#7V<q|=NL(jhJxGZ6?`ruJXjRNA5*{=ty*4Wd?R+}UgLBy5SifemWz)*U9
z*j5BGTI?|Y{d^uTCDN7E&d+P)%_=B=Zd_}Sik>L5-{kp;-HBC%qVRdhF@lBXB;azh
zvTfGdlI7yHwWBTZa9lH&hqg1K`P)%PCnlS@&~8e&D^L@?j#Vvc$X3GgW-<}Q>t0xN
z*G9hjY=ND4iHQ#J85wFrtH7$_*2AirxfDmUKZxN$zUnh@ne*=-2yWD~SAP&J+YdP(
zudDKJ$l<{<MZJ^<PO`O;38J9cjg}>T5a|!U<5*#;IIhkr_x!cLw^RR*rmGBy>U-KO
zu&{J@hjcg6ASECO(jC&Vgfz&~rF4fNEsfH(gdp81u}F#3Dj-P6d-3;w`NW64_sp4@
zGiT;G=g!k9a%iHuP}oxVK{DVV+7tJ!i{KE#N^t0#(JA)u!L@nCA?=|}994{wj?&2k
z!iNS#9Rt*NBZ#{Xw3V@&eWzZ9$0}NZ^=&5Qmqj<%AFe_jBtblSwOhJfyW{1kfBZlH
zelJg|G^jZ5uQ4)+`-JBS?vutR9iieN)|yKXV()J^S5=!d^=j7rFuT(Hwx~#3H5+wf
zh!KEwi7j?{0~;Nw0wPJ^h3cp=Y8(0OQ0F4HPr)Xt`Q|?K*M2DDaN*CppZ5ei)DRPm
zFY<&M5Qxe49ZtpVq50`2>t%P|YUTtZjri{3ebV^{=|j%iBXC@YY4&6vDQ&7yUOA5r
zvsu}ShsvGp7t!d3%!F*TXDizNVU11#opn6>wS3k|WA*dH_`B;2c;LO;v%3|}URy?|
z<G52isOORxIUpVDBlsrDCE```O^0I3l>hy;Hr9r$i}uY*+&k}IenG*+Ctf&6PF$D7
zcb2NBJC}VgKM3&y0o94~MckXrkDL9AxcJ{u=3w_k#)DZ?V(CYTJ}1kXrwW%`r<cy`
zfj&f!7Jtzp6pCVT7!*A^__*fJc$t}3oHdu0<i{}G8=yq^FH=4T%e^R0=9|rX!TKza
z|IpYZY{7pZLs%cBjbxy<V3#2nZ7f$|CW5~2+(JHSO}S7s7<7eh5#?XPqV(j6osJ+J
zir^-frROw^c?YCLrDZx~KkLFmMFuuU)ezPastYgb<AdxmeKdqE+;VWpZv^l&4`JHH
z#XHQ*%n{}(jlo17TzL-%iI2T_X<)qPzZwog?ZXafuM|sf(V`pj6dK1@1rL8eOUZhX
z%FOfnNg!??39wK1h=beYnvBHv9_+%qt3QLzgJwhZ*Wh-Mru<IlXz^0=8IL_7`uStc
zQ*@tg0rEBwo(lz_aB^6k_dUnI1gw<PHUN8|M*(So9md}#X_jjeyf~e6ptQz2Z5L=A
z8&pb*IVRl|00a!?RGA?gD&PwRyg*>Zs!xUCUl@hTN^D?%4C5a<GCsw1xF)<bxI#R=
zka6UU5O=W5SOoe2H)#-39&j1{J0u;gWd=**xKg8g3=;$|!v_j>E2ip&CgU!Yg%<>i
z8Vx_R1tldXM7Jiy#@g8yewhJ>J?OIa&fj}xI|9*-4(k<wdiczY$OC<J7}SzLs!C+t
zgwuFdj>TDA{lG(NLu#nU%ZYr1Jb-sZ+@`G-qgA5+?k~E6^MU`855(B``4_Lra|~?G
z5e2%$O0^k%7`gox2Fz?$*bNRk1oD7~t9M*R@vmq%Ff0Z;KR%|^{D^d08vgvI)e0H7
zD6CJs?81|>;oneaam5P<9p3@Z8V{g;4U6a&V%7$duCmh|zDMy}su3KYr~oiXYRil*
zbw%bomQ~hQ4N!NrC;qmU%QTh=un9>GhGm7Q|ClFzIMk&2Ab6YHCw#s_u!a$gmYv^s
zT7tJsXQA@Gm0rL|-N8^^u5ul?xy;Ia)YrtbFRA-4W{#i~W1FG%Vs21A@fafrruEMr
z=Oy+-UtYmX>ZCWqdi)&F2(FhpLUO6hM2iW~NX{S>eV`W&FXFrTE{d?wd2=U*FHM$t
z_Z__W>06Kn%=h@}!5vNLkaim%Bgz+(0qxtxAar$yUP>Y{zjre&I5Fx4O>L7~;PV~k
zxmn|KhfS=W0XD|q#p$xa%lmIGaZKIuYzS4jZ}U)?OLMg0jjO4l4vzEyqJxSBczuPL
zhE)KwSuv6K!^9gCcg+!)^3^7`^0ne2=3gd$1zEM`0{XGjF>&k86@JK@a=46?M~w<m
z3gwjtG2+1%zFG{MKgH%jz53n{Gki>h=%tIX|L4qhp=us|zqI`qddlwU8ZwuDW&S?K
zBi>GiBT*ZawD_2rg(_<>EpQAfB_2*AaIqJMpCRtioENC?PUvi}LnYG@x;4#5=oa7t
z_`|f~IFQVlX#bQ=#oDi-PBnH0*M{LYtM$f#rl{xSz~;f3<OndLG8Zt8osI-Vn&Ja<
z-9r|irNC;W9MgO&g(S{R+OYHmRo}R%nYfcJQf{EPe47XX7GcVGMt{R-`Et>JzvnPh
zE)WbR@i(oV*(Zi;EH-?UdnaPlM}%;iF7O3@jk^@T+q&ft_u&CWc$w^#<~sCC>-$Z>
zjBy>vpUrIMo?RbJ8}zjq3)otPR<UtBK0FiwF9~tv5}NVrL7SLA7MMf&!4~2Tp7Mq3
z(<rx^c?y6>*(x{Q&U(iE!!%wt#GGT>EdHWI;(ULHYHwUV0T=EYg&Eh27%5=;gO_Wx
zn1CRVV(~HhqVHwfe?AzUY@sZLOoG=>2k&mQU=L`Z-BLRvxjI}%L?5O&i!Ys|@))S^
zHGF}KvFO1B^_AJ-W*!AE@b32%^{DgLEKz=ZQhyLR8vGlTyFX)ap7y~n+|2M%?=d~p
zJ}F*DRS!0CnF%(G<wtQfQf~BBFV}B*0*;i!1g;UOE3X1dKn(+I-hpVYmH4fCSnKrl
z3dJ`8<mRFm4{wVbShZ0a&&P2t76>Vk<cOaLp^L(YRqiY7ir$0TRWc;OuZ|$I^=C25
zkd;})$Sk&bcsG@o<dBAJmcBC<5a~v|ipz}9T*7)W#442oYc}Bg;bgR06%-l2W+J!5
z9k0V5@_d(O<XQept_^O$VNub!)vW{Yu7nz}Cd^NK@iG36mHUgXTNrKVn&I%5{DbX$
zF1Z?>|0`g;6f5INWi3EG7_QF~e8w<v@2LYbU*v{<KN_I<Q8-&p3?%m&$8e$9(zkB_
zr&W6#buNdX_K2&iM>$+zX86v5gg^z%EsI>#jf}A8oxS5?V@v69T+u8D^iy=aA8Ok!
z)rM5ojp^_FMP(fu+NhOHNX1I#kbVCc?#7`eI)5zv1Hc{ChY#Znb!C13`IqvBVP$G5
zng@dHt!Xv%l0GQxT@<&I+KrpBa>CERnIz|r52IuZ7!{7*M+8niB>Iy~t;9>G`}znC
zIJa{O$>T6pIw1#6QXKA|nKN^a5WpGE5vcr<T=?vOhGxxSkBoS#8-hXg6k=abktT>%
z4Em|s@%(-lP6kyNmzx~$`%g1*TO(^+Hvc7v?l}5e9av)9cz%}HPd>IuE~4B!^YZ4L
zzrW*?gj0s#KM3?DtT9nN!}ebsVwOq0L=}6X-YghQbcMGeaO&z!ImS|2iLd{1IX`&J
z6~Q7eAM_D0oynKR=&8YOn(zN=H19ra=P(bHP0ZY;@3nrYa*dhs(;p8^o9|qi6ClR^
zDA_eKM!>RhqvW9gF=%Z>X-_;vRxb<1jCq<dIY01)3C8OvKZ;6`Xoi@qnH+WguI*>I
z0yJ%yfZG8Q<^dbn+Je@<j2nmWJoDsQGMXPvifV14Z;5(f6O+4gs*QjyhA<iv7PsmG
z#Gu)?AU-iD08U6c<Nr(32@Wy_BLx4_3|NA=89zpc`pw}Z^3<1ei!1Jo;p;`k-hlf$
zaS`~#sa2{tpIqPQ8ru=Z^9?l$;EXMW1;G%E0WPXx^=L^^TyYLxS_I-qCHSo0C8;kW
zA0d)v@Du^P`(&d0<<A`EIT9c0Jb~!rUpqxUl*J*%wt_?QRgJ;b+=S#WuVgo)w{9?c
zC~DN+1?;CNOHk(5!H+TfhX`BvqA1yD20qvA5~B%+*;qj$_M-vge6d=@|AgY^KZhxf
zdWcjxYX%W@y>G^{h1Pz?)5rh88T~K@1(z>-d)?Mno%Ly&uupH2)m4sQuyC7?)smF<
zIxJ3-lv1e}#*8xndv0!Ni`>i9{&79JM9%@l_3u6?H?Va$eHUtX3`~jg2ks{m4Vvi>
za6Z5~zczeKpYcIikXndQ+^?6m@cJ;17%s`#QwKyRQi^lPI@`u7HL9@!{a1>=%r~&P
z<%dyVt4Iw=^_TfkD=)0GM7B&|D``dh+h)aDcYa4!LviBaP(yLJ;3^oOL@@a4YGO`S
zAxfsrmCK<maIf!l;wGNAzYPP85?7YT8rPcV58U614=M;H+L%6vE*bLWQ_j`}JfwbJ
zY?tZv5*q?nzChor0TZ#u(SEf{HyiFn1A)D1s66B}A7;(`08D5EyYX|n$<@2XUrt>X
z2p<{zRJaC44`q#=Y5jU72m4M$G5ky222#jMWFz^!%3o>ztaJ7AcW*@?um+|aU!&G9
zg~;i=p;)}!#>=V){lFE$5H97Y1ER^7sL1n9NGD$cj2{hW7HOmg${0dkSSg=Ip6#e2
zAEk~Dn0`A7?1Eavii@n*cR#!k!-&=+J6OZe>NV9sst$O;nHms~YKM9EDooOUEHh&p
zZ}S<Zx)l{te^|HRD~?jv`-Cs8(J^6DXmitMo<t9z&~vMZ=3s)cCZv#!Gx5XIgEk3*
zoMSp@@4$btDFxxf%C!DOSJ(~R;)~SBW}07&WHE2?bUxGqR$-<Z{Dv2D|G_1It^TR<
z9#AgZO&@;v_4g5iM&7aLgaU=ksE7W8jUj(e>rEKL_JkA=+K5J;bcV$!X4L5GeX%OX
z9%q)XOr^|h7O-+j^h4=wjIKX-marU0@7nN>0%|3q5j#DeTP;^2urv09>QNUP&>X45
zD%`>Jpq`gJH$j(Lo)iM6roaiq6QKf_i+P(eFB9Oq#*AhGHaSUuebJ4GKkPs~#wF!|
z`y#psE+N})Kj5_~-kgI_t>NPxV6O$4vr4@{b(fRdr?j&E)RmR>SU4R~>r}UG_Ml)i
z27#IV3L~wDx2{-Ap8pa!^BzN!wdsB}67=RGex0nuCK=F#kOXDXGQOFD<wINu{X6dg
zH;w4(H3zM&>uUk|2=E7l<)-{c*j>1^hx-nbAlV4(Kz><!(t57$g=jKBA_dQ~J@c<m
z7DxeaP6dbSmxkfNrO*gnD{TIZO@8if305O%&;~%A()%1?;?Fsj%w86{aM`8vAk}!a
z-k;T2Po6oLJHHw5>cajWaCbE?iwNbZ2Ch9!L3g9bs&VwaM06)j<s}IhGs)Do)p0y2
zi0dvl>i}$GQ6m6D03#UV79$)V-6u%K?Xx)mx-jqF1u>$&@%*k9NTwEo5shwTN2s!+
zSck^M5E-)JOP)e7k5MyT%~)5S-1oEsHRWS$x-f%3ZT8~kC1zMHyaFal(Q#z5&&)<O
zkOr4_(@%lf!t4xDlG>pX*m#29)#P1>@s$xe<2M1ewC_3jE75i&bST4SX*qEEOQktR
zVDD)uNy4oqEvyJ69#;`WzF5M`?4Ueyin%Lj_)Y2R9PNnu*gV!mZ8?p?q8&f7!@VVO
zGKL#~2o)(AqT>QKrtUhhSiHohXkwBu{$aU*Rd`a{@Y8s9h;Z*C{=UObMJ>eWamB?9
zC!-*rW!b&OLUK5&6Q}&u+~HipS-qDF+NZNO)IYtOTCLC?nb%~KVP$2CqGYB6Rbl58
zF;}v3#9qIDUwLUUrav8&!NH8m9LuPj%!-N%8-F4Y9&Yv+i!*J5ZNyHfyD)Up;*{Yq
z5?wRL>&N)%nY8M+@tp1@i=flyMZY@}J<7-L8_LiO{1o-(jr7IA)|NH*71@aJgD3$e
z278Ku8XL;pDB{+}PdJ=i-7PCnr}%#I-h`}6Wn+Cze#cmRL|X+JJgy9x;&r9-Ei}G0
zlCi78<VS{mPit;gnJ_xh_izDptjU*L@w@Or+<aN|H7BRgy{cjdMojGS5*?>dP%zoH
z3O__Ko^8G@8eh&Qqd;xU(_uzWC_m&GK|#w912#baG~Bd1=Cs@`b+9itiMDSDS1&_u
z>9U;s$}f6^kPH}PvXBPf&nGOKKh8BTR9vNdb_-)HFgLF;{}>IlqYZ{v{N%iP0?s!D
zRb=(GNpGt&wdzv#qleuq{50JZQYU1Om8Xa-=9*sT_=|Jp6dmbCfwbiwH!ma`ya`G!
z)Gp%5+fLtO(v%ceAWW_ODl)J7bom9)p)|{UBGyTmgGQ3tW8+5S!q?Zg$?wzxpze76
z6QC|inaC60zPw3W&GdY>%@^_)awf^9y;g?VW8$T_CGu_G4EE|;xiZD}dC%=7_|5RP
zXKr5J$|olhfUJS?_08AK%KCa$0;`zPS&&1z{DPx5zs7p%x*=t*5>Epz(8*c!DCLCk
zNfAyz;Uf`>GO}Ud+pK4kI>Ns}a`X7)yE1e4ksK)ro`bxyc(>%0$(`Jgk%5-2dkI*_
z89DTXU3TOOiv%ix-&;}9%tnj<(Q{jcoZn(xPg~|QzzC7LQKty@#@-qu*&6qvJOOFg
z-^rGkvrG?LhF?^<KpDhwc7GPXO`e!&aeFK!6|k4%VASO=$lr6t{L?>J-mS6$F*^gj
zK+iUDw3!E6LmKfWntP}%lIsYgQNNvVL1MPbd7%TO9fDNTQ}B5%ZSg;FpR$!dB~`BG
zZSAk=-^zUr@3F>yB~K-QS%qMPsOx%6n|ShVRfodc-}U4ZWn>sFrYxlCVpq(mzkK-!
zFt9YhEoU|SmQF-e^ii`DQ4t@m_Nnm5Xmn5_*r4X1TM5J@m4sB5gWt8+e=86|8{3tA
zFR}Uhb%-p(T!ps7x4}5{2n8|swOF^yUV#|Pfv@IB-vo7Rqb<QL9Kd<M&mg&v_Zr`M
zq;n7`pm#9h29Ue6{Vm}<dOj2`3p)=g;3y)!N-kUW9eB~Q{;7R)iMZVw+w^%pBryw6
zI~u!10LEh0>;9{xU{Xtq&1x!gQc?k@Syq)d)SrRAy-u#Kn(9)9uli2hp^{cMFjo3Q
zyaJ--S3jq!uErO3^xvvf^}Na**s_|he6E=*5=STH%lcWv#*%cgMd3^I&p$dl#D;3t
zIwdgn^u4Trsc%{Tydu28a@lv?k>_APEhq3BHV5W2!}tktP5XT<(`!SPkS+UFk8DL@
zD83GdpCKWJ{6Sn>{WViSMM2Em@xi5amj%D=P-5S%S0liy3RqaLX>^@FT-428K1UZ~
z5fj^~M6|><_P2L0IL6rUUhb*t3;G4;P}US#fOqb#<R0Z>jj@uF(m>ePCGr@3x`r_V
z;e<rBtZA@yvMx@r_Q2aH)6#!|fsu9)Pj3Y)5p189Lo0)=$99Zfwj8qU>Due0cmL!X
zPp=A18eSEqaZ<;4j%FzNRy!DTADs1mZ3DzGd3Yh@dt?N3y$QIxSkhZO@}=2uib0Lx
zoH9U=ri2Ozy8XzKvye?n3cWMceEfU4B$|g7Z>)(@&<0&uS-I<C(bVS^URSU;y3uJ2
zo1Eqs;Vm5e_LP3ad%UZhXtDs&!7{1H;z)^hEJY9UsWAwuA8NJmGA>wi37X(`mV>Cc
zY3<uHRY6aixsyy**CxD9#BkX&%ij+n-qkA9n*bOw+OL09Oa9rJ-z#-(-I}aLRy=mZ
zA3F}#K3_%Z3a>57#^t>GOAELkLxaR?5B#5jlKc7ISMwFJ8J}!XdEBBcJ;KGmj_-vt
z;$|)p`J?)C)#q0F#4w_o6GjeHZS!KKY~-xi3s7A>VyeTM`n;qKdrq*1cl6}xA-3Ws
zQvX}_CHTdk*iweTAM;gaukS86dn2PIZtZELduTdry)+`kY8=BY-J2+Vf2`ztPfGer
z7nT;5G&Q9Lob3sE?~Rwz&0!QeMdgY2n=;Qv^}#R}IorkzwL1S_f^W5K7(8g1^7k5r
zkJL+(R2ruZ%ou9k*Gyg8NK$_ugGb2gJlM>F$k-ti>#$AI>XF=2fYZ)<l(Pc*XE&?j
zR(I94%}JQ&D^4-fkp{^eGLZW=5Y3gFa%8RDn@Tp={}+W>=4UMSeE5pPyO2Sl;Jd9T
zd|P$PuUIXyK1{7nXzt#sRT*^;@h=?eW9{PWeTrd_5-@fKMa00h!}Svn&zgByoVicb
zT^~cjr)lCHYzoewzX)~q;^8Wz6m=%uFYu#}imIC0UUnKVH$^myrO~=^G_gAl9=n7}
z?CzvV{v0Ine5|K8Q1j_iIu2i37(hAn=x&++lhP|RpE%XX<jz5u92cgx@l=A}yMI>M
zu=z;eM&~#ZzKDy~gL?Z|BFQ-9Zu|D-Cp2+1mz^Q({^z^G&fi9ihH0~i5C4Sz`t>VY
zCP-|qTuWn2zZS1Q_3`o+gUL@mY{<}7-1-nI9&}cnz3ho}uivvWiLFSIZ0P{6*(ADI
zWH6fRNoLz4CiWS40(^bLVeq3iA&d{A$bjS>R{2QUhSS6xFvwuL`TutT;wY|Ib1}p9
z&4{yAEz#JQ*z3yl0o%}8Uwr3b7jh!^eXc$_B^vO{?wQQRd}!9?4;(?n)EnfQ>~|`~
zMMD~+75uSF`m2>c`|sZQT%Hl=nwX3#Q<$fx+V+wN#GTyQC{uVPt;5$_?6Z8L{wbm-
z;hef3JCs0L+1F0d50wGq<VCKFqW-yw!qtbkB7f@2$y7W{g)dKS>byGGH3tsHwEwin
zCnV&EI1vv-<7tdFD~qnR;EDO*(H*+DQ2yOOKSbTTf9E+wl}INgwwuQcv}CB#t0+IZ
zS_pdcwTE$skjW}ex8_S$s&n|m_}6c=grVFGsYgb0TvpQ1wCH!>>hI03YdolU;ckrT
z#^_s6e#34D(t_QLgPeTc1b`T)Vt)NPLgdL-UqSi#suh0~-BLq4=HCs7Ox(=Pn)lS_
z{j;*&+l-71fS8#5(QxRTN6mL@(+yK52Y;-qT0bh#0k*<}3GRkmgo^8Dks4N(<f65t
zS3*$BhIpI-itQg043Z*;Iy{4RI%McujPJ#|GR323w%JOL^GVn=iz+KQYM1^5v)q<A
zA=wiT-}cQ0`hR_ma1eHwWaa1Q-`zKODj0g}&-N@oyUF{u8T319r<=?xycVt<vM{e<
z2nmPyC{POYyc~4%r%n^8<4(xk15KjP&y!o?8wfJ{|4eyfb3*MZGR3JEsAaYew(c!L
zb?&s%R&X*As_wlc4w8QH;zj9&iRm|$qEq@ozFBIcCGh1*=>62$*;!-FIZr~ctmRX2
z%Z(qA3v^22!oA7DK4^ZN6H>Ee{}i68q9EErw(aM5!rti|&u#TdtnRSY#?$1==B<I$
zm-?+xggz@#10xFC+~o%YXhufaMHgRIg6H;7*+sq};#kaR^X0c$Od9O(iT&H1j9$|h
zZvejX)~^LUz8o2|6NSEC{guwunk1Nl0F)J^?e7B7@cTDqHLk*aB$^+J21(j8u`RGH
z=2|sx4{=*$_&~^v;hV*w^&{V=F*i#>p>T{JO2}Up95fawnKeIdJUiC`Ms%U$B{Q1n
z*P=>6cF>KfR|Uvb%y_%Z%*=1TopZjgGjw<M_bbjHFMS%gKIwa>E&s*%N7&ckg(D#5
zJY=-V#NTpd97EqW$~u^m>#_?d4e*ZcUG{OkB`~0SEGAQ*D}j_oswSJeS%2wWay^DU
zD$vo<N#4j>JkYGxxa(W%#r%zd`Hn`*aJ~)caRc9rSUS`S2xun2_DLE}oAq|Z>c7T^
zs6TUMx<Qs)JwlcPOY0#;O@H1%dt7Zzhunm(^0>Z7#SY^RQ-$$Ievq&WtFF&Y)?Iru
zQo~hu(x3c@FoGg)ym#&Z6^|_~=!5dw*7vsq2@`CR8&>aSA>3aXuVBxC?CsODQyqt|
zRmW?5c=gQ+8c#NCnW|&alC00#>Zz+l86nKmV5W;U+O9gp4Sa|v^wa^=>U`za5dX16
z|DHbY#9XC2JP)?+Wf)aF`Wp_jvzc)W^k!de(PB-NeAVnjAQFj@l;ATT?I(q3Rw_Ni
zMMoX9IY@XmYeIruCN|N5;Vfn4fa1DhPu3Kp5BCTb^?CZAH>=@HIyAUlVaC(I%#GLP
zZ4g3;KFT%9u1HUq#)cf5<3u&QfU#LUs#w)*z#9+qhq|AiU&ZC^Pyf}a00Gl*qybH(
z!u#p3%t&1vANqd#R);Bi<`lKn8h{(RqeIv7YI~i~^@Khj>pn8S6x=o!eepk6o_%T|
z`h0B981XtRnuh_64Rv#1k7#T!t*@OldY`H!r#@OB{<FO3O4jW&Lfz<oBl=BO8RW<E
z^72m|3NZuL)YL-a!9L3k#W)=peR=hQC&&Dv4A+;Ty>hi1*gySgdpF-@de^#&=Ny8W
zfYwOEq|#}YyoXr-f(I2`PYHd(20<b3ohiPAlV7_fODfkY`mtZX4HP`)h!UFA)`6o0
zp;=x7)8GqPwYIdBH{;DXrki&P9Y&tHV~SvSZ%)XRZ!H)s2cQ?PuC3*_2t+R~8sz5Y
z&U)1;-xO)hhG6GZ7;uIj)|P8@C_ZeRibKc+!o^%nSe}=BDbU@)5=`H;^|db`5mQGc
z-|q_@Sv0ncpwS9Q3-cx1-Ni+tdMtpISUoRRiZ<xBLCSIM&O7IAEN8th>xTa}9x7~J
z^~|w$r8`I*;JA7^){MEmXZ$uHuQS>Bw{srb0mgsRWUt4NLlO?9Zlt1V`J#S=F_ajS
z<_=ScjDy*}tdmA#*Agwy;=v-GXoZBxyn6M@NwRk_x1;0nV=3W0)hZ_E(eck-W^2dT
z@M<^JzmqupUCCmwjrsXOfWmIh_l`Rkm=%=+b$rS!-oMz?glZ|e{?uCzYWOzR2fTw;
z;WE)3HxXsip4P7eUu>wwG<Ypf1MpRT^gG=+|J2uF-0ZBsI_W2o>*k2H-y)ynLh`z2
zm&1MIYm1xh#gBz5{@05OpAYMM3m0XVSMY8yo~wj1u~WgmDgFO=7#?`Qcr;L)oy5GX
z{=tfy<mLi_C$Y}UBw=qD2z_d6YhBknM0pJs-@e6faa~bVRo!FJFc}!yl{3Wpyp>^5
zPT>FZGlibT-<slL7=?vDQ*FpJCG3^C)d&Zl9h3?^f8h+v?0??c^t9BLxbBYBF`b?$
z@~A|L==EO(6B84oPG2{ZCueWzr2KD=TR*;+Yq{2nYchNE-dc~J1q_(#P5Myu*Om!@
zhDqD^^qI%x#AMrwPyFhyP#mg(IQtf|3f=#?hd~O|?=_^2YMEVz8M30xh~d$~U}Dw{
zB(?zE8w|)9JI*jsYs}hHFAhJJyg-u6p?_-+lJmPRF3VE++3d3PYcJ1(7#BltHWMgX
zkN|__%*ICHhJ#67sAEDbcY5;ka?XkU2>nEe1#8m#e*uoC-&9GSO5<8PtYDTUJeh7e
z&VTn9Hh|M+Xb})qSXh{zZ|S0CYs<plg;X3O89a6{MSm~JGu`^9xIK&S+T7e+`eI(s
z^=k|7!7(ICu?vv2>wH$jv0$U<Z)|il;nnu9w>VT~kk5E<H;F5#VMeUaKd)S2ovp?y
z4xqf^YcLLcLf9l<gm&tyxI}b5hejX`|Da#3o2tn}9Ebc-UEkz$Fne@BipTWT)nbRa
z*4ptjLLlU9LcFT3?wK=3#A0r(Y3r}^Iqi=gn>~rrOY1~>URtT`I8;NJ!Q@xnICYST
z0d{lde}Of~zKXiv<R=f30lqr;_$guJxYKj$x2b6E02LvRMOQY!8CUx5HQ;({A(!h<
zm{-Wo*GWewgv_7gtv`oc6d%*k(MjJT2fF@VA3iCWDg9K^-Ca<ok@YaT!wG$Ku^{X2
zVq^^*G!Al~%Vj{{e@;c}((zBtu1p?OL>{0q*da_D<f%v+i916jXmjo;{d?I~`fOYn
z;6N-b=qxLXdLXUn`Z+{nyu|M|vqtJ$@SNr8&Fc9Z8gZ|$Zmo@h7e7GzGyoyH^q-wH
z9<w%ql8{DWrfFN|>8K-l5KGu;Njs*5q}d~?4e&$ZY&XVp;qa+G_cf{|fb0&I8lp4Z
zRe#Qe933f>HP_Yg0hGrei-6sXQX}Ke&dz}Ag9_sgAE(hn=w(cu(MkIy2b@9p@{^pK
zWupxkAjG6iRy+Oi<J$=PyPZW-53PVdEhRIw;&;W$MK~)_m{;h~UNOGYfl`gg;<rj2
z@`Oz>QF%^Xp4c8BFkB>g-A+NRqNo}N&e9o3@zKoEQrpFa8_32U%4`YRyFA^9K1P7L
zb}jVg*mbQ<ko(4dQb3{vcg?_keX3l5XA{%(Iww5@NZ&=o#r2*)R{($rKEu<Po#<$c
zOg{5bI={6PQK~=u95Ke95yWSS$szSFU?fq|(NL9Zhm$bfEbl|sM%Wb2pj~dxZGpK4
z*Bn1-VuUyuF`eMT*E@sW5xN~N*3K_22?P8-093HR4ytG_@9fNbq>%{){OzOrdPmK1
z9sxz)%0ORQ1ZsCV$QK{(-yLvf-{QXhRDjZMJX`Q8Se9qZCsOxE$fcf(mH_1rK%~3V
zb91s?7<6JimdR^8$IedCmrslw{h|Hk*)JGL*owHO^)Q~ElTn+o*BL~Kpuz{nXh(5E
zEiz47F#JYP3kUhi3RkzsfKc~sEz|1oE+U?cdl?A%o7ao2l^;IXTp_oBxWIyaW+k9z
zZce9>$+HEFSJK!hSbg)zu=~9mqdh^*-B17ghXvib5txvhOD%hrGX?z?$F6H;rWqP4
ziyspKmC*F_69-7K0hZ(jp95Vn*X86_cQcgHP}-jp#7$HLP(CPbt|fZ4|Ei%@wNJvo
zx+8JhL`6V4>8slWK9H@r5*>sm7byB{54{OKcCp;9ZQc0RkHc@bH=dK8nW<}F5dHNS
zgOLz2y}J4o2q@JCwcpzJ3%9E(3P<BY8|{!x04Qej8QY-7NWs%nkirZK0w`q%_s?cl
zS4VS2UHlKqvYM``O<2m=Ix=rK?ZmOOA#14PSTBBdb~!Jqw>~Uzg)b6?FC*{trOT{3
zb#JLB4%8gu8B5V=Ab0{Y+Ytn^UjSa*@%bvf906-oK+KY&BA}i4@$vD|T6-F><voBX
zdU|O-fz$?n46xH3RaG&yrGGw|yioO%_B&j`QA#C7P=h~4=&wb7#@_t+q|EdoGi9_(
zzjc29SHu5nymy%aP;yA|rWX=L&c~j^nqD0m0KNe*aQ7ek_GU@*ktY!0$6%VZaI+*t
z(Q0wobEYjnOX+mK9%1D`HUA<5KrisqhCeSW<Jr3wKB@h0PBm(;$?=Y`4Sv3Z8z4-q
zO3xD+SqYTA(u$OU7?f@`h7q4%)%c`>Ix9Tb_@FC>)q}#z-=Cgft!69Iip<Z&D9w=f
z#yvc70g#l^QdG+^6fYQum0p7|wHwt(&FX}S(|3O_yzelKIBU@Kbl3@&I!Wl2Wes0|
zcVq8G?LW<+d59#C6~<YY?;rp`he%^aJ_HhVX7j-@CaQxG8F4>6TyMoFRrKr7_<Vbk
zr`Jkftj*vf@DLNvkji+XKCXaL@IOVFlp2zFb8I}%b%EW;KTU=x3PDYDnD}PyRobP*
zyK#OM=<EsBorJe1xgX##T|S3H@Z{#8QUb%T8(F8x9*~D*>$S1z4Qk)wKY~aWE8$d3
zbwUib@3hcF!|N@pcv?i0*W3?h&?9$_)CGJLmhPcrOF|4RdW0}MB=fUFL>3~AkLcfP
zJb}!2H9jWYGoT*SKg%6H?dPL7{wD3w#!Yr6riH`F#+*7>=OFb4!bZQz%PnUO-9p(z
zX|+DvMwqFnXZ_FB{*1*I1!(oXg6lG}bu&XkD#r0_&q^8a_u5cdj=YG#nxipWsZX)f
zR~(55{H&9OP_OXgZ_xDfsq<r8$FTmnE00OBNIIxwXPrFq?^ctQYtQ*WU*g!9IjjyQ
z%|6bNawD<sCdO5&3loU_iNatAmCNNu)hGRb2`I7Of<rO#*-l#+&u?UGd{YK_PAdk(
zQ)$N$p#l~-vbt=SUZ19d^MASGM7*Ya2fBUQr>&^3nf^Z|_yVYnx}DG+C8J~|xVSg%
zeYjwNK&Q?%s#Oh6Ir*M1xlc@Z+#h5LQ?NFje3|&e3Q&}*MCtjkwP8i(fL=YgYyJ-h
z=@|SYJp^h+rHGfHh=l1Kg(n$4l|#Y3?5DIm=~UMK5;GUwKqh0^x1*S5tMD`gq6A@p
z$|41z%9YqZ%l@;6ji<%uFHdrswI5!=8b`%G&J|vjYLu9xtRoA#?z8Qy+D#UJvR$u1
zC*Vd&I?BU@tHlQDok7m&kj<4!{ab~lk<-$nVee23t%yq?;mPQvL$x=hsl^39S1YVc
zQq&2v4h;-r!nb4khR0&|y7P@tD$>Kr&)NliI5B!kxBkMVC$o4?|7{VePoeaWOwP~{
zIANqw#F4CeC$NvvN@cd+fA`rD<V(bgadLI*h+@abAV&AoN{XMXeFgWS<(G>jp;!_a
zJg4t5;v&F)ur4KIF<1QbEjv<QGdKeP)rVaAIA0F^c>JOr%1Uc0;QypxEL`$4bdm5Z
zQKE!!l8$e80i6kks`9=atL<~kmCkp?8Kr*$U8_Usqo{kZ%rHsSk|2uC)7j&j@2G`+
zvKqX(y`_R~inEgmyj2wr2#xO~NQ2#J6VI9K6&3vD!0>%oP;-n1s=j}j#y=&UYvmhm
zDS3+`c{O<ekvIo)2&)W}r~{Y&9yhYNuTa~4m~`pO8Q2=1E%s#C&*(GWMR<R?;eo_Z
z@4?chZ!P*R!_O*lr!u;E{>6lDMiB~eHmbEGnAZW3f*L`Ydz%eyup<PVHjbnuG8Ka8
zaX49Z5!A_IaACAN7VLzN4fPBDv5}#OWGM<Gf)h-y*>l^2tCM+o|Gh4T-7HF#yAFl?
z!3)m6=?HXE*5#s<M8i`jz`>=o_E}*;z=;MA7&ub8NW<_m`&}^%mmJVK>Slzfe#Q*O
zze0l=JJSD|eWW<FNIwe>PwtM~`!*Y|5%vybOQaObSW1@!X6b4WMroFbxG`aGLrCdu
zUA^xMDR9T(lAb0DzmTyaXgbWZ#M+b~SJ9w;uJ9cPn-VV{M7k*ug@+?P$Ggs4O0!}}
z7RmDniiKY*fphgpKz*>C(e7<y0J6IZ`kQg;Z-bR|zRBY&OqwXqyB-_he~C8oR}`cJ
zuZD9ENzjWe6VdB1{}N`89U-Li0YM)V;Q(10Qy&*C_9Vs$=xqj2Sd8Oz*tBv=wN%jf
zMq(CA)JJ+~MGIX{w^d{c(eNwtRUU?S&#d*9x0fLq^tJ{bv5gcn0!SEG(;(hEtc9k$
z$z{u4@xN?`GJH@rs>y8<!yRnTnCY$Xg<8Sz1oo8S=tuZ^N}soz18IM21>AE0!*cI;
z$@&~{wDAqrTsvJj&~uW7FMf$Eo$bq$lP46Cmjn{2+vpKj<F+W@1L6l`;W8<DH+~cI
z4*LotNfsbg_`+pIo;V`>s;t#det@x|<C*7xUpc&;CAz%9`;iy!ph(S0Sn^~+3%6B1
zNslyrj~rP!ep)FsJQ?*DY=z@lm$lGW_x#a~JTZcfc{0V!H^;zk1=`|Dug#9z<3nf$
z$bkVkY8mN;v(`!W;j7o>W42BNP1@Gz&t&nz)}z)7_;KFWRyW~T6gWRzJEO167STmC
zq;JWFr=Z43y&Uz8O+=&7@j@mc)14^wjS&E{Xk5*Z+N1WOPAc&1hbOP6RfIRnW}88N
zQr1lN0Uu}MUu}7o(;60+%D*4P+x}G~h3j&+vc5P%e^svzoe+dNQ;~kTBZ>4W3bWCC
zTZCP<;x%V)Qf3<YLfR3KJX?os*trd_4Z><#$+&~<={8z_=fu9&i(|6gK65hSes%Jq
z%`UyM3mfxfzmpDmsD<>=_}yIpC45Y!-(k4_$cAP7B@;V@iV3wVYVY;)+X2G@lDFdj
zg27vrhS5ku)Ls<60XsZ0bf%bTc~f))k^YFUTeXEpZ){t_l5ivH8E_~P30r?F5q-2a
zr?_?JzAYmPH-_@7N@@N98lDcU3|O}XIX*fgiR7XGdx_##<iEW5Y1c4etWyAL=S&`n
z>oq{Cf+`2vO8{2l+7s)@lXS&wW86R3)VI?)FF7NzdD;R+%;dn6JecolQ1Ii*iu%#?
zqG3t{zv&@%jw3Q_ApL=@d#!L6Huy-H&W^~>eLyLnk(TKBpmY#7g?qVbhqBsG#3P6z
z2IR%_V#I40e;hCpaO{!Wk2JxNZow>cW8>DnD|`+yOpHiSQ7kh|H9OA?VKV9=$ei!v
zUO$jJMW@IYr=4W5Hd`6qQ8x#i+sg)iI&<4vukxhhAwBhaYelvTPYSEI8U^4>f!Nq(
z9fru)Vgn3*@E<6VbFNXZPzDN+6SjyN>})~Jn!P(F!WgY}<e<C14Z+bH_SbXD*3$e#
zfm=;8Py<O#PJy_?HS%x5+>mw!9B4s(?r<6RKU`8Hp*w(-bD}lu6crrt$3&FvKv$a-
z^*P7yuqCF(wEk%((wJ^0{}1$kS0FS&MY5mccA`z8uJri^_!LQz1Y`<@dNq4!eT&m{
z5uyc1JDD2y2y-UKM5Wgr4k_V6?1|_7o>9<1$$Sfpm-Om?*-HHiWq%QD)RXVDOEY^S
zJP|HE&X3Ff?SRQR2^RsR&J#;v48pOz``ES$M~Uzal%pRoN80FiYcr<wpdvV!?>TNi
zA(Qh!IlX@c-iCY3y$cL)Yp2jQ9&?f-%J?%>5o?@_I3a**P}7i9%umUU!u2RzMqzmx
z?@&qg{wBx5mWp93>UqH4W(moChOVFDhyC4bF?B+>@qF55rVmVoS2?v4<=1_v3f5lq
z5~LEmB+gq+<HJ?(rt#AnmhU%ALxBh{OVPvsDew4OS9~zu5^y{ADdrh!3omYGkd-A0
zYUAboBr5j0&JP$i1MVetb%*AUew}P+gs0;hxUXE-SNzwjV3!CqsqZhzkd4@sz*kc{
zIey%h8&&BWs!^UHr*R6Zq`#xyROpP0$(;?qS#BgvZ-d)XdFmmAu)fEeA(|7*)|>z?
z%ihR&YZqaDaqz?$)mtUj&}6Hn)BEoc;0~dCU)IPr7N+TS{4?C3g5=Z4;H-ecGxNP0
zrK<pUl%Dt20flxx6c31=L4tREbhUu`78k~LXN6!|sCnxjbYh(O0esmw^&U9b_Uw)5
zNCEV97EGP;XizlL6+qo-+>kaTt_R`W3g6@NYPR^m^kDawJo*FqI(uS#(=2D2E43uV
z%?)SydpGz20YXB*S1_@T+CuM__g`Q()p6S;lFVw`5^lIfS@E>zDcsZ^lG~Sz91C~{
zzO*@8KNgT<u$c;~STrNJ<*Zi$Vl}WBno_OEED&>WUU+4n{~JLXlYxfjv7_lj*=Oq5
z9}i`FP)56Sz6+6b;_CG4Fxk*lUq6$Z$1D<bjr6Pf3h{0o=%Qfh78rHlY5ElkQ9LQ0
z>$-aD0(?3q)o~*$PJo^@5b2fpMfi`yFIpwGnp&DGF=Vnf&v!++lvs8JHKwfu&s8KR
zqP&l55={_5@m|U7ZQdFCI~GY{AFjTsHp`(H?H<x^oqkypE;NP}LDdSLi^<UzaF>1d
zvR`g(5tQ}ZGm1QNjcuLArH}Gc)GYU8-(cJ6?>uRdC_au0#i*t6(~Z6Y8WxPmblmxe
zAo=B~=Sc(a9!L&th@j<gj?jaEWMvxG`Y1O35XLJ#uMjS`jD`o=ZZSQHY6J_6i?W*U
zMlubCwmK%glx4VWXM`wPI^pECox!yKglt;s#Qm%mMl~l|KGbGSn1~teQaI$KKwWhU
zIFAG|{xJtLfzQn%b;&<IU&E&zM&v~Ve%foysO?%LwaUaW<QjY`flp^$d8wp2XqU78
zPTfGAwt^87&PbbCi6_-j6k<ij5)SXgN|AMC#Ef_Oh_y*6LW7AvW;90^;c^y2fV8r}
zd-Dq&`%ak$I0T8O7*ptpb_v@C@`6pA+o5R8-+ywPIAy_!>I)-2HYttbC^z9lsTL<u
zM6w@AfTn(Ga8_U}NO&JZRQYrm{tSjRZWrFIwDED1=%>i>p`a)NHiDA&@dO%<(Vu)v
zaiXcQ-q!MJWHt(F6A&4Y#B0JHTPYY#V_S7Yv_XswO9Qd(+A~gU5~U?7n43A$K)7oi
zH}CPpv*5wZWitgrbOlC1OEC;4($m)I8njSUo<XC@VYW`d-+o`uRGVIrm7^c$`qZ%N
zs^J=yi4*|SdN}U5$yUIw9i_6t0bse39tSLx@%l2NN{qnS_3TPFUxc_X{7|c*=`(Bf
zVSG>TPRfJwa7DvCw_hzL?La*dcnBI3PdP%2NzGz=|5lb!OH%1dQcF~N?R;U10|R7q
zE1Sbt{)e+1g~yx*#bGn@e;M0Z@mw)_-b@JhI5RU`?dp!#`KgFB)-sU_k2n~#_8-1!
zXd?R!*8IKN7W)dAl{KVTuuS`gC};zX2h`)JFp+Tx;p70z=Dsgi31bh#@DP1|Dzt%7
zhZ%}3vTp?t!daxT>riqKNAN*LRIGufvu}SO0zxT}Bx^NDnTyjphs*?Wy4_1S4@R~;
z8<kBryR&RiV9~Ohb3<mJW>?2>YN{Jxvwj4dvy!>rJ8pSbO!@}0%g|7|+EZRs5sIQp
z&F6YcXj6v~JbOLPxdZxZ?lf<HZH9@*ENYB(EzFORaNv?Q0->qNl1rFr2xDaT3T8^m
zb0MRAu49>f&qqCB3F}Q&UR_!o3U4T;+i((m_ojf2jo(=V!~bEn$l|a)t9Lc~Vr(yt
zO+;E5cqS>dK1Y0;B|V4=3$I9j|9f=xvYt+Q0UfprKcW3`4YZK}EIA_o;GlM&a*j-h
zZA6z|Auvc^|KwxwNhec~g0e%Y1^DvFxvb0(N`j79{rEW8W;zEE&jS#z!u8bI9)@MQ
z#QJQVw8?5Eh3%P0^Tz`gGcCerbPwW}Wd9iERAmfw9+*Hob0MIsuO+?tR8EzhPop76
zukxH-yC@lVFq{vluT0+z&G#KqqGx?nbx9*UQRAspE-}Ko#rQtDn6P4L+AWF>@Xs;e
zv9+6-CpC&iZnG7?b$qZd)6#TydyS~i3%?e6#3?+Bx9ybzFCGKw{+NA{K;8)%r=xRc
zxaFkf)CSA=lC>ARu^DfCZ#ZiVNovGHCJE-4V5?omOyBt(g=h(^GE9wq(fR*(0Vrx%
zJ~?6}p?Zf++235f7a)(8k(nk83#K4#*Xjz4qluSBr1}FgTub(J-7|E*Fueb(t?*rR
zM&byMw)xZQV+0X`AIEvcvyK0if*=%QBngy+rtSDq0b2ShJ!%w7$%e3Ura{&tr>o;%
z+}m4NkwwrUB)nD_tEp=H*1y*CSC%==_?$cuq|tNc%eaDN!R7=<4z3rbo>Gkk`k@Ch
zPXo=I2HN_PkP0v9{pk>&2AlXG{l%}EYng16H1lXFZu%RzQSg|)CsOy3CghzuhcK27
z=^}jP3rR-T1Xgi|ca)0fSU>rgBATx2E~1Ln4d-1Y81@8@r*&-m>StW@d;cPfT@v0Z
z73V~L^M|MG*t>)NhP}Cu-E8`zv^@q}dR?u{^P5*{^y1$U^OvjSW9>Gn5MqumTijG-
zn!gtpEfUTzy4oEvT0y--NAL=4oGW%DJL0jG7#fdT1wdYaDtP9EpZoamr3K4;!n#VE
zREDt;aEVL=9562y-t`Doi^(woK8o+(IS@IHZc8{%I7>Lg=JLH*$eH^kh7bkfIYK|#
zp-+HdoF`mQ!%7!K+F3M<1AbPe!bF=}8SQ3nFZ>;CE{(w-bAo}EudvHTw6BD2tOaTm
zKNp6j?rxtHev_emOxCO#5GndO`GU-|AFj0VvL=OH=K&lse%l)D-w<yI(}(9s26G0O
zct6(pS^=)v6D-Td4^ERcLfph+>g8P|JpCEHIF2csq|T8>`lstP(*|MK5Xx2l5MIwX
z&LesbA1aqlz*i}62Sr7q6JG_}Qn((#c-N7wV&C5qSS731{0jO#mLWkH<rj$9deT=;
zus;x!@W$v~M-V8=!eus@E|mq2G{*HXCsaMHcpD)8UVYaCogZHlj=hIWL?yq#X~DZi
zGxGYpTEbc=n!Xbz|JuTdKBeN{MVIz7uj6=5dL?hvb{FL-v`O5_ZKBPl4R*P9FPXo}
z!fiC!sAihK%y;0Fh4vu!?e`|CZTfwiVf>&h6qeLS^ZGV)-J4B4{Ejt7QE}N9TNY;?
ze*H~A>VHxqz;|daxV#mU;f8#MS>qgIZtd7Qx0LC3yN4z)0@jP^Q-35-0(r&}y+w~S
zvC-FNzZoEF>dPBth@e~F3($gW$-r$$6Q*5Fbb`JMRmIqh_@pmtE+A7_#k~oj!?&vg
z?9n$U10T(bPSH>1r};c0{?!HOhN8ZX^i$4PxI^)U39}QZ_NXK<yb!alqZd$`q4zSP
z%1f4@{A$}SpO?JmNs|cPGUJ$USO^mYYEd$)Va@bv8&P!lG_jC-6qt|WG8f(Z5j~0C
zhjic$2NOk<;du1Ag3%m^o7&n9(-ljhV%?9ZhL|BMygw2rsPLuOd48OrqRaMs+C{=o
zeI7Ikftu`*2!-LV<WX+5$Y5?wXx}GmXdfio99Iv=I$Lj4rTo`QgWLDFIUg7vxu+>H
zrVTd#{PhDug1H)22ETJ$<gJtmB+DeQ;ikpr(`u5yM<d{z;O_q81*NAKNmDDpv=FM>
z_XK+cxHRle5T?Gt-3m{(0j?(Duc4*yet+bHaz8aIeQ>b-<UH%Zh^bA5)1B!R)$tOV
zA&LtHE$Lhfz%1^_rv|x56R^PLq&pL_?>qL+jrtn<WUXV#iCAVNj2p7T+k+~Ywsw4~
zg-l}eucAY}6VS*q)!7o-5FOqD?*3Bl2`w;V7qFE}^Al;iTYsC25IY}C{M^HBvpP!@
zbsW098?z^H^age>aGbLTJ5!ZkP-9uuShJTV#`tSC=mr_m!ul8;wgS&FaqTY<TK{bR
z{79cyq$N7wxzou)d<M?qg`D+Di7;gaf=bgO%kHQ4%SNles~g5s5&EPcKb!u=7v!?U
z9!?XY0iq!|_au=U%;Y`_YJ#IS?JWV)Or*86U6G0ivs(W`*+rm{wao8<Isf^pZ1iAE
z67fRUa_;PexU|>&#6EqGS7E~x6A1kxY+E7;QBRqw_9aRe747-p-&cM6Pt)DcC>sk;
zMLZ90C?>z9azSz`gt7BFw<g4{&fNVw8Ga2}_f?E-Eu&@|q3B@~@6wpFDRs6ol=RBs
z^ICs@oBPCvrqjA&)9Czu<ImqycgR4rIdYo5&FdUg9T=q=8TfkWSEbm<-^VQFb^hMd
zMw4gx&|s|e0HJzX(I=fOJT6#Xy}Dh}y}pQ?!Hl0*QetrvYiMh<OGk%FG{KMK{@A5z
zVnSPEy_ptllL6e1&FC{JLKbmE+Z`zi2Ttq{ug``P>;*8kPeM7X;#4=^Bf|eQ&(r<L
zN^n3-=L&L6qVJ*Y+lq0_iD8(GLe6fVyl?;Zr27Ij%Px{4GV#rXr_loFehKxpOPFEc
zHRBf;D)?heUs;LP;>C<C%aD0y<J)R9MQ1opb6a$STfYnSK!dm=&z;7lqV2q0qTKRl
zr&O9r8vK3z{+wN%Z>cxyH2t0=9rLu|Ee${IVp&^4W$?4j@mWGv1l?Wy?JdI&*?PWM
zq&94JI%0s~ciGcjc4pRuZAzS>vajj7Lp#Iu^C8j-zdn9pFCP0NgOsrpmHE{IsZxAM
zQ1D5=POo7nYG_KdvyIrJu*ZB@M%0#1{fU2-AGnL~)tqfJ=4|`xNnM9dPX8n8%q0%(
zRf2s3roRREq+YIv&9}ody-!X^*`r8NHcH_|O4av0o-iD)5S=aI1i7r2<e~3Zz)Y{W
zk1Fvx!;zXYJhd?spzlA*x~PuBs=xx(kZ1K_b5FJY2)ABec9A?*KL!i)UD5989Yv3T
zp-(x3|4{ljVp({=L<<^3U7vQjCCHo0+?G(u>f&c5iEds#_L|G!)}IP5s5C!KqPn~Y
z0SP}FmQD76Z-*I|t*}hO+_2rLOtWb{3lS2CbJmF&YQ2%Yl%Ci#xO7_Svm92>m7z`(
z;JoZ5^JLVEG2nj4tYQN;1JVuAt6rS<3fs6?G;4ohdgYy*g%1xoJvmA%N(QInW*&T>
zeExS``uMkl#0NAr3B-<8BGj(tV?0FGGwx!ek7H)fbo>nB>lp>$|0GKd;^?QJ22NZ0
zdang?+j-lZS;sjA%O=aos2lSS{JdXh#9cGi9E!MKFW@!op)J%Uy`m9(J)=f>C|tK1
z2*Ky!YE&SJ&&$4l3+&-<nx*Rqx&?kewl)z=<o=TTln#Vo!)x8uzwGHBP4TxQf2NvG
z13OL2hkhbQ<l#8~@n%?h%7>_c6Eq#oI2j@>;Vc`@HbS`%g@!dU_FNaCid9_bqJk@j
z46qc{zPv;MJbYnq)nhOns=-zOC*W;6jbGi+E=+I`&>e^o^gMc}IkTs58rdOM(ljg-
zw}-v1QbXWP(|Q&BGk`eqFM*Yye=XkMRg}vG8XPB&KWmUS*Tlx$tRTjzl#o38o3Z{^
zPqg0r-VNF^e06K8Bxed@sE#~S$-X)-e!{rbH%~6u-^K0>v?nAYA9TmDPw!t2uZi1T
zf^UA>TkXnKig|Srqv`-n43`5rupT7$0#wA4mP^&S`=>-7-oF^I&DQzk%7h{PLFE3R
zwpdF=?;kST0`8Yq;o3g=+fGYzAl6_u{Ks{}^2VZLw@6vOD%=ue$=Gf!M(zK6fTF(U
zr5}ShKCOJASe$1aNEV!K`?Sl>HU9<WVLQw7RHtkjm8-;zbr;xNs;+xDROB1=Z*=Ps
zJ|hEyW+uJKBgfMXju=m@`$0zuzc%&Xd*dn2xSnoetQ*IXpI;%LCU8!*yc^+TneFw*
z)#MX-gutM|?5y`w(VU%ZNhaD6bPM$pStHlvkNUY{D~VZYn9Hv!Iz_>!Jt;f$ZQIi^
zPaBmWIscb4cbH4i;f$|9Ux22<85hvAUB&jp=iolW|10k*_?mpfH847cbc1wvw@QbA
zq;!Ym=#BvbQi@2AQo2K=VRR3rn~{?cMo0+4ng8=UpW?h9;C=TzyYK6c>y9Z+npbcA
zo?cNuS8x3(lB4Z~t%}CPPINY{(T|}}LJz@{+dO@)HtWgj`J?RHA5)FB5<RE}J}kly
zTdS_-S9nbri)uIB>lO!&%^$fqEsbS`@s(RvnRvx@WCXv0*4VfY-@WMGY#YdGOVJZV
zZH@ozkUl$JrBd?L0dm4sa}s;^ZNNG=#QFCX&rJ|bSQn{+nXP8Y-iq6v-tgW2q>ohH
zlZR!@30k<|l#|Ue>0AEJtzW>K6LP6iLe2H|-sM*75AoaIDa50@gmE8SHsu++fZ6&G
z|7|&3xst6Rhj$l)4(adDSm-iizx+avsQ<aDdi&f%T2$kgI;A|cJ9CH@b@;DuunsVu
zk3wA(srDMAh^8!|c+zv1xS;pZblvlQ*Vj+q>kQ~qGGUzzvz2OaM5>8K_J!o)_<T^A
z1=$Hx8sz?BN$y-9b#Et;IaAb`7*wO|NTv&W#UdK^{6zynzn(*+_EmG}))qrUS(eVX
zkv4cW*;c=1_J=G6;eCMBPi}khIs)i9_KiXQ8OzeN3uoAyXz#7U?84BzFk0X6e3G9D
z*=Bp|uXbRJEmJRA>$7%!q2BDK6uv!=`dK_}F!Ps6BE(%T*uFxjq6i>T&lYY)=Xn$6
zvAKvF3RBLYIHN|sX&M<odc%);T+JLE{t>|aPdNjadG@#Y%)RSHj8`md89?Hcyy4SV
zk-s{Euys7v*1VY8>)hpOd$9RBngZZ%N9>}FU(3|<LN5<uUl&HbE1s?rHsmH_J@BSZ
z`Xkg@Id8rzEKqYNI!MrO!{IuxA`k?^ky}P%=uQw9Y=+xYpn7TyWV@i!H<aqMtY+3;
zF^VVZuV=}2c~Q+x7NR6NJi>MBrt*<1`wD?7DQ~;v-D`G9Is@yD)cTL24%j*N<t8L}
z7Xa3J4KwEXUEyEU%6AMCPcbCfpOg32QWUoPSZ-RbqGR~xRFF^G@r*bC%%i6NRGG4~
zR{!%<<8b?UAk?i*t6+N{m;6B1MDy?o>`eK9v$LD8Qz_8M50IM1yI{tf`{)(0=?}$*
z;v%s&A&p%ajzHh*MJ=Jm!(~Y*=l1ONNPSjFN@@7(e)f%Gwm3^_3yiO__<|Axr>*_G
zmX|!Rz1^^zHK{fS^5je4!^@hsxcmrI4qVO*XO-+^6KE4h+#@?bSWYF=jOcOoP!0~9
zd_68Z@I6=k{M+O87!(mp!!E?3cvKL>0q-2Qdv|U6c}x!Z`M~h=YcRL0%#&QyU}8u9
z37iIw4#GB^bO6V!ABN(KGn`Ek7Lk7Z17$l@2pxkeRL4S`LyXC}4F*EBfnw(9)`qzw
zQMCZ+@Aoa3D7On}BZoBoNcBFF76cp?=jWc3T)4q)AP55eAS}QxV~_pQPHplxtcmNa
zaKY(}Hc^Soh41C5>5^qxrcdRFL;SUL$_J`Gf!!)8FWkQ7-ynqX(hmZ>FXR@Yqzdws
zI{f0EQ%dOZ(HdBwUu@}5PkKC%K6aw^FQ4?Z!Y=np?QK4dTpDnkIIqP$TYEjc+Rt$p
zqD$V7#~a*~CqC)HhsfxuAww|3r|}CkzDePHPt^LP@j>PZ3-mALee>pa7^9UK=LZHy
z7klU?x!fLww}697F+c8$IK0WBFXSeG$0QBLtY1r95gG|hx5k8%^Cw%x#Smc>I|b0u
z{RnLV_qyKnXS7uEzdOGXgODWQ;p_CjUeLwnK<|NMV}9~J;M4x(%d5?ne<7Ws)wL?4
z=WP0<DXlFoWgS_bkIb{|zRPRWA#;m3a7oAXtcZ(Hc1QIXTzkeVN;2LA=U|znhzdNW
z6U;O}Nm(hAmlxh?+Mp0#I4}4KU0-<rjYjX|69h$1O&H}v*_K=Iyu0odm};tsK8U={
z2F<q~+X|y@zM-_Y*b!C5s(OI~-!U!H{f4o4Q+1n_-0*_q$v=J*Jy<+8#z^jC5NBkZ
zcOJ}%Pb_PD3y=D>`+`yRkM}yx-TG^U_nEtGf6Sqk;|{iwEEx=EIYfg|wOUTi@{C_p
z`&`7zZS3Z!HP*CnOgosX#4+vnXX(Y**Xi+o^e8IbN1R!~wfU@u#swVkgy}X8qC&s*
zH+SQ@cw>wp28d?iUq!eAh<-UQd-Q3Pa+nM)$+fw0Vv2uVL9WWU;Py*$3><&n#b5lZ
zA;2o(r&`P#2JZ#8ksJl&24GEe3pEc0kyRdtQX2yv8GNq<%Q~qb81&OniD9}ZS$7<B
z#on^WZ8;(0w@w&B>;(JcLP$3{s|*t3QSzo?(pWwgLzRL!FEm9q=@xYZ`FHDO?h%KU
zIxa;x=5hoRsTcl^5?qPtU$Mh92mDPIX0l<areXjs?(-k?UtZd^6M(&+adRvvI1gLB
zCHbY}*ff;YR=nn{+_@@Z9PrMOO62)d&%@Bo4!nnX|I5=XGmtk{*zeajdTGIBZviom
zzDsk7y^*r+DU3_s+u8_wL@D;kuN8wKwJGKXVrgr&tnLz+s{<UE<*oi>>F-LDhr(3f
zEk{lEkJw6r@}u`Q4mYxhehtWp6YjKJ`~N9aV=sXN;gS1`SBji3S(v7?qBcLQg^7hn
z7=jfs9uKO^lvJ_*<MEw7=S~QbKVpT6YP=B7aO?1g8jX#EI(9MT0%Dl}o5KzoOkB0l
z7PE`XjH5=;`BzEr%avCl(HcB~q(ThTTIY5rvmvuTRI{l%h}(v!+O!GlM{LJCe+8yQ
zo_b+2zaz-H;*x76j_Af)_-z;3FdBWTE#^I+eqNf}x8?Fad&aNT13ZN@R-2>+|D)XP
zSLnYR7!s+jJs77>kIEXCZIH?9;q%k8ekqX36fs>XC(k+KB{;y0@EowboyYtbj%4RJ
zI3OfSiZTUc1FZ}}Q}<>8L?!ZI9{h~6qE4QH*;O7muZ!`mtHU9wNPJrx!HdkT>SA80
z_nSpyfI2{zX_t-y@W!DiF(r5&6DI`9YfNG#7PDzvGgUa`@RAAF5sFb7gy%A5s5WJ-
zC5v49nA7Fr76(WNlL>oA`i`b1w{-U$aM_=^5-6FEzFxAVY-4~oYTt)Me5MS2&f?ZJ
z=RoyQ#SN2j6L9SrlhWO=oZ?5Fb)}_BYvTE1KfZj<Lqb?n9yA-0E(Wv-mW`F{U*sHU
zA9#s7Sv)rtL9&tmWcNY&W-?;p_Cc5}y>1tXm7Zub(@^u0kXoskdgyQE69n3~Veqy)
zwlS;*{YGWd={ibMyT~H%Rw&)CC>U)>h6M2+5&d9UBKSpIEUR!O5yLUyQ?pSCYKvvY
zy9=wVWPt0C)x^GWZFcOt_jDCs@7jzZSaKN$uns#0lA?$oezbaB_;R>}O5UWoT2W-A
zcHLY*T1`D{Hw70wh_>2p{g}R@wUYWTuLHR>9g+plq9wN|Prm}gOqA0>bB)Z*u*a`t
z5v>e|_G(NJ1qksXqnl>Mo^%Ou=<LW_9jV^_`$E}PX!ZK=>GhBf(o5|0PI_RflVScl
z8My4jSQBGnUKg5ocv0fu-Eb1&jyJSXMX2$+JC75yx7hUOTPEw5holf5t_9F<wEb)$
z5LKc{Z-Xt|PJ$Vx=Hz3b+;{P9`$w1S*ouI|*dJ60@iCiNmQU@CG>CmUk75n`WG0ts
zcb;%9v8ND~8pGW=m*B>W57pom==HHYKm1%Cts+VToP6#}I{XX7Q+5&}ZED!#@iZw?
zNPH=topkyZGMu>QOIr;wU{E*YT;6#bvrHE~<PX}(+z5}fq6u?kfk>~p<Ku;!)Bo8)
zz93s`r?EOvKz7dJ!>jOwV1S1N?$AsEg7Ftr+T0T2FoxyjB`G8e@};$Sp1#bYHiC!z
zdR9@%lD-1H&RlPz0KMK<pwDBZ@>Sx6=+bpH7^}zCb?+Bd6Z${;fO4m{m|g099_h(R
z*6gFQK;<BYfXW#g(q{(fdM3H*#Ej~@x<Mlj{5)fk%DtD_!Y4$BJFO^^Ljjaz$MaU8
z*ol)O*a0dQ&?j3e2B9-0KYxl%1b*Dy$R8OoHU7)=2nlVPWLtOP=I6iLD65HP`7C!{
z!S@nBK~B>^S#Pkg4c=cC#fO2x(}aur`KbqqX{=K}3vMDESKjxp?vDJSruZ*8R@V^M
zBjRsnX4o~HInsCUBD(i8zUF8<)wLsf>V5eUv8GpbduL<HxC_<eN>2(kW9#P~%~%v6
z(hY4`b;0N`v$@XAWoJJ6UjE|-;Jc}~&^!}P_$*y)UrG@3y|3xba&KlIuP(HY%r^cN
zsuHa%WZ1ZWuCXD)acrOI=gMQ4CZR6XIq}qTLE|9(H{fC#Rg`t65z@!1KyMP}q)h|;
z7P*>#v6KDw&#qSG?=0n#*cNSPR#5>=C%s&No9PSwd9RWgz6o=+&v9u`Z|L>w))H^w
zVx1$C!@P5At#2Qk;**h%P+Ul(llvCT3R)1`{pX0x*hE=r3$oGTuE5gTD&MNy)4|7=
z&SXS#)tY4<7!b^##<T+7o}jt;<qO)oKspKP%QMO`MhH=SV|djhS{kFtK@ixBj^K}t
zH|sEIaW}$_*N)~cU4{uiQBysNGy0^<ptP5e_)%V0YM8~h$BE?H7{=@VVpdw59@7F_
z`{zh~D~2-wF(>&WCu^rVa|g_nRyf!YK96UCey?Y`s)F8;aQT<Tctll75eMjiDyyOB
zr-PDIinZ>wiwfi2&=gxNcXXWug30LX3g*RvuCu4s885OcY9Qw@op1fiX$^tlYBsz!
z4kyOKhKdA^nR%wuj_>Qo`QM-Z$sPR_dD<}sCxTgfyyd0rYwxbWkne{-N)Y;YwlWl7
zD-@bFfhKs7&73yR-w%cv@WL<BZIY@!x||X+6AkA*Gw_!d)TS9QrZa9d2_6h#Uqggb
z?nuR$e<Z>phN%G%WLJe**E_7i)dN}1;(N}KVOsn<L;^U#q8r`;`k?Pyqg8$pK0gz<
zQ1en!pNH_90~FB@HHFmQgUzGrOJ{D^kL1haDq~3=-`DR1A9=zP!jAKJD~rO-$Gek%
zI3))<^Vmw?OF${xSASX{oYncSelt=vHC4zmTP_w{9inE8Sp8rf2vhkSI&4V=ow@SC
zt_%AX*e4C$qU&#5g??lGy+Qf8xs8uNQXnp!3i^@Js9o`6G(a*o#X!a5I$P-O7Ko%r
z5;DI!_yW{D76#7^@}wri)rsfjH0KGR@0`{?xfgwb_O;uFQ6p&k7b<(qqRvXW`eNcA
z0@r6_91ewS0wLADi`90aWH$jUeZMJMffh8zX+}R7{nDPZW$YhlGblp2amun@Ln+W7
z0byk=nMdgd_k58Prs}UT0)rK0DJ>Pyk0s0FtQRGT^6%|M3rk}3R6X$;Krlm3iSiXz
z4zzT<J!AlO89E)w)ujpX+oEDEXZnz%TGOC<#Pc`n{>!|+{wtjrH&tu3a)&xPCYgEu
zCPdd@hM?eXin?(0x{%_)-iy64(D6dUJZ_71@kaU~CcT*SC!M=KvhSI9Ah{3VaY;Dz
zoltR8k?s})+#k+fgcn5qI`*tey56~Xui4xV&zUn&2g9}7kGBYo4_jy8)?UPN2`*QT
z;mq_RXSyc@r;QMC7KVwKuz!M3YIoCb^haCFnJt1_Xm&@kvcxF6F|4-vqBjVZO7ezK
z4QCk;9a+(}luH|M4@hC6$l_|cja}7WHMeq#7O7e7;-7+FpDSS4lf_J}E$0f&_9)fG
z7^=4E+ErN7bvS+hvUa`q>NRa4pZS>^&*4K)HJioa+gZQgeA%t5TZstxXB8yOtOJ&T
z-OTY89+u?F?<S~P^x$OvdKua5%)GN+`0kMbOOylJmgxULmfwz6l`kawfXh5_WdLn%
zf7-ncxym7Ut&mk%`GpiA8r!$V!2L_3LJ~YnA7PMD)}p=$o}xb~I*nP&hfYat)>uR7
zZbKCKTaud_L{2`SCqz8Ff!U5-!pAjO@xQB-#QM`m`479t_>v%dKN%aJ2Ebp(@P2&L
zbQ=a=o>kAec!_EQuka6wpnBr%^LHm0HL=rhUz^=lb*6Yc84oDie({Q+`4`#G0!P7V
zf4_Jmf#ZU^B{E8<jZTqqldO2{i@o+j|EVrc<J@qQ^Nt?#6?7AMV#N@AnMDy@D)jg_
zrgu@82h$P*(}l5aR<^eqJ>xdRQcIzKg;qBREGOnxaYes+?MI(-`aHj7ZI6`9@}If7
z<up;8&8f7PgONZ3apwg!m))StlxNkuUE8}#?Y2CxI?ux4H4%gsd}7Y~lUQ=O(}+2<
z30OON3{m3RLp?418)W(TSEuQJkhc3HRJ1|FIn)=1rC@3I{0{~s#vny5?Hbw?ch;^m
zOa*@6tMId79!~px5&TSTCUIZK-r?BYwSmUgDV*%Zo7Sli%#$y1=AA*)UOpzUI>L*L
zcPAp=d#|nwt`q-BK_If==PN|_<N`7<&eqxC4bHu-Fyt1q>qk;?_a8ja@i!rlPYSIn
zk8)CS_X3a&%)sBH;qt=hg?VU9wB0M`0jH#-i2?jZ*2=HT9xz}8j>1Wby$#nB-R4d<
zl+E%;e)z?+N-v)vJgHTl8;^6RmBaw6dRwyiyeP}Nn`$q>W}TN^fI^{eStjlcI>;$)
zwHHfjF~87QeCD%m6AJIry4>p66CuBZBc3l=Tk<qR7@&KmJ8dLHx2Lfdk||U62>kv-
z@B?II1)M~$lQ*y2oLXXZ_cuU!8B?QQ0rsjLEnF!^189Kzd)Y0j(Jw2QE^{%X0PpB?
zM$_muAB{;S!O{dP*J)Z?9Sha*HQ}*|m8#+=x^L}dp5a&mLHPUEc70yI{(x2tvqkhL
zR&X@S2R1XIi%fBd(Ac@WtiIyndowkME<C=c$25@`K{P?)R7I_5{J&p1{JFR`KjOl<
z3PdQ|P-*u13{^wN_U{!;2jR#LZe{PmS5VzMsi+@1ucIT(!VMy{KC~<}ftBnPu?ZJc
zC~wGyg)6ZOW;tJz7bU4bZ&_&Uj*s$})O6<fD-Cy<@CiJ>y4NLy<&0lZFQOaV1q+xv
z3{$P&F6wDgo2WOtHCU`s4?Ptht@*!N0NaDmw{)z8C@V)}&k5njGu=@m8}BwE%`^md
zI-Ym8BhB9HgI>MwK6@pguDkE_GhN>4fY-M#e2$ea+<|G$d{Y$EVkD2ln*w_lN*Eao
z&~b}aTOh5<02I;^C_*q~Q(om?fHTMXe+%|i5*XLK>Q$jRjr5`4Tn?otqV{-}C(A&&
z)wmO1J0Kmn?DsjgQ2|5^!eG`4<t{X*4;o5l3Z9&=9KQGbdpHyv8r|n^bE@#wfbgDf
zhxe5<0Wh4ccX3pkzYtJ|IZWQjZ3U9*;F-$*v#VKYKpB|E{B#+*lVg_3JV)>t^bYeN
z53O0Q@5_hHCO0%sF&YT2g*q4ef{A&DZ)~4ERD?mrO_JBID*vSW3ylr8t?_X^nxy-S
zIfaiz;3+(<9m(-^4zjDb)B|rVqLF~(<yrsuAS267zKN^=DPI1@X)O3Vi|EceQeR8a
z3(H4=B!#xD9>ikZ^4|hQ$s-`rqtqtiEnBesL%s9xoe}$Vvv}9T($<es(Aeh|90Xcu
z3fRkwNR~%R5U&#RSdzrbCC<A0d;Z+%vQAZDc)n6yNdBr1BBAT&64O!gjSw@{Sl^{r
zo<XDH2>}Caba+kb^Jw2~i>%$k7$=^Q+#ivIM$EPk*>7)wF!S>C^9KI<>^uVzsACTP
zLy2zfKi%1W7yKz9-RJqA6T<uC7twn4e-{>HJT)YCC21yVIJTw5?{hxP|26CvIqH!a
zEk@5S2XH9@gVUs_8hQ!u!%tw!8>$o}M*GPiXZqow7aR{nQ7OLQdx`ev`Q*GqaRcl4
zci8Sj@NfKdniIA3f3GZjdb>{UcrcK}_Yg+W+i!=3tD2(MmeX(i@92zmG7;@yJVGna
zw?|gdog1d1vhJu%+cJiNNWlEvwWE#uhu9d2=vyu3zgOpvskqg75-fbbtgfM+iu(B^
z)`PdS#SRXY*S7gWXknlG<cEMT^v`@pWKAX4dw;UA>iV)xHa|VGxq7;5W~(P8-YBfW
zLGx{SEoM5{)Be`VaS53;%7e!rc`cg=N*j+)(c@#PW^NBkYL7b@DpH#v1YvJ`rpu(h
z{{#)zaAf|(<>_iSXpq7e&ZA3{riktSq5(;xHb=R+y6E397g!zAnbPjeG~Q>dxh&ro
z9ZN|eL+DDZ<L@?{Yxl@GGWseF;1_gn9Ut_~?BW%BFgO)}Nu8UML$RhOk@d6wwW+bv
zdFY$0Y|-XkPcE&Sp#%G2vne<R2irIZ-*S@ni#Il&On^&8fOVzUcqI4CLX5E2jtg9_
zpR&RZ6Y(CPmUnUL@TKWn5{4fMO~teBOiLmGv@0$!8pjcmEJ;dUy#9JQ_-=cJc32JM
zT_?J<Xqr(9m0Xq02_H<SwG@2!G=gXU+dZu~wlf9^nK#ErEkm`}0*Yxu1d7o@7aRUs
zhkuM$HjZLLGof9r#UA{H9b<Sm58s5`$f%3`BTGZq$NEc?;GfFRQ9!pK+@G&YGG6|x
z5B~ag8MyWf83W^Cr28EYZm*lHv}FU~+15vRO~(tOy3J4clsz*SWELj&CZyZc5kCXL
z24t{$9@-=W)PiLuze+NiL_5Z-gdHLnj)DyIc}PE8&+de!Rabfw=(as2Ee^>c*tj~}
zc^k=w%orDWK$okIafVM>G<4bjj1^Jz0uY4y$?-;MCTj-jQ+`zTc=lRivn4EoZ|jls
z^5*eJY%Nu8IMXu*An{(NQa!&ElOt$}0|aHmLX-+*L`}bpQ#l=&KWek$G0>tJRz0D|
z3d745Q_p1vSP(-DGWILc!#QB_!dC|azl8u6Bm>sOyn5THnk`oMK3-K2BY<e%q8e_2
z!s^ke>W-AQ_Eemx(>z~vBnftas<%BnskUWOw=B5&S)@Jl@Um2TRP7xygcZ&Tw9b`H
zDV&7VuT+%Q^}Gw{=bOK4e+=QKYP(+Rv;wF1S44wEsgIaV*Gnwm3c^%v@7eeYPdoho
z>P(|qiJk|4&mn+$1p`F7anY|f(VRT-$<kdr^_`)+<n&!6ay{gXhkH3<21)H?w#g|r
zP4Qvmom<Cv$D?o!j&|}JcF-%HsUyIx^1hB^ES(w|wm>K=!5Y#1RYOE(w|U;efnf+&
zicF*W6p{;M+$7w~zODSv81))SK53mhi=x{UK~NF8Z@2l_bSQPSWCo969FJ6cvGjP#
zo}`dLl$JIndWd_i@aXqp^1kIho0C{8<r3<E>jKf!+V<7+OZ&w|8;13<iO>^!-R<OI
zYNa}5NM8t}(+1~mYN>au>FsY{^l+9yqL($7;WX8it`ciQ(9e`@h?lQtl603iRR>yI
z!L_Ot=r~E|xB+wovz&diN$~CrY}*wev@1PUiIml`A#K3Z>6p;{i1~EzW=)(`SY1ID
z*p=v=Dcf7Enf1)*iM=A+Al#)Ffuhk#yY!)|X2eV7>Ww3G(01zSp9b9joX-+NaX5dA
zT*5_oav_P+3=WTaAzGNc1Y=ac9|kZy8YV4np*xBnL2oPZwN(Ppvo##1*WXtB^MW~Q
zdf!vtF$DWu9`I+0E2;CaO%^XkBv}NK=9xT_g>$ll$MBu#rvUR}3zI_2Ozp>$(yLeR
z!5HQTcgzFeR#dT|*FdxtpTu8vamZH&ZMsr;SObj#5JxBOE(=<?G1OJiv~XR~p943Q
z@V0leF@Y;F8k-m)6gXpEWqyb~){gPxkw<D(A{H8o2hw8uhBsW$Bjy+S!hgTSS`+j-
z)*L;ycJYAv0|O1k$rYsw)gu%HSVVTk7fDrY1^=`Y9v55!;qiXw7jq16J3+Ag@gPYU
zc50EA_*NGBX0_KgK7;s-_2Qh~tE;{uIY-s;@Zj%OD%tN)xS0Dn^iB9;EpPT|htcJY
z*>h?suIcrT9>Fns#DvV2t`@w^${n;^L#P8Gy%IHXqqPO8)&d{HlGu+je7CN?coRGR
z`1X{rz*9*1Phmdv7qs0E6nO{(6JsM5^!7nq%o@jSRu3!gF<#T|+rM7O*DuI?Jh>;A
zDucd!0*J&()g<x*qNVv4X%&tN>E(04=6n=k-qfgpy|hI@eQ6{G?~WC`@5Nsd(M&YO
zv0suKt;F>$v^Rf_EAKrPqEAX3Si-%7t{HDjt2?Lt&F%B|*d~3nPVz^Yn-WIpFHUlP
zW7;Z)vuE<EQAJ-o$u>W!u76UKbxn<_j&20xJ^~(3_<|#D@i*xxq#UU=!fvPeu5&co
z1O>ep4L;5j-wZu7UpR};6gh;oR@(4aW9i3*2e4MG>h;~)WC|^X)7!I3Z75kce=N@U
z<V^!&2icaN&5X)w*_#`Bn!}!l<HDG*1&~HHPscAVR;VlZUyyZSL-K#_z6r;76q+UM
z7!~@sO!Wn_?k#{GfI6-LC#Z#P#7LZ6Ab4Vpc5>k3!vvV&52(kD^HZ60X^#fpPRZqD
zPTZCakkS7-%A}_!&z{kCP}wuv-}>64RN<M)E7<{b)^Yg)e?{uH`g75*-=;HKM?kaH
zY%`}KnIE3IlG-BOU!2?#MH$kR^oZr8yx&YElF+G)WMSv#U@N^Z9iT8em?!S1SY3OQ
zEh`%3_H+IGsx^wK<6_m60KZdAQ8a%?81|HKj$D%$BmuJGd6@?^dKD$W62qxpKM*C4
zM7KP7phTundsgUBd78o@w4|{i)l}bGMdYW^)Koy5#V<9fIrTB;i8_V-7aQ;W{G>y3
zHQ^cu2bd=Sfz<J~+VjjAo^|n1{4e}=0u7OZIJ8doVXYAneLU4@-}Oh>eCu!&jNxrN
zPH?(PvHC;3j6NNzXKA(^^HG3n04Hf?>n)mGN+VQ~j9J*cZ%#68sEtC3%;jXD-V=|W
zc7TgPnC0!FM9j@BS%qai#F8AMh3AT0H+bVjxu0PMLT%=0fm(HU-PuE>-vVJ-sDc@U
zJ@gX>4&nlQA#62U%!7g#hX(8e)8t5am~3FNz!hI3iO3D(hQ9d{ZR_ugH-LQ%DzZ%I
z@2lJJf(8?7W&8?98AosJ+JQwmwka}h3R5^%vcYx&nJ{Ilde%l&Rr@S8L0H;{Ks%32
zv~s(*x}P&$oWzRPR)1nV<OD`(5iyRxeD*Jel=hsiB(y3m(vDs!`;}k-_KGH4ORAIV
z5AnR@86(UQTKoMd=lu`pneU4cHD!EfS`db<V266;C|V%8b<#w8MF`_*>3)V4np8Qq
zFCzU?U{RA7=@!$wFfDleOvy<mu9?R6O=vM{o;cctU7JEqnNoZWA;owakI0&1dtt&(
z)CG8*`vkf^>_necxp!vR(a<u2T86s~p@p&VRX?f=MtTrK1kth%pOK99emh7`Nnyf}
zy*@YD&hVUyb-$JOn(zUuJJ$gU*$F3y{Ib4OPlj(AbqHusf1-O?VXv5bLipQ~2Ei)U
zC~VQCGddGt#4~ll{$Q~PC?gYTtN+#PtBB0YdD&%xbarHml8^GhzcHcA_O<f2-_jg>
z_wRZ>Yptt(YtOu!x~Y?<!eA<E!(&rKr(d?1n8wc$0zP$h>M^;njNiO!n$^R)-#n&0
zfy@uGFz~b7&gyHa5N@)uwImvoXiFg#m@9Wl;f~1&Jkzh%Y)-<%vh7d1W{U@sbs#m;
zsDa%eocGDu)Yl24_I%M?*rKc=oc#&(;|$|`CMFxj<1c!Wy`h-#8YJ51AKvxwCNRyj
zX18W|irhHhu|J!BpTAm7r`7pUt?u(c0QB+lv+g4;h>;o3b!*4hzC^Z=HLL*N_MLP&
zN0VIffbl=a<bwgj)to#^>?H{Wpvy7aQ{+bI*2(amOf$v`O)5{kt>Wn&U)LK+kO*`0
z-EPa!B{UVgu)uskOEgbYQ24`W5h2X^iRq+s6ss=+jgyZ%f+~pYMiBV~NeHt`Hlfcn
z697pLq^G>K_JmXpP4?BwA|+T^oY)!^hNa@Yfn<ad7~jw&a3nCRd|N;LF=wB~i+Q#v
zYe7#lZCH+@g7qHXrgu%am7^9aXunf8uutWj(<95oK3PPy`vuPe`@a1iXk<o$^^-;9
zt9<f>Wxvn$dn#^qNgH!GD25Xc&l<n1=N+uwz~wT5A1~*4F|*5w5ZO-c39y=DX89OT
z+}(_2dlg2%<@<<>WP(K6$=R#=eoJ0BR0JLuY#LriZH-Uw)UJ87?;m_(XBh|Mzo$_e
zq=aTlnPcQV!MI8Bt#22hvUpP#4)=e89V2j+5NveuTEaz@!cIcuOAl2H@f&#6!2Yah
zGGbmLCQ)jCqTOC@vkAHhs?(6Ts+36@g}19#;VC<m61b6%ge5ePpOBGzas1_v|FYIr
z+JT$%+Idk31}B7bE%ad4Ge}|Ic!V4LNI!T-*y5|rlX@#ttl8fUjU+74Dl?TawD!Gt
zNLE))PO=S;(SR<*dPrFI-8uBTcakr<LFr>Q9b`<~HW*@f32q>?I@me@Qn$8MGH4b!
za$b@G@f($8D33qnrlW#yQ32Hahu5UfB^*7d{@twh*ArdkK_HL{Ba~8DJ#q;W^D$*8
zrxGOt4)IA*fY38qrc}EtML*{HBV3Y$-b?@ug?|$k^5ejaVTV+6NGXeGenNr>q{%`L
z<*F8WC0*DiTjFW$^5VwCugn%idO&-@g=mplrNNO6-BHrw?wD(6ZZ)yz0!XD8Ybv)S
zV>bqkKppHJf?&ZfE2+L=EZbQWL<2`%%qW~xc9BI(lFf<pX0~k)75+BphtZo#8diV4
zu+4_ZF&IdV?RM|1--BceBQTHPAC(FhD!SDpy91v{9KQcU7!JoY(26zKBB{jv=8dqA
zEy>T@$GH0KgRa|dR=wF2TdOIVdqKcr^%6L<vW!xJ7bh#?PDG2dM{BnWHCSEdgF8Xy
za7n1z1-<hilpelhX!gHk1K}cY=Yg6;meh$xVJJ82$KFIWG!W#eElwFBecNJ?@Ht`n
zi7`P9fAifYkrT{)o1KAyMc?JDT;r=zlQ1*hlJPSlX6F&g*wQnN7XA8e95D<O10+as
z(3f}V3lme_wji8q{?m7%7A1%R=GP=K7a}$sAmQTv=L+%|^eGoMRj&YHB3~;D$`fjg
z4<#U&@1?^w;evXf{w|V%zX(Zq)$-my-L@ro$8aJnMYmv<6rMZq2&??jZlIj_U86I}
z9fgaPV%JXjn`q@|YTNYqle{z1!3{}7j95L`VVu#OIKH5QW(M2&#lz@}QflX+?}n3{
zdoZs4%tq8?RxcV0e7UOsVxENPOY?WtGn4Q0A93y}@Ow@tqI5ChI}zcJD?C%KpVtSh
zdbDg)Ol{Qwg;VpYlqB4_Bq<ICZ#GW35aI-;@bW6AR#gV*1ynE!5T#f$(`A*zh_Vz;
zr|QxTRJIgDhgR41QL;eZ0DS{{9GgNpMORtmh<kK<r5J-u&A$w(x{eX6u51{fjdc;7
z>$3g8E!Oe3%BxLf0$bCKHF3fh)W}!y@2w<XB6LZ(d(HzS{C3P^OHWp++Fa|=e1&_o
zCpE^z1j}ZW>|4N&H1B=Us$nWm0M|4Yri#67?4!BPAH<|+E5<LB9#@lntCK!usVbvB
zO333~VG@P`qIzp>)~)~wE6c1+q%INclZw|9U06hR0Rv1l*hskRGG+9st>fphT?Q;g
zzdyqz@6B?B7tDFYL`dKH___)CDi9vG<#u)vS{$1e@P|AjX{KW?c30@O1L?5vJRFhy
z=MMEJhzSc5Ox;or=d0k(1fr*o&ru3e2Lz!b+~Ivf9F3ZxcPnJ$?-F(er3z6C+q)4X
z&%D1q5|RQPq9Qxa9{Kh`F{`Nirj4#|S+kW9vZGLR!--JK9R9<Z*qPkhlBrn)q%NF}
z#}WwK5!`D9o_OG~{0n`G^jo*aR4Hk~@;;PQe3)1_c-B3@De6BYZ9SE%n|$^JBw#CH
zXT*Wt;#*ExdI?D&^ku+#Vmhc0%jRuA$1>4E(iB>F2;zbr?|O8lYZFiH1_SdN56>Ne
zYo5bzf9sO>Dg@D25r!q+*B*{y1lrz9%ommZ`izE}Io~S(Rs~9o>*$8R%$)5G;%o43
zv@#1P^L8HOk3IL9F!s*E)I$u%$vjamDI)n<-U#FC+=A#-yc5{U6=jlIEmBBqE8>|-
zGCC;E+qye^LyfexnJ~H!Vg~8Z*SC;rY_y!H_1k#axSeN_ZPCEXE3qk$^!`C0OZO-3
zok}rXI6d*2rfpVd<Qn9Qx!XlSm{SWd5>hkZ4sWdv_AtO*-d9n^C%d1s^JX^yGXZ43
zl%Vv|Z|S|T(>5ee`$BoItJvDJj<4+FhtMLQQl@U=dti|JLi9GVUQjd<c)Ib>@%rzN
z7Znxfy7}n;gh^eogf^J=Mq!wVd(Gz>Kfb=hThEvO9ZOHwHJCT*?iL<#$%cF@J$t^u
zO+sQSe1j*tugF1&jf#P3Tv#Wo3gRvzsTKQj3SCP)+Qg!1R-ZV1_b5eAQj4NH%o`=Y
zlXfgKP+cN>{2|w~7zu6T$PjqY5C6}Y`bA%gO#@Kl#3%hr@NMZ~+$3RazIxtHX-?u>
zpBg_1<vtGczzStM5ViKGO<4^0b4?^5Y#~6RjF72jF8C2ck(G^EmUDhI+7RGT$=n4A
z7gu;$!7?+ek7~&(1CRN54zs~=`=k67qAwn^y{)Rir80m7RNgAhpJ;|Ih*1i!jRfC#
z!HE~{AhYy2yl@-y;P)dtT^~P6KY8E(toK5`YZqsF?Zd^=B+z#|jOwUIEo$sB!*0Wo
z8rniqM|{$}7zE`T<r@r0_4EB6U%q>U+a{U*Xqo>AlFFXX3OlM^jIx%Js}+qx`E?{?
zWT(5*k~%zapKDBJnknIBd{v4ge})xh0uUbPP7^iZ_sTE3D9~E@oE)Nyse^q8PPDz8
zX@=fmzlkx0l1OD4mj>6EP2@|We{&w%`*i`Ha3(^rrE7UD!btXW(a;Ux+Oew4KOz37
zK5Sl9C(vrzps^HtVNZ@Z;i*<lxH<&jbGMl&dV^NqaUp6<zb<JbYje0po3E?lu>N&U
zI7d(O*92jq83M?M9YK6=eR4Hom`fEu%Xl%LW3EKg0I1OuFUk2p?YZ?#uKfB)U0Fw-
z**8?-eihzTg}t$8`s|-Dk+bL(j<|VGp0xu_m{qUU0OdBvuWA;>s_`UuBH&pK{SR$K
zxXnK)LJ%q?y&^5{wy+%N55jtRL=u=R;l_t&ieXGFXBW9@-DBpRUHpK%ZaTwoll68G
z1Q3aOb>hA^>^wI8bf#IVoMX1DBGCN5PwM%z3g-Ia55FXL()~jT#j9-@swS}5RL<*b
z!Iu2q9jl0;@J?9u6h+Xp3bktUo1=LAcwjU9o(PU3C0Vxr=3AH%;V^*db7=5g9+dr6
zA{F*-Q0<p~TSc2yIk~k^=n(bej4b~@Y)E{%Ji5&_5g2te`lua(8P>mrTv*x8kjYyf
zT!pqnBW-T^rUkI`ju@!YdRz*09BjSLN}<fV&WfitXA+<~=}44$=4<?^*B~s!ejUyU
zk_RnPG16xnVN;5x>Amj`VPL8vYhyqVSNc8wHt;VTATiAtUwv%{dl&oHhd(&th6HL7
zrTg`zppJ1M+H^yLUfxY<U6uwHIvdLjZw^31dDse=MWX3KRIXqu3c?!p`j{ei<np>@
z+2CE2B{&V0-CI*KZOKHJb}CN?EfFm76BA#T^iAuhbbSsq`|mDIo6;61rO2|C9-L<+
z1Ely<m#SXZmp_=w1w0Vm%Psy83%t7h8!(hNh6?29A&dGNoH%Gy=$%Su*wk9jItMbG
zAcd}QO9mph<I*e=Y%jD-bSfhmUEtiTF!ft+wj+hm2MJJs;c!eyy2`(};2G8dQI|VX
z3BHJhvMs~X#Vww<d`f)`-=YGNm!b2q?!VJzCeDqCjg{jn7GzXy=&|lGDrlU-8ztb9
z*oqnw$Zx4};u9IjHJSu49XL*j@(J<Mh42+STa7st1JsE6g2(ff$g}^@rr~)=t|XBT
zjsWK*_O2F<&_F(5^)NBZrZPgoYfonR8biZ3V5XCgrFupL;8y9_B<s9=pWV<J^m+;c
zkhF#D4AaT5g#6G53;<L`jexaMz;tsiwf5v7siREJKJcB8fUfv%3-Qx`V&cFMec2k-
zjOr1ivAA#yhr2a4d#omJ`-dFm1z}48g7B9@PQ=E43nY!=ryYRV_?%1#v{oLOiVb%`
zGk9zJu*ynos~XqulWzpSEa&64n+tGmr{%l^EP9^Y6EhcYb)aSlqQTy81$TG;XDVY$
zUVMlEg7A<L1&j+(!~pB}_Em|uA;bLu=oyr(@l|EuYvm3xUCx|eB?TWU|DsU`AQGru
z2BjLS(o|4qLLB~5r*_9%vjbWF-SGp?`Hszr;NKA1KJD6M!h(y4KIpFtfhI2h{a*<+
z3hLI|u;6`*Pn{ry5)G(A)<36XJAd=at)J-OWZ1nGJXXicQd;U5`FX*NLv45&dK+uD
z@XmID&=gfd8RT0q^Xf-v2A)3kHHPz)yWIgAHUirnD~!J4Ct_gm*%!KA4T*}4?$MR(
z+-J(z`W$mYj|xa48;b(QD{H82<yR*+<&qmSx5{(9#KTT#&(V`5LMH}jH}$55c*8R_
zmCdOfRYS!{8|OUHmQMg{EP<#9udUou*p^X>Tuq>n!!NNI0TuvHhEq;F!Q(VI@cgoY
z38N=>*?MA1rQ|32lqw^}1#cA1fSA;^1t{Wk^u+_+PdGi)1`{+YgNg9MK$s3j{!UH&
zFN^V+XG%XliJ$HL#7My<K3tFVxORaejRk9;BF9YOITw2{M#Gel2Wrkuzm!B3{1pRD
z%>9&@g3hB>wPVyK`h`A`!+b7F_ZRjP7JW^IH8!L;oXsXi&W{u!O^-_XJGRs%kiWR_
z8)vaMfD=F^eGzqAE4{8bC&M=d85a)cPL6I&lV(R;frt<|G=hFs7#WycUq&0GU&}oc
zn){q8?PHqeEWQOEgs7`0mDm~jNIN+?S9zI}uxsrkbEKv!528SIrrT2EAcR5XeRzAd
z_^@~kV*jPUUkKs9Jj3nyFqJ=b+Ap87uv3;uN-mxqjlKcE9M;D>lF?H8+v$SLYI#|1
zzZ<JL5{7Qm1CFth8TMa(#PSr``Q#AO)IyAvL-UEOIZ&~_&jWh<$v4YPzvT4a-Lsc>
zfSr6Jw3bG0$7K}OfKZdw>)EK1giGrQ@awWvhq6>lJA@p>4Nn6e7Yf?Iw;-TL(Bd-4
zK+a-kT*F^}%f+f2X&3MpWpGvhPp6gH=>8?DH~~qETg9Z%8h4sP!)nxupy&%3{Y*z3
zh$!d*jUM1WimY~f0GFZHq3fjDTWN~BnBF~+-k8d#wb5j<$HLhMWT}#PRk5r5Q4`f<
z(Qnd!V`hGs?N&SyNZX*H$mA*q`;lYzkf(VOXJ)J%@}(uy)h1WA5DSPQVY2zAy%gI-
z29l@QtUM(gJ7`GS)T(>3<JFUNHX*fNY-koE{!c|6)T>;JYJ(&9&6KNAl}%-TgR(Ij
zI2!`B_$7@?4-6*~M{_ZJ<K^jr*a|At^oC7io+x4K^meQQ2hP(gCDH6eg&*7<--<H|
zmv=m0eEr|qON=&E&D|IzJJQhwFeS=PO&vMW3MFrNkyU0!${k;|M<%%>sR73)Aadr=
z-%?onOt}4&0B7pSmlEGUbI_QH_)|B?!@ep!9LiHu8Ld~@qqb6`0t<pMY#Foi*`KNy
zQUpt7Owa%HP=~h83%BZrva7^(@5dz6#CT(ylQ~TiqbU#?R`?rN;@t>?4D@-^;>r?4
zn&!~Q6z&&%p|?;1*Z+lGNc820;fO{wG<;<Z<>!W~G9sec@nky?9QXOfc&PM3{#{ho
z+-H^ngOP_%{;J2yI7|Qq{Nw1WtonC=YcyvS;cKG}RpXZ;%9}s4HLyd!-=U)_12drE
zmDqMIMgP1x8YlgWpQH58a{08t*68LltvQ#!)}=6{lxn$An?@IGfq1?=L<{e?X7g>m
zZY7{WMfi^P*7jr=tr+Eb7oVapil-o!QT!pn*L=f;pgQf+v5Mcy=a<rkWT?$?b8<1U
z(xG`5qD65VoXy^F=HvkbB0fE>M8duVADKN*L$a$+Ub(VrC|*ltRE?yPc#iiJt#?FP
zcD_w#Pry;g!sNP~CGiFi$sZ!=(gKAijfoLgqF%2iipqF!8BMdU>Q8i|K)M|Y?7lYw
zl);ZT%|=AWuvbvn5Mv$6|3${uAZAMLn0r$oaFu>Zoqx`G+RnlOcKR`;>&rg%A3i%m
zq;QBN{I3Ei!g=tEtMos~U_mo1Jvd&<>TGiS+UAW1yXsCk1q0|k(F<N_C4N#+#B`g`
zJ7ET!zkQw|n`e8Bc?@|w#m{HDiAJr_R+w3LZAlqKenSG2*WkBglwh&XmCc*!oM*oP
z?Wbgy^ArCQXXOyC-WYdthR8sKEVMK>ytKQqQ@YT#b}!hGm6AJEo1esX`Wj!<4|{*h
z*oisRkF-L<`77NQ@DGpL8bmSUtrL_INN3N6=lFg6N7L`FE){4#^Z;sK`SoK25!C}t
z&HQqD1HXeufpFWTHZSi#;xD1*g(JXQiy=}}3V`6e^ptkX8AzjU{oL`BWk`hf2D`yr
zAUoiNjTN4uY4-0E{I+yY!##{Yu7STz@sEvSc-Pzc=11LJQ@2WMA&s%F*coo^ZrE+G
z{P^?~9uuDbOtyFxt;_VB4_Si(h_*WqD|;V?4&8Hop4N{_8I?QAL`vApAW2%(H4?xh
zQNB~zS=zHGEs9uMO8C%hn-U0M#Mv*QZTbDYM2|%roIqF%i{ynFLD|1o>o|?WKWOul
z&4;k490pNe+lNuLDN@moL&wmXg;Nun*Iq9d!Na6nD_L3Cj3Hg$Ix(_TNOUXb|2y@i
zfV!6hjW8A5g5c}#Z4(7$u3k14l@HovJ921YI5(VJID=@T7}Eew^iA=d-uc#}aB5WG
zhp|MVUVD}bs|89L!)9j!{u7FDycw?@k7Oo5D+YsG`yDPEo++bRwWpkTN%J!OWoi2+
zoce{n2v+=1GhrKpRIW%wj^>52KKdfaM1{+!qpS*Ny3M;-sX15cA55r6VnhwaY7bsF
z?mK?PgWK;Pml%_A*E!^l_)M(VqnpjMU|kb}WKd;9Vv(BbpTQ*Pk`invrazw&ZYG*r
zHA_4BUv6PW_ni(U3cU$zQt}fupgpzzZl)WYEWyshv(%jERA{F)d(Aw56MAu*3j(vx
zRFbvyiMlGB(ikPMB&?>U?NAdeh7Ds-g=+r^NKB$E(lGZ<I2EVB%7Bmfm~6zo;@^+j
zm6Mx?=HW@D3l&{qYBFXCGC+5*%gN5(IshI~rl2(1&uzVs%Lm1#Uz=rIM^)>;p{!{6
z@Ah}&eW^hB4K762D+|f;A}un4iG;C<VmTr^_%Zc>H$|4UO*&Qp0j$)+*uwJtMnhC@
zNShojh39coTqmjmq}xpMzq{HfneqDwH;%smFPiwal0cn7Rr7W%fG=HO9mrjjmkort
z3!K??&Aq|G=iJL6H3^^NyLVR3t;tn2CMz<*kgeYzd5=bARNC^@vLt(a4wFTM50&$J
z6B?-)VPHJW1xb(kZS39JEhK@SJBNBN>#{mw*W5Sa@{zrJsvF8;`p-VfHKwA-?6<L@
zkqP;M1}U^_AH+rNSzskb_b%KZtcv~i|Ds|HnqXOm+S&HO6ig6{P)KQ~_VAJxddDcV
zx+@(Bk6SQf43MI~VPVe6sHtGM5gdLgCE4{TFrD()iQ#+DLR(~4`Z$3RXutN+^}8`y
zn%yjEAPJ4}#}o&lx~Ioe?p8z6XU!fsdX<@^+-cM9xhg>CJZ~waEZn07AI$@54}Alz
z!Hp=;yr_xxJs>NOBi32!b{`%>dBO&O{~{soHba0Hpck#qacE^CuKA_J#FZ<1+RUqr
zvJAFs^pSsqzO|`s=sf!>05ot#Cx{)=lo)(#kU`}4Q#~zWDN%2%L`xRbF8gRWn?Oj&
z?@g7qMN?89j56o5^2cs}G589_ncQF_Q8@FOETyc$0AN>o<&G01(&jkng2D#p#YZ)|
zenCa8C9*Y#;ZuegpkVo@`<DyP%Q7`>!aF#R>DyLQ+^WRjtpDYZelk3W)jO8?;>Q8v
z_oFY_hQDg_v+BRetjti>)T%<~{Pd)=qb0R-O&Trx`KWSt-;>bxUr=9YZ<Jz=uZhSM
zDFfC=s$uG|w|@}R=!)V&Mk)Wj)}cs-D+xRcrq0eGm}zC1<Ux6&+_q!bR<Psg`h+&D
zC5k1c3IK;lQ0rOn$=Zb^Tma5Qty@kTWOCZJd#g_OJkUS(8mz_eU^HJOhy5RtW2^I|
zw8G$6b~*t+F`dxUql8@<(w-w}dQ$k1np*sCVnT%k`g733fJ=nbIY+0TT%n9)F!?F!
z`mHrv&=*-<jME{K{CWKB5;Q^{hH)^ks4v+~pNMzYkt7X#>z<f^`>ZVWOllLLfQ`y(
z6>3)7^NCQm%nOSIDBv9vxf8qVyvDG$WYImdguk>QX+Ue+goLK2nDq+`{V<yd0Q!QR
zL{m{wUbdOaf2ySMCI^Nq8h}=yQyOf*&mz*cM{4_$ZL&<4buN5E;o85OMInPa2oJTJ
zkys8-?%f&b@KI&3<~X|w@cR)p0`>#iy#V_=rugRBf5EOgH}*kG_VT%dA&Uq(#_t1a
zRhkc&s_6E@t{{cDMtRuqGoup(g`Z$jYs+O;mm1oJ#%qDK%+78sJ|P6NBqOF3vxB{L
z+>aB}CZpB^Iaa`zzt8ycN>}nTtHc^no5WE#iP}C3#row>otk6cC^|#xp4cqiz-${3
zE@n#{>i@ndpAkKnmw7gX0BI%gG?Kzu7^Y<!#82|$4bVH5_ck-(A76@E;CWDf1>in%
zFYhP)WV*KQ<pXH)3sF|PQ<oV=4W?~HedPbiGYb9l=uC)>sf^q{LPI?oDmuz_FKnX!
E2LSsXng9R*

literal 0
HcmV?d00001

-- 
2.25.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [PATCH Rust front-end v2 01/37] Use DW_ATE_UTF for the Rust 'char' type
  2022-08-24 11:59 ` [PATCH Rust front-end v2 01/37] Use DW_ATE_UTF for the Rust 'char' type herron.philip
@ 2022-08-24 14:28   ` Jason Merrill
  0 siblings, 0 replies; 59+ messages in thread
From: Jason Merrill @ 2022-08-24 14:28 UTC (permalink / raw)
  To: philip.herron, gcc-patches; +Cc: gcc-rust, Tom Tromey

On 8/24/22 04:59, herron.philip@googlemail.com wrote:
> From: Tom Tromey <tom@tromey.com>
> 
> The Rust 'char' type should use the DWARF DW_ATE_UTF encoding.

The DWARF changes are OK.

> ---
>   gcc/dwarf2out.cc | 23 ++++++++++++++++++++++-
>   1 file changed, 22 insertions(+), 1 deletion(-)
> 
> diff --git a/gcc/dwarf2out.cc b/gcc/dwarf2out.cc
> index e3920c898f5..a8bccbabca4 100644
> --- a/gcc/dwarf2out.cc
> +++ b/gcc/dwarf2out.cc
> @@ -5600,6 +5600,16 @@ is_fortran (const_tree decl)
>     return is_fortran ();
>   }
>   
> +/* Return TRUE if the language is Rust.  */
> +
> +static inline bool
> +is_rust ()
> +{
> +  unsigned int lang = get_AT_unsigned (comp_unit_die (), DW_AT_language);
> +
> +  return lang == DW_LANG_Rust || lang == DW_LANG_Rust_old;
> +}
> +
>   /* Return TRUE if the language is Ada.  */
>   
>   static inline bool
> @@ -13231,7 +13241,11 @@ base_type_die (tree type, bool reverse)
>   	}
>         if (TYPE_STRING_FLAG (type))
>   	{
> -	  if (TYPE_UNSIGNED (type))
> +	  if ((dwarf_version >= 4 || !dwarf_strict)
> +	      && is_rust ()
> +	      && int_size_in_bytes (type) == 4)
> +	    encoding = DW_ATE_UTF;
> +	  else if (TYPE_UNSIGNED (type))
>   	    encoding = DW_ATE_unsigned_char;
>   	  else
>   	    encoding = DW_ATE_signed_char;
> @@ -25201,6 +25215,13 @@ gen_compile_unit_die (const char *filename)
>       }
>     else if (strcmp (language_string, "GNU F77") == 0)
>       language = DW_LANG_Fortran77;
> +  else if (strcmp (language_string, "GNU Rust") == 0)
> +    {
> +      if (dwarf_version >= 5 || !dwarf_strict)
> +	language = DW_LANG_Rust;
> +      else
> +	language = DW_LANG_Rust_old;
> +    }
>     else if (dwarf_version >= 3 || !dwarf_strict)
>       {
>         if (strcmp (language_string, "GNU Ada") == 0)


^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: Rust frontend patches v2
  2022-08-24 11:59 Rust frontend patches v2 herron.philip
                   ` (36 preceding siblings ...)
  2022-08-24 11:59 ` [PATCH Rust front-end v2 37/37] gccrs: Add README, CONTRIBUTING and compiler logo herron.philip
@ 2022-08-25  9:46 ` Philip Herron
  2022-08-25  9:52   ` Martin Liška
  37 siblings, 1 reply; 59+ messages in thread
From: Philip Herron @ 2022-08-25  9:46 UTC (permalink / raw)
  To: Philip Herron; +Cc: Gcc Patch List, gcc-rust, Mark Wielaard

Hi everyone

I noticed a few patches still didn't make it through here with the
400kb limit. In the meantime, Mark pointed out to me that they can all
be viewed over here:

https://inbox.sourceware.org/gcc-patches/20220824115956.737931-9-philip.herron@embecosm.com/T/#rbff0bb3df2780fecd9ee3d2baa864d9140d24b54

The missing patches on
https://gcc.gnu.org/pipermail/gcc-patches/2022-August/thread.html#600200
are 8, 10, 13, 29, and 22.

Thanks let me know if there is anything more I can do to help.

--Phil

On Wed, 24 Aug 2022 at 13:01, <herron.philip@googlemail.com> wrote:
>
> This is the 2nd patch set for gccrs, since v1 we have dropped the changes
> for target hooks which are not nessecary for us right now. This now
> focuses directly on the front-end the only patch that affects GCC now is a
> tweak to debug info. Note we are close to merging our port of the C++
> constexpr code into our front-end but this patch set does not include this
> yet.
>
> Thanks to Open Source Security, inc and Embecosm for sponsoring this work.
> Special thanks to all of those who have contributed thus far.
>
> See our branch over on https://gcc.gnu.org/git/?p=gcc.git;a=shortlog;h=refs/heads/devel/rust/master
>
> We are currently testing on every commit the following systems:
>
> - Debian i386 - all tests passing
> - Debian testing-x86_64 - all tests passing
> - Fedora arm64 - all tests passing
> - Fedora X86_64 - all tests passing
> - OpenSUSE Leap X86_64 - all tests passing
> - OpenSUSE tw X86_64 - all tests passing
> - Rawhide X86_64 - all tests passing
> - macos x86_64 - all tests passing
> - Debian ppc64 - some tests failing
> - Fedora ppc64le - some tests failing
> - Fedora s390x - some tests failing
>
> The patch set is as follows:
>
> [PATCH Rust front-end v2 01/37] Use DW_ATE_UTF for the Rust 'char'
> [PATCH Rust front-end v2 02/37] gccrs: Add nessecary hooks for a Rust
> [PATCH Rust front-end v2 03/37] gccrs: Add Debug info testsuite
> [PATCH Rust front-end v2 04/37] gccrs: Add link cases testsuite
> [PATCH Rust front-end v2 05/37] gccrs: Add general compilation test
> [PATCH Rust front-end v2 06/37] gccrs: Add execution test cases
> [PATCH Rust front-end v2 07/37] gccrs: Add gcc-check-target
> [PATCH Rust front-end v2 08/37] gccrs: Add the Rust front-end AST
> [PATCH Rust front-end v2 09/37] gccrs: Add Lexer for Rust front-end
> [PATCH Rust front-end v2 10/37] gccrs: Add Parser for Rust front-end
> [PATCH Rust front-end v2 11/37] gccrs: Add expansion pass for the
> [PATCH Rust front-end v2 12/37] gccrs: Add name resolution pass to
> [PATCH Rust front-end v2 13/37] gccrs: Add second intermedite
> [PATCH Rust front-end v2 14/37] gccrs: Add AST to HIR lowering pass
> [PATCH Rust front-end v2 15/37] gccrs: Add wrapper for make_unique
> [PATCH Rust front-end v2 16/37] gccrs: Add port of FNV hash used
> [PATCH Rust front-end v2 17/37] gccrs: Add Rust ABI enum helpers
> [PATCH Rust front-end v2 18/37] gccrs: Add Base62 implementation
> [PATCH Rust front-end v2 19/37] gccrs: Add implementation of Optional
> [PATCH Rust front-end v2 20/37] gccrs: Add attributes checker
> [PATCH Rust front-end v2 21/37] gccrs: Add helpers mappings canonical
> [PATCH Rust front-end v2 22/37] gccrs: Add type resolution and trait
> [PATCH Rust front-end v2 23/37] gccrs: Add unsafe checks for Rust
> [PATCH Rust front-end v2 24/37] gccrs: Add const checker
> [PATCH Rust front-end v2 25/37] gccrs: Add privacy checks
> [PATCH Rust front-end v2 26/37] gccrs: Add dead code scan on HIR
> [PATCH Rust front-end v2 27/37] gccrs: Add unused variable scan
> [PATCH Rust front-end v2 28/37] gccrs: Add metadata ouptput pass
> [PATCH Rust front-end v2 29/37] gccrs: HIR to GCC GENERIC lowering
> [PATCH Rust front-end v2 30/37] gccrs: These are wrappers ported from
> [PATCH Rust front-end v2 31/37] gccrs: Add GCC Rust front-end
> [PATCH Rust front-end v2 32/37] gccrs: Add config-lang.in
> [PATCH Rust front-end v2 33/37] gccrs: add lang-spec.h
> [PATCH Rust front-end v2 34/37] gccrs: add lang.opt
> [PATCH Rust front-end v2 35/37] gccrs: add compiler driver
> [PATCH Rust front-end v2 36/37] gccrs: compiler proper interface
> [PATCH Rust front-end v2 37/37] gccrs: Add README, CONTRIBUTING and
>
> --
> Gcc-rust mailing list
> Gcc-rust@gcc.gnu.org
> https://gcc.gnu.org/mailman/listinfo/gcc-rust

^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: Rust frontend patches v2
  2022-08-25  9:46 ` Rust frontend patches v2 Philip Herron
@ 2022-08-25  9:52   ` Martin Liška
  2022-08-25 10:18     ` Philip Herron
  2022-08-25 11:13     ` Mark Wielaard
  0 siblings, 2 replies; 59+ messages in thread
From: Martin Liška @ 2022-08-25  9:52 UTC (permalink / raw)
  To: Philip Herron
  Cc: Mark Wielaard, gcc-rust, Gcc Patch List, Frank Ch. Eigler,
	Jonathan Wakely

On 8/25/22 11:46, Philip Herron wrote:
> The missing patches on
> https://gcc.gnu.org/pipermail/gcc-patches/2022-August/thread.html#600200
> are 8, 10, 13, 29, and 22.

Hmm, I think our limit is pretty low, sorry for that.

Can you please paste output of du -hs 00*?

What about limit increase, how much space do we have on sourceware infrastructure?

Thanks,
Martin


^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: Rust frontend patches v2
  2022-08-25  9:52   ` Martin Liška
@ 2022-08-25 10:18     ` Philip Herron
  2022-08-25 12:50       ` Frank Ch. Eigler
  2022-08-25 11:13     ` Mark Wielaard
  1 sibling, 1 reply; 59+ messages in thread
From: Philip Herron @ 2022-08-25 10:18 UTC (permalink / raw)
  To: Martin Liška
  Cc: Mark Wielaard, gcc-rust, Gcc Patch List, Frank Ch. Eigler,
	Jonathan Wakely

Hi Martin

4.0K    0001-Use-DW_ATE_UTF-for-the-Rust-char-type.patch
8.0K    0002-gccrs-Add-nessecary-hooks-for-a-Rust-front-end-tests.patch
8.0K    0003-gccrs-Add-Debug-info-testsuite.patch
12K     0004-gccrs-Add-link-cases-testsuite.patch
356K    0005-gccrs-Add-general-compilation-test-cases.patch
132K    0006-gccrs-Add-execution-test-cases.patch
4.0K    0007-gccrs-Add-gcc-check-target-check-rust.patch
656K    0008-gccrs-Add-the-Rust-front-end-AST-data-structures.patch
112K    0009-gccrs-Add-Lexer-for-Rust-front-end.patch
504K    0010-gccrs-Add-Parser-for-Rust-front-end.patch
200K    0011-gccrs-Add-expansion-pass-for-the-Rust-front-end.patch
204K    0012-gccrs-Add-name-resolution-pass-to-the-Rust-front-end.patch
476K    0013-gccrs-Add-second-intermedite-representation-called-H.patch
212K    0014-gccrs-Add-AST-to-HIR-lowering-pass.patch
4.0K    0015-gccrs-Add-wrapper-for-make_unique.patch
4.0K    0016-gccrs-Add-port-of-FNV-hash-used-during-legacy-symbol.patch
4.0K    0017-gccrs-Add-Rust-ABI-enum-helpers.patch
4.0K    0018-gccrs-Add-Base62-implementation.patch
12K     0019-gccrs-Add-implementation-of-Optional.patch
28K     0020-gccrs-Add-attributes-checker.patch
60K     0021-gccrs-Add-helpers-mappings-canonical-path-and-lang-i.patch
628K    0022-gccrs-Add-type-resolution-and-trait-solving-pass.patch
32K     0023-gccrs-Add-unsafe-checks-for-Rust.patch
28K     0024-gccrs-Add-const-checker.patch
72K     0025-gccrs-Add-privacy-checks.patch
24K     0026-gccrs-Add-dead-code-scan-on-HIR.patch
8.0K    0027-gccrs-Add-unused-variable-scan.patch
76K     0028-gccrs-Add-metadata-ouptput-pass.patch
460K    0029-gccrs-HIR-to-GCC-GENERIC-lowering.patch
36K     0030-gccrs-These-are-wrappers-ported-from-reusing-gccgo.patch
16K     0031-gccrs-Add-GCC-Rust-front-end-Make-lang.in.patch
4.0K    0032-gccrs-Add-config-lang.in.patch
4.0K    0033-gccrs-add-lang-spec.h.patch
8.0K    0034-gccrs-add-lang.opt.patch
8.0K    0035-gccrs-add-compiler-driver.patch
64K     0036-gccrs-compiler-proper-interface-kicks-off-the-pipeli.patch
104K    0037-gccrs-Add-README-CONTRIBUTING-and-compiler-logo.patch

I hope this helps. I can do another pass at splitting up on these
patches if it will help.

--Phil

On Thu, 25 Aug 2022 at 10:52, Martin Liška <mliska@suse.cz> wrote:
>
> On 8/25/22 11:46, Philip Herron wrote:
> > The missing patches on
> > https://gcc.gnu.org/pipermail/gcc-patches/2022-August/thread.html#600200
> > are 8, 10, 13, 29, and 22.
>
> Hmm, I think our limit is pretty low, sorry for that.
>
> Can you please paste output of du -hs 00*?
>
> What about limit increase, how much space do we have on sourceware infrastructure?
>
> Thanks,
> Martin
>

^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: Rust frontend patches v2
  2022-08-25  9:52   ` Martin Liška
  2022-08-25 10:18     ` Philip Herron
@ 2022-08-25 11:13     ` Mark Wielaard
  1 sibling, 0 replies; 59+ messages in thread
From: Mark Wielaard @ 2022-08-25 11:13 UTC (permalink / raw)
  To: Martin Liška, Philip Herron
  Cc: gcc-rust, Gcc Patch List, Frank Ch. Eigler, Jonathan Wakely

Hi Martin,

On Thu, 2022-08-25 at 11:52 +0200, Martin Liška wrote:
> What about limit increase, how much space do we have on sourceware
> infrastructure?

Feel free to increase the limits, there is a couple of hundred GB free
on sourceware and we can add more.

The public-inbox instance at inbox.sourceware.org was setup to
workaround some of these mailman limits and make it easier to handle
patch emails. It was only finalized last week, but testing shows it
seems ready for more wide use. Let me write an announcement for gcc.

Cheers,

Mark

^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: Rust frontend patches v2
  2022-08-25 10:18     ` Philip Herron
@ 2022-08-25 12:50       ` Frank Ch. Eigler
  2022-08-25 13:44         ` Philip Herron
  0 siblings, 1 reply; 59+ messages in thread
From: Frank Ch. Eigler @ 2022-08-25 12:50 UTC (permalink / raw)
  To: Philip Herron
  Cc: Martin Liška, Mark Wielaard, gcc-rust, Gcc Patch List,
	Jonathan Wakely

Hi -


> 12K     0004-gccrs-Add-link-cases-testsuite.patch
> 356K    0005-gccrs-Add-general-compilation-test-cases.patch
> 132K    0006-gccrs-Add-execution-test-cases.patch
> 4.0K    0007-gccrs-Add-gcc-check-target-check-rust.patch
> 656K    0008-gccrs-Add-the-Rust-front-end-AST-data-structures.patch
> 112K    0009-gccrs-Add-Lexer-for-Rust-front-end.patch
> 504K    0010-gccrs-Add-Parser-for-Rust-front-end.patch
> 200K    0011-gccrs-Add-expansion-pass-for-the-Rust-front-end.patch
> 204K    0012-gccrs-Add-name-resolution-pass-to-the-Rust-front-end.patch
> 476K    0013-gccrs-Add-second-intermedite-representation-called-H.patch
> [...]

Just curious whether a human reviewer expected to read through this
much content?  If not, and if this structure is only for machine /
bisecting purposes, maybe they're not worth also emailing.

- FChE

^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: Rust frontend patches v2
  2022-08-25 12:50       ` Frank Ch. Eigler
@ 2022-08-25 13:44         ` Philip Herron
  0 siblings, 0 replies; 59+ messages in thread
From: Philip Herron @ 2022-08-25 13:44 UTC (permalink / raw)
  To: Frank Ch. Eigler
  Cc: Martin Liška, Mark Wielaard, gcc-rust, Gcc Patch List,
	Jonathan Wakely

On Thu, 25 Aug 2022 at 13:50, Frank Ch. Eigler <fche@elastic.org> wrote:
>
> Hi -
>
>
> > 12K     0004-gccrs-Add-link-cases-testsuite.patch
> > 356K    0005-gccrs-Add-general-compilation-test-cases.patch
> > 132K    0006-gccrs-Add-execution-test-cases.patch
> > 4.0K    0007-gccrs-Add-gcc-check-target-check-rust.patch
> > 656K    0008-gccrs-Add-the-Rust-front-end-AST-data-structures.patch
> > 112K    0009-gccrs-Add-Lexer-for-Rust-front-end.patch
> > 504K    0010-gccrs-Add-Parser-for-Rust-front-end.patch
> > 200K    0011-gccrs-Add-expansion-pass-for-the-Rust-front-end.patch
> > 204K    0012-gccrs-Add-name-resolution-pass-to-the-Rust-front-end.patch
> > 476K    0013-gccrs-Add-second-intermedite-representation-called-H.patch
> > [...]
>
> Just curious whether a human reviewer expected to read through this
> much content?  If not, and if this structure is only for machine /
> bisecting purposes, maybe they're not worth also emailing.
>
> - FChE

That's a good point. I prefer reading this stuff on the git branch either:

https://gcc.gnu.org/git/?p=gcc.git;a=shortlog;h=refs/heads/devel/rust/master
https://github.com/Rust-GCC/gccrs

The first patch here is a nice isolated GCC change the rest of the
patches are either pure front-end code or setting up the rust target
for the test suite.

The main patches I think people will be interested in are:

[PATCH Rust front-end v2 01/37] Use DW_ATE_UTF for the Rust 'char'
[PATCH Rust front-end v2 07/37] gccrs: Add gcc-check-target
[PATCH Rust front-end v2 31/37] gccrs: Add GCC Rust front-end
[PATCH Rust front-end v2 32/37] gccrs: Add config-lang.in
[PATCH Rust front-end v2 33/37] gccrs: add lang-spec.h
[PATCH Rust front-end v2 34/37] gccrs: add lang.opt
[PATCH Rust front-end v2 35/37] gccrs: add compiler driver
[PATCH Rust front-end v2 36/37] gccrs: compiler proper interface

Eventually, when we clean it up, our port of the constexpr.cc from the
C++ front-end might be of interest to other front-ends.

--Phil

^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [PATCH Rust front-end v2 02/37] gccrs: Add nessecary hooks for a Rust front-end testsuite
  2022-08-24 11:59 ` [PATCH Rust front-end v2 02/37] gccrs: Add nessecary hooks for a Rust front-end testsuite herron.philip
@ 2022-09-10  4:05   ` Mike Stump
  0 siblings, 0 replies; 59+ messages in thread
From: Mike Stump @ 2022-09-10  4:05 UTC (permalink / raw)
  To: Philip Herron; +Cc: gcc-patches, gcc-rust, Thomas Schwinge, Marc Poulhiès

Ok.

> On Aug 24, 2022, at 4:59 AM, herron.philip@googlemail.com wrote:
> 
> From: Philip Herron <philip.herron@embecosm.com>
> 
> This copy's over code from other front-end testsuites to enable testing
> for the rust front-end specifically.
> 
> Co-authored-by: Marc Poulhiès <dkm@kataplop.net>
> Co-authored-by: Thomas Schwinge <thomas@codesourcery.com>
> ---
> gcc/testsuite/lib/rust-dg.exp |  49 +++++++++
> gcc/testsuite/lib/rust.exp    | 186 ++++++++++++++++++++++++++++++++++

^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [PATCH Rust front-end v2 09/37] gccrs: Add Lexer for Rust front-end
  2022-08-24 11:59 ` [PATCH Rust front-end v2 09/37] gccrs: Add Lexer for Rust front-end herron.philip
@ 2022-09-14 13:30   ` Richard Biener
  2022-09-14 13:39     ` Jakub Jelinek
  0 siblings, 1 reply; 59+ messages in thread
From: Richard Biener @ 2022-09-14 13:30 UTC (permalink / raw)
  To: philip.herron
  Cc: gcc-patches, The Other, Arthur Cohen, Mark Wielaard, gcc-rust

On Wed, Aug 24, 2022 at 2:04 PM <herron.philip@googlemail.com> wrote:
>
> From: The Other <simplytheother@gmail.com>
>
> The lexer is refered to as a ManagedTokenSource within the parser, this
> lexer does not currently support unicode but serves as a starting point
> to do so.
>
> Co-authored-by: Philip Herron <philip.herron@embecosm.com>
> Co-authored-by: Arthur Cohen <arthur.cohen@embecosm.com>
> Co-authored-by: Mark Wielaard <mark@klomp.org>
> ---
>  gcc/rust/lex/rust-codepoint.h  |   46 +
>  gcc/rust/lex/rust-lex.cc       | 2729 ++++++++++++++++++++++++++++++++
>  gcc/rust/lex/rust-lex.h        |  271 ++++
>  gcc/rust/lex/rust-token.cc     |  135 ++
>  gcc/rust/lex/rust-token.h      |  455 ++++++
>  gcc/rust/rust-buffered-queue.h |  204 +++
>  6 files changed, 3840 insertions(+)
>  create mode 100644 gcc/rust/lex/rust-codepoint.h
>  create mode 100644 gcc/rust/lex/rust-lex.cc
>  create mode 100644 gcc/rust/lex/rust-lex.h
>  create mode 100644 gcc/rust/lex/rust-token.cc
>  create mode 100644 gcc/rust/lex/rust-token.h
>  create mode 100644 gcc/rust/rust-buffered-queue.h
>
> diff --git a/gcc/rust/lex/rust-codepoint.h b/gcc/rust/lex/rust-codepoint.h
> new file mode 100644
> index 00000000000..22da080bbb2
> --- /dev/null
> +++ b/gcc/rust/lex/rust-codepoint.h
> @@ -0,0 +1,46 @@
> +// 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_CODEPOINT_H
> +#define RUST_CODEPOINT_H
> +
> +#include <string>
> +
> +namespace Rust {
> +struct Codepoint
> +{
> +  uint32_t value;
> +
> +  // Creates a zero codepoint.
> +  Codepoint () : value (0) {}
> +
> +  // Creates a codepoint from an encoded UTF-8 value.
> +  Codepoint (uint32_t value) : value (value) {}
> +
> +  static Codepoint eof () { return Codepoint (UINT32_MAX); }
> +  bool is_eof () const { return value == UINT32_MAX; }
> +
> +  // Returns a C++ string containing string value of codepoint.
> +  std::string as_string ();
> +
> +  bool operator== (Codepoint other) const { return value == other.value; }
> +  bool operator!= (Codepoint other) const { return !operator== (other); }
> +};
> +} // namespace Rust
> +
> +#endif
> diff --git a/gcc/rust/lex/rust-lex.cc b/gcc/rust/lex/rust-lex.cc
> new file mode 100644
> index 00000000000..70e6b50209f
> --- /dev/null
> +++ b/gcc/rust/lex/rust-lex.cc
> @@ -0,0 +1,2729 @@
> +// 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/>.
> +
> +#include "rust-lex.h"
> +
> +#include "rust-system.h"      // for rust_assert and rust_unreachable
> +#include "rust-diagnostics.h" // for rust_error_at
> +#include "rust-linemap.h"
> +#include "rust-session-manager.h"
> +#include "safe-ctype.h"

just diving into a random patch here - I'm assuming I can take rust-lex.cc as
a boiler-plate example for the #include structure.

In GCC all files should start with #including "config.h" followed by
"system.h" where _all_ system, including C++ standard library headers
should be pulled via system.h to allow working around OS and system
compiler issues.

It might be that rust-system.h plays the role of config.h + system.h
but then the rust-lex.h include is before it.

rust-codepoint.h including <string> is also problematic btw.

Richard.

> +namespace Rust {
> +// TODO: move to separate compilation unit?
> +// overload += for uint32_t to allow 32-bit encoded utf-8 to be added
> +std::string &
> +operator+= (std::string &str, Codepoint char32)
> +{
> +  if (char32.value < 0x80)
> +    {
> +      str += static_cast<char> (char32.value);
> +    }
> +  else if (char32.value < (0x1F + 1) << (1 * 6))
> +    {
> +      str += static_cast<char> (0xC0 | ((char32.value >> 6) & 0x1F));
> +      str += static_cast<char> (0x80 | ((char32.value >> 0) & 0x3F));
> +    }
> +  else if (char32.value < (0x0F + 1) << (2 * 6))
> +    {
> +      str += static_cast<char> (0xE0 | ((char32.value >> 12) & 0x0F));
> +      str += static_cast<char> (0x80 | ((char32.value >> 6) & 0x3F));
> +      str += static_cast<char> (0x80 | ((char32.value >> 0) & 0x3F));
> +    }
> +  else if (char32.value < (0x07 + 1) << (3 * 6))
> +    {
> +      str += static_cast<char> (0xF0 | ((char32.value >> 18) & 0x07));
> +      str += static_cast<char> (0x80 | ((char32.value >> 12) & 0x3F));
> +      str += static_cast<char> (0x80 | ((char32.value >> 6) & 0x3F));
> +      str += static_cast<char> (0x80 | ((char32.value >> 0) & 0x3F));
> +    }
> +  else
> +    {
> +      rust_debug ("Invalid unicode codepoint found: '%u' ", char32.value);
> +    }
> +  return str;
> +}
> +
> +std::string
> +Codepoint::as_string ()
> +{
> +  std::string str;
> +
> +  // str += Codepoint (value);
> +  str += *this;
> +
> +  return str;
> +}
> +
> +/* Includes all allowable float digits EXCEPT _ and . as that needs lookahead
> + * for handling. */
> +bool
> +is_float_digit (char number)
> +{
> +  return ISDIGIT (number) || number == 'E' || number == 'e';
> +}
> +
> +/* Basically ISXDIGIT from safe-ctype but may change if Rust's encoding or
> + * whatever is different */
> +bool
> +is_x_digit (char number)
> +{
> +  return ISXDIGIT (number);
> +}
> +
> +bool
> +is_octal_digit (char number)
> +{
> +  return number >= '0' && number <= '7';
> +}
> +
> +bool
> +is_bin_digit (char number)
> +{
> +  return number == '0' || number == '1';
> +}
> +
> +bool
> +check_valid_float_dot_end (char character)
> +{
> +  return character != '.' && character != '_' && !ISALPHA (character);
> +}
> +
> +// ISSPACE from safe-ctype but may change in future
> +bool
> +is_whitespace (char character)
> +{
> +  return ISSPACE (character);
> +}
> +
> +bool
> +is_non_decimal_int_literal_separator (char character)
> +{
> +  return character == 'x' || character == 'o' || character == 'b';
> +}
> +
> +Lexer::Lexer (const std::string &input)
> +  : input (RAIIFile::create_error ()), current_line (1), current_column (1),
> +    line_map (nullptr), raw_input_source (new BufferInputSource (input, 0)),
> +    input_queue{*raw_input_source}, token_queue (TokenSource (this))
> +{}
> +
> +Lexer::Lexer (const char *filename, RAIIFile file_input, Linemap *linemap)
> +  : input (std::move (file_input)), current_line (1), current_column (1),
> +    line_map (linemap),
> +    raw_input_source (new FileInputSource (input.get_raw ())),
> +    input_queue{*raw_input_source}, token_queue (TokenSource (this))
> +{
> +  // inform line_table that file is being entered and is in line 1
> +  if (linemap)
> +    line_map->start_file (filename, current_line);
> +}
> +
> +Lexer::~Lexer ()
> +{
> +  /* ok apparently stop (which is equivalent of original code in destructor) is
> +   * meant to be called after all files have finished parsing, for cleanup. On
> +   * the other hand, actual code that it calls to leave a certain line map is
> +   * mentioned in GCC docs as being useful for "just leaving an included header"
> +   * and stuff like that, so this line mapping functionality may need fixing.
> +   * FIXME: find out whether this occurs. */
> +
> +  // line_map->stop();
> +}
> +
> +/* TODO: need to optimise somehow to avoid the virtual function call in the
> + * tight loop. Best idea at the moment is CRTP, but that might make lexer
> + * implementation annoying when storing the "base class" (i.e. would need
> + * template parameter everywhere), although in practice it would mostly just
> + * look ugly and make enclosing classes like Parser also require a type
> + * parameter. At this point a macro might be better. OK I guess macros can be
> + * replaced by constexpr if or something if possible. */
> +Location
> +Lexer::get_current_location ()
> +{
> +  if (line_map)
> +    return line_map->get_location (current_column);
> +  else
> +    // If we have no linemap, we're lexing something without proper locations
> +    return Location ();
> +}
> +
> +int
> +Lexer::peek_input (int n)
> +{
> +  return input_queue.peek (n);
> +}
> +
> +int
> +Lexer::peek_input ()
> +{
> +  return peek_input (0);
> +}
> +
> +void
> +Lexer::skip_input (int n)
> +{
> +  input_queue.skip (n);
> +}
> +
> +void
> +Lexer::skip_input ()
> +{
> +  skip_input (0);
> +}
> +
> +void
> +Lexer::replace_current_token (TokenPtr replacement)
> +{
> +  token_queue.replace_current_value (replacement);
> +
> +  rust_debug ("called 'replace_current_token' - this is deprecated");
> +}
> +
> +/* shitty anonymous namespace that can only be accessed inside the compilation
> + * unit - used for classify_keyword binary search in sorted array of keywords
> + * created with x-macros. */
> +namespace {
> +// TODO: make constexpr when update to c++20
> +const std::string keyword_index[] = {
> +#define RS_TOKEN(x, y)
> +#define RS_TOKEN_KEYWORD(name, keyword) keyword,
> +  RS_TOKEN_LIST
> +#undef RS_TOKEN_KEYWORD
> +#undef RS_TOKEN
> +};
> +
> +constexpr TokenId keyword_keys[] = {
> +#define RS_TOKEN(x, y)
> +#define RS_TOKEN_KEYWORD(name, keyword) name,
> +  RS_TOKEN_LIST
> +#undef RS_TOKEN_KEYWORD
> +#undef RS_TOKEN
> +};
> +
> +constexpr int num_keywords = sizeof (keyword_index) / sizeof (*keyword_index);
> +} // namespace
> +
> +/* Determines whether the string passed in is a keyword or not. If it is, it
> + * returns the keyword name.  */
> +TokenId
> +Lexer::classify_keyword (const std::string &str)
> +{
> +  const std::string *last = keyword_index + num_keywords;
> +  const std::string *idx = std::lower_bound (keyword_index, last, str);
> +
> +  if (idx == last || str != *idx)
> +    return IDENTIFIER;
> +
> +  // TODO: possibly replace this x-macro system with something like hash map?
> +
> +  // We now have the expected token ID of the reserved keyword. However, some
> +  // keywords are reserved starting in certain editions. For example, `try` is
> +  // only a reserved keyword in editions >=2018. The language might gain new
> +  // reserved keywords in the future.
> +  //
> +  // https://doc.rust-lang.org/reference/keywords.html#reserved-keywords
> +  auto id = keyword_keys[idx - keyword_index];
> +
> +  // `try` is not a reserved keyword before 2018
> +  if (Session::get_instance ().options.get_edition ()
> +       == CompileOptions::Edition::E2015
> +      && id == TRY)
> +    return IDENTIFIER;
> +
> +  return id;
> +}
> +
> +TokenPtr
> +Lexer::build_token ()
> +{
> +  // loop to go through multiple characters to build a single token
> +  while (true)
> +    {
> +      Location loc = get_current_location ();
> +      current_char = peek_input ();
> +      skip_input ();
> +
> +      // detect UTF8 bom
> +      //
> +      // Must be the first thing on the first line.
> +      // There might be an optional BOM (Byte Order Mark), which for UTF-8 is
> +      // the three bytes 0xEF, 0xBB and 0xBF. These can simply be skipped.
> +      if (current_line == 1 && current_column == 1 && current_char == 0xef
> +         && peek_input () == 0xbb && peek_input (1) == 0xbf)
> +       {
> +         skip_input (1);
> +         current_char = peek_input ();
> +         skip_input ();
> +       }
> +
> +      // detect shebang
> +      // Must be the first thing on the first line, starting with #!
> +      // But since an attribute can also start with an #! we don't count it as a
> +      // shebang line when after any whitespace or comments there is a [. If it
> +      // is a shebang line we simple drop the line. Otherwise we don't consume
> +      // any characters and fall through to the real tokenizer.
> +      if (current_line == 1 && current_column == 1 && current_char == '#'
> +         && peek_input () == '!')
> +       {
> +         int n = 1;
> +         while (true)
> +           {
> +             int next_char = peek_input (n);
> +             if (is_whitespace (next_char))
> +               n++;
> +             else if ((next_char == '/' && peek_input (n + 1) == '/'
> +                       && peek_input (n + 2) != '!'
> +                       && peek_input (n + 2) != '/')
> +                      || (next_char == '/' && peek_input (n + 1) == '/'
> +                          && peek_input (n + 2) == '/'
> +                          && peek_input (n + 3) == '/'))
> +               {
> +                 // two // or four ////
> +                 // A single line comment
> +                 // (but not an inner or outer doc comment)
> +                 n += 2;
> +                 next_char = peek_input (n);
> +                 while (next_char != '\n' && next_char != EOF)
> +                   {
> +                     n++;
> +                     next_char = peek_input (n);
> +                   }
> +                 if (next_char == '\n')
> +                   n++;
> +               }
> +             else if (next_char == '/' && peek_input (n + 1) == '*'
> +                      && peek_input (n + 2) == '*'
> +                      && peek_input (n + 3) == '/')
> +               {
> +                 /**/
> +                 n += 4;
> +               }
> +             else if (next_char == '/' && peek_input (n + 1) == '*'
> +                      && peek_input (n + 2) == '*' && peek_input (n + 3) == '*'
> +                      && peek_input (n + 4) == '/')
> +               {
> +                 /***/
> +                 n += 5;
> +               }
> +             else if ((next_char == '/' && peek_input (n + 1) == '*'
> +                       && peek_input (n + 2) != '*'
> +                       && peek_input (n + 2) != '!')
> +                      || (next_char == '/' && peek_input (n + 1) == '*'
> +                          && peek_input (n + 2) == '*'
> +                          && peek_input (n + 3) == '*'))
> +               {
> +                 // one /* or three /***
> +                 // Start of a block comment
> +                 // (but not an inner or outer doc comment)
> +                 n += 2;
> +                 int level = 1;
> +                 while (level > 0)
> +                   {
> +                     if (peek_input (n) == EOF)
> +                       break;
> +                     else if (peek_input (n) == '/'
> +                              && peek_input (n + 1) == '*')
> +                       {
> +                         n += 2;
> +                         level += 1;
> +                       }
> +                     else if (peek_input (n) == '*'
> +                              && peek_input (n + 1) == '/')
> +                       {
> +                         n += 2;
> +                         level -= 1;
> +                       }
> +                     else
> +                       n++;
> +                   }
> +               }
> +             else if (next_char != '[')
> +               {
> +                 // definitely shebang, ignore the first line
> +                 while (current_char != '\n' && current_char != EOF)
> +                   {
> +                     current_char = peek_input ();
> +                     skip_input ();
> +                   }
> +
> +                 // newline
> +                 current_line++;
> +                 current_column = 1;
> +                 // tell line_table that new line starts
> +                 start_line (current_line, max_column_hint);
> +                 break;
> +               }
> +             else
> +               break; /* Definitely not a shebang line. */
> +           }
> +       }
> +
> +      // return end of file token if end of file
> +      if (current_char == EOF)
> +       return Token::make (END_OF_FILE, loc);
> +
> +      // if not end of file, start tokenising
> +      switch (current_char)
> +       {
> +       /* ignore whitespace characters for tokens but continue updating
> +        * location */
> +       case '\n': // newline
> +         current_line++;
> +         current_column = 1;
> +         // tell line_table that new line starts
> +         start_line (current_line, max_column_hint);
> +         continue;
> +       case '\r': // cr
> +         // Ignore, we expect a newline (lf) soon.
> +         continue;
> +       case ' ': // space
> +         current_column++;
> +         continue;
> +       case '\t': // tab
> +         // width of a tab is not well-defined, assume 8 spaces
> +         current_column += 8;
> +         continue;
> +
> +       // punctuation - actual tokens
> +       case '=':
> +         if (peek_input () == '>')
> +           {
> +             // match arm arrow
> +             skip_input ();
> +             current_column += 2;
> +
> +             return Token::make (MATCH_ARROW, loc);
> +           }
> +         else if (peek_input () == '=')
> +           {
> +             // equality operator
> +             skip_input ();
> +             current_column += 2;
> +
> +             return Token::make (EQUAL_EQUAL, loc);
> +           }
> +         else
> +           {
> +             // assignment operator
> +             current_column++;
> +             return Token::make (EQUAL, loc);
> +           }
> +       case '(':
> +         current_column++;
> +         return Token::make (LEFT_PAREN, loc);
> +       case '-':
> +         if (peek_input () == '>')
> +           {
> +             // return type specifier
> +             skip_input ();
> +             current_column += 2;
> +
> +             return Token::make (RETURN_TYPE, loc);
> +           }
> +         else if (peek_input () == '=')
> +           {
> +             // minus-assign
> +             skip_input ();
> +             current_column += 2;
> +
> +             return Token::make (MINUS_EQ, loc);
> +           }
> +         else
> +           {
> +             // minus
> +             current_column++;
> +             return Token::make (MINUS, loc);
> +           }
> +       case '+':
> +         if (peek_input () == '=')
> +           {
> +             // add-assign
> +             skip_input ();
> +             current_column += 2;
> +
> +             return Token::make (PLUS_EQ, loc);
> +           }
> +         else
> +           {
> +             // add
> +             current_column++;
> +             return Token::make (PLUS, loc);
> +           }
> +       case ')':
> +         current_column++;
> +         return Token::make (RIGHT_PAREN, loc);
> +       case ';':
> +         current_column++;
> +         return Token::make (SEMICOLON, loc);
> +       case '*':
> +         if (peek_input () == '=')
> +           {
> +             // multiplication-assign
> +             skip_input ();
> +             current_column += 2;
> +
> +             return Token::make (ASTERISK_EQ, loc);
> +           }
> +         else
> +           {
> +             // multiplication
> +             current_column++;
> +             return Token::make (ASTERISK, loc);
> +           }
> +       case ',':
> +         current_column++;
> +         return Token::make (COMMA, loc);
> +       case '/':
> +         if (peek_input () == '=')
> +           {
> +             // division-assign
> +             skip_input ();
> +             current_column += 2;
> +
> +             return Token::make (DIV_EQ, loc);
> +           }
> +         else if ((peek_input () == '/' && peek_input (1) != '!'
> +                   && peek_input (1) != '/')
> +                  || (peek_input () == '/' && peek_input (1) == '/'
> +                      && peek_input (2) == '/'))
> +           {
> +             // two // or four ////
> +             // single line comment
> +             // (but not an inner or outer doc comment)
> +             skip_input ();
> +             current_column += 2;
> +             current_char = peek_input ();
> +
> +             // basically ignore until line finishes
> +             while (current_char != '\n' && current_char != EOF)
> +               {
> +                 skip_input ();
> +                 current_column++; // not used
> +                 current_char = peek_input ();
> +               }
> +             continue;
> +           }
> +         else if (peek_input () == '/'
> +                  && (peek_input (1) == '!' || peek_input (1) == '/'))
> +           {
> +             /* single line doc comment, inner or outer.  */
> +             bool is_inner = peek_input (1) == '!';
> +             skip_input (1);
> +             current_column += 3;
> +
> +             std::string str;
> +             str.reserve (32);
> +             current_char = peek_input ();
> +             while (current_char != '\n')
> +               {
> +                 skip_input ();
> +                 if (current_char == '\r')
> +                   {
> +                     char next_char = peek_input ();
> +                     if (next_char == '\n')
> +                       {
> +                         current_char = '\n';
> +                         break;
> +                       }
> +                     rust_error_at (
> +                       loc, "Isolated CR %<\\r%> not allowed in doc comment");
> +                     current_char = next_char;
> +                     continue;
> +                   }
> +                 if (current_char == EOF)
> +                   {
> +                     rust_error_at (
> +                       loc, "unexpected EOF while looking for end of comment");
> +                     break;
> +                   }
> +                 str += current_char;
> +                 current_char = peek_input ();
> +               }
> +             skip_input ();
> +             current_line++;
> +             current_column = 1;
> +             // tell line_table that new line starts
> +             start_line (current_line, max_column_hint);
> +
> +             str.shrink_to_fit ();
> +             if (is_inner)
> +               return Token::make_inner_doc_comment (loc, std::move (str));
> +             else
> +               return Token::make_outer_doc_comment (loc, std::move (str));
> +           }
> +         else if (peek_input () == '*' && peek_input (1) == '*'
> +                  && peek_input (2) == '/')
> +           {
> +             /**/
> +             skip_input (2);
> +             current_column += 4;
> +             continue;
> +           }
> +         else if (peek_input () == '*' && peek_input (1) == '*'
> +                  && peek_input (2) == '*' && peek_input (3) == '/')
> +           {
> +             /***/
> +             skip_input (3);
> +             current_column += 5;
> +             continue;
> +           }
> +         else if ((peek_input () == '*' && peek_input (1) != '!'
> +                   && peek_input (1) != '*')
> +                  || (peek_input () == '*' && peek_input (1) == '*'
> +                      && peek_input (2) == '*'))
> +           {
> +             // one /* or three /***
> +             // block comment
> +             // (but not an inner or outer doc comment)
> +             skip_input ();
> +             current_column += 2;
> +
> +             int level = 1;
> +             while (level > 0)
> +               {
> +                 current_char = peek_input ();
> +
> +                 if (current_char == EOF)
> +                   {
> +                     rust_error_at (
> +                       loc, "unexpected EOF while looking for end of comment");
> +                     break;
> +                   }
> +
> +                 // if /* found
> +                 if (current_char == '/' && peek_input (1) == '*')
> +                   {
> +                     // skip /* characters
> +                     skip_input (1);
> +
> +                     current_column += 2;
> +
> +                     level += 1;
> +                     continue;
> +                   }
> +
> +                 // ignore until */ is found
> +                 if (current_char == '*' && peek_input (1) == '/')
> +                   {
> +                     // skip */ characters
> +                     skip_input (1);
> +
> +                     current_column += 2;
> +
> +                     level -= 1;
> +                     continue;
> +                   }
> +
> +                 if (current_char == '\n')
> +                   {
> +                     skip_input ();
> +                     current_line++;
> +                     current_column = 1;
> +                     // tell line_table that new line starts
> +                     start_line (current_line, max_column_hint);
> +                     continue;
> +                   }
> +
> +                 skip_input ();
> +                 current_column++;
> +               }
> +
> +             // refresh new token
> +             continue;
> +           }
> +         else if (peek_input () == '*'
> +                  && (peek_input (1) == '!' || peek_input (1) == '*'))
> +           {
> +             // block doc comment, inner /*! or outer /**
> +             bool is_inner = peek_input (1) == '!';
> +             skip_input (1);
> +             current_column += 3;
> +
> +             std::string str;
> +             str.reserve (96);
> +
> +             int level = 1;
> +             while (level > 0)
> +               {
> +                 current_char = peek_input ();
> +
> +                 if (current_char == EOF)
> +                   {
> +                     rust_error_at (
> +                       loc, "unexpected EOF while looking for end of comment");
> +                     break;
> +                   }
> +
> +                 // if /* found
> +                 if (current_char == '/' && peek_input (1) == '*')
> +                   {
> +                     // skip /* characters
> +                     skip_input (1);
> +                     current_column += 2;
> +
> +                     level += 1;
> +                     str += "/*";
> +                     continue;
> +                   }
> +
> +                 // ignore until */ is found
> +                 if (current_char == '*' && peek_input (1) == '/')
> +                   {
> +                     // skip */ characters
> +                     skip_input (1);
> +                     current_column += 2;
> +
> +                     level -= 1;
> +                     if (level > 0)
> +                       str += "*/";
> +                     continue;
> +                   }
> +
> +                 if (current_char == '\r' && peek_input (1) != '\n')
> +                   rust_error_at (
> +                     loc, "Isolated CR %<\\r%> not allowed in doc comment");
> +
> +                 if (current_char == '\n')
> +                   {
> +                     skip_input ();
> +                     current_line++;
> +                     current_column = 1;
> +                     // tell line_table that new line starts
> +                     start_line (current_line, max_column_hint);
> +                     str += '\n';
> +                     continue;
> +                   }
> +
> +                 str += current_char;
> +                 skip_input ();
> +                 current_column++;
> +               }
> +
> +             str.shrink_to_fit ();
> +             if (is_inner)
> +               return Token::make_inner_doc_comment (loc, std::move (str));
> +             else
> +               return Token::make_outer_doc_comment (loc, std::move (str));
> +           }
> +         else
> +           {
> +             // division
> +             current_column++;
> +             return Token::make (DIV, loc);
> +           }
> +       case '%':
> +         if (peek_input () == '=')
> +           {
> +             // modulo-assign
> +             skip_input ();
> +             current_column += 2;
> +
> +             return Token::make (PERCENT_EQ, loc);
> +           }
> +         else
> +           {
> +             // modulo
> +             current_column++;
> +             return Token::make (PERCENT, loc);
> +           }
> +       case '^':
> +         if (peek_input () == '=')
> +           {
> +             // xor-assign?
> +             skip_input ();
> +             current_column += 2;
> +
> +             return Token::make (CARET_EQ, loc);
> +           }
> +         else
> +           {
> +             // xor?
> +             current_column++;
> +             return Token::make (CARET, loc);
> +           }
> +       case '<':
> +         if (peek_input () == '<')
> +           {
> +             if (peek_input (1) == '=')
> +               {
> +                 // left-shift assign
> +                 skip_input (1);
> +                 current_column += 3;
> +
> +                 return Token::make (LEFT_SHIFT_EQ, loc);
> +               }
> +             else
> +               {
> +                 // left-shift
> +                 skip_input ();
> +                 current_column += 2;
> +
> +                 return Token::make (LEFT_SHIFT, loc);
> +               }
> +           }
> +         else if (peek_input () == '=')
> +           {
> +             // smaller than or equal to
> +             skip_input ();
> +             current_column += 2;
> +
> +             return Token::make (LESS_OR_EQUAL, loc);
> +           }
> +         else
> +           {
> +             // smaller than
> +             current_column++;
> +             return Token::make (LEFT_ANGLE, loc);
> +           }
> +         break;
> +       case '>':
> +         if (peek_input () == '>')
> +           {
> +             if (peek_input (1) == '=')
> +               {
> +                 // right-shift-assign
> +                 skip_input (1);
> +                 current_column += 3;
> +
> +                 return Token::make (RIGHT_SHIFT_EQ, loc);
> +               }
> +             else
> +               {
> +                 // right-shift
> +                 skip_input ();
> +                 current_column += 2;
> +
> +                 return Token::make (RIGHT_SHIFT, loc);
> +               }
> +           }
> +         else if (peek_input () == '=')
> +           {
> +             // larger than or equal to
> +             skip_input ();
> +             current_column += 2;
> +
> +             return Token::make (GREATER_OR_EQUAL, loc);
> +           }
> +         else
> +           {
> +             // larger than
> +             current_column++;
> +             return Token::make (RIGHT_ANGLE, loc);
> +           }
> +       case ':':
> +         if (peek_input () == ':')
> +           {
> +             // scope resolution ::
> +             skip_input ();
> +             current_column += 2;
> +
> +             return Token::make (SCOPE_RESOLUTION, loc);
> +           }
> +         else
> +           {
> +             // single colon :
> +             current_column++;
> +             return Token::make (COLON, loc);
> +           }
> +       case '!':
> +         // no special handling for macros in lexer?
> +         if (peek_input () == '=')
> +           {
> +             // not equal boolean operator
> +             skip_input ();
> +             current_column += 2;
> +
> +             return Token::make (NOT_EQUAL, loc);
> +           }
> +         else
> +           {
> +             // not equal unary operator
> +             current_column++;
> +
> +             return Token::make (EXCLAM, loc);
> +           }
> +       case '?':
> +         current_column++;
> +         return Token::make (QUESTION_MARK, loc);
> +       case '#':
> +         current_column++;
> +         return Token::make (HASH, loc);
> +       case '[':
> +         current_column++;
> +         return Token::make (LEFT_SQUARE, loc);
> +       case ']':
> +         current_column++;
> +         return Token::make (RIGHT_SQUARE, loc);
> +       case '{':
> +         current_column++;
> +         return Token::make (LEFT_CURLY, loc);
> +       case '}':
> +         current_column++;
> +         return Token::make (RIGHT_CURLY, loc);
> +       case '@':
> +         current_column++;
> +         return Token::make (PATTERN_BIND, loc);
> +       case '$':
> +         current_column++;
> +         return Token::make (DOLLAR_SIGN, loc);
> +       case '~':
> +         current_column++;
> +         return Token::make (TILDE, loc);
> +       case '\\':
> +         current_column++;
> +         return Token::make (BACKSLASH, loc);
> +       case '`':
> +         current_column++;
> +         return Token::make (BACKTICK, loc);
> +       case '|':
> +         if (peek_input () == '=')
> +           {
> +             // bitwise or-assign?
> +             skip_input ();
> +             current_column += 2;
> +
> +             return Token::make (PIPE_EQ, loc);
> +           }
> +         else if (peek_input () == '|')
> +           {
> +             // logical or
> +             skip_input ();
> +             current_column += 2;
> +
> +             return Token::make (OR, loc);
> +           }
> +         else
> +           {
> +             // bitwise or
> +             current_column++;
> +
> +             return Token::make (PIPE, loc);
> +           }
> +       case '&':
> +         if (peek_input () == '=')
> +           {
> +             // bitwise and-assign?
> +             skip_input ();
> +             current_column += 2;
> +
> +             return Token::make (AMP_EQ, loc);
> +           }
> +         else if (peek_input () == '&')
> +           {
> +             // logical and
> +             skip_input ();
> +             current_column += 2;
> +
> +             return Token::make (LOGICAL_AND, loc);
> +           }
> +         else
> +           {
> +             // bitwise and/reference
> +             current_column++;
> +
> +             return Token::make (AMP, loc);
> +           }
> +       case '.':
> +         if (peek_input () == '.')
> +           {
> +             if (peek_input (1) == '.')
> +               {
> +                 // ellipsis
> +                 skip_input (1);
> +                 current_column += 3;
> +
> +                 return Token::make (ELLIPSIS, loc);
> +               }
> +             else if (peek_input (1) == '=')
> +               {
> +                 // ..=
> +                 skip_input (1);
> +                 current_column += 3;
> +
> +                 return Token::make (DOT_DOT_EQ, loc);
> +               }
> +             else
> +               {
> +                 // ..
> +                 skip_input ();
> +                 current_column += 2;
> +
> +                 return Token::make (DOT_DOT, loc);
> +               }
> +           }
> +         else /*if (!ISDIGIT (peek_input ()))*/
> +           {
> +             // single dot .
> +             // Only if followed by a non-number - otherwise is float
> +             // nope, float cannot start with '.'.
> +             current_column++;
> +             return Token::make (DOT, loc);
> +           }
> +       }
> +      // TODO: special handling of _ in the lexer? instead of being identifier
> +
> +      // byte character, byte string and raw byte string literals
> +      if (current_char == 'b')
> +       {
> +         if (peek_input () == '\'')
> +           return parse_byte_char (loc);
> +         else if (peek_input () == '"')
> +           return parse_byte_string (loc);
> +         else if (peek_input () == 'r'
> +                  && (peek_input (1) == '#' || peek_input (1) == '"'))
> +           return parse_raw_byte_string (loc);
> +       }
> +
> +      // raw identifiers and raw strings
> +      if (current_char == 'r')
> +       {
> +         int peek = peek_input ();
> +         int peek1 = peek_input (1);
> +
> +         if (peek == '#' && (ISALPHA (peek1) || peek1 == '_'))
> +           {
> +             TokenPtr raw_ident_ptr = parse_raw_identifier (loc);
> +             if (raw_ident_ptr != nullptr)
> +               return raw_ident_ptr;
> +             else
> +               continue; /* input got parsed, it just wasn't valid. An error
> +                            was produced. */
> +           }
> +         else
> +           {
> +             TokenPtr maybe_raw_string_ptr = maybe_parse_raw_string (loc);
> +             if (maybe_raw_string_ptr != nullptr)
> +               return maybe_raw_string_ptr;
> +           }
> +       }
> +
> +      // find identifiers and keywords
> +      if (ISALPHA (current_char) || current_char == '_')
> +       return parse_identifier_or_keyword (loc);
> +
> +      // int and float literals
> +      if (ISDIGIT (current_char))
> +       { //  _ not allowed as first char
> +         if (current_char == '0'
> +             && is_non_decimal_int_literal_separator (peek_input ()))
> +           {
> +             // handle binary, octal, hex literals
> +             TokenPtr non_dec_int_lit_ptr
> +               = parse_non_decimal_int_literals (loc);
> +             if (non_dec_int_lit_ptr != nullptr)
> +               return non_dec_int_lit_ptr;
> +           }
> +         else
> +           {
> +             // handle decimals (integer or float)
> +             TokenPtr decimal_or_float_ptr = parse_decimal_int_or_float (loc);
> +             if (decimal_or_float_ptr != nullptr)
> +               return decimal_or_float_ptr;
> +           }
> +       }
> +
> +      // string literals
> +      if (current_char == '"')
> +       return parse_string (loc);
> +
> +      // char literals and lifetime names
> +      if (current_char == '\'')
> +       {
> +         TokenPtr char_or_lifetime_ptr = parse_char_or_lifetime (loc);
> +         if (char_or_lifetime_ptr != nullptr)
> +           return char_or_lifetime_ptr;
> +       }
> +
> +      // DEBUG: check for specific character problems:
> +      if (current_char == '0')
> +       rust_debug ("'0' uncaught before unexpected character");
> +      else if (current_char == ']')
> +       rust_debug ("']' uncaught before unexpected character");
> +      else if (current_char == 0x5d)
> +       rust_debug ("whatever 0x5d is (not '0' or ']') uncaught before "
> +                   "unexpected character");
> +
> +      // didn't match anything so error
> +      rust_error_at (loc, "unexpected character %<%x%>", current_char);
> +      current_column++;
> +    }
> +}
> +
> +// Parses in a type suffix.
> +std::pair<PrimitiveCoreType, int>
> +Lexer::parse_in_type_suffix ()
> +{
> +  std::string suffix;
> +  suffix.reserve (5);
> +
> +  int additional_length_offset = 0;
> +
> +  // get suffix
> +  while (ISALPHA (current_char) || ISDIGIT (current_char)
> +        || current_char == '_')
> +    {
> +      if (current_char == '_')
> +       {
> +         // don't add _ to suffix
> +         skip_input ();
> +         current_char = peek_input ();
> +
> +         additional_length_offset++;
> +
> +         continue;
> +       }
> +
> +      additional_length_offset++;
> +
> +      suffix += current_char;
> +      skip_input ();
> +      current_char = peek_input ();
> +    }
> +
> +  if (suffix.empty ())
> +    {
> +      // no type suffix: do nothing but also no error
> +      return std::make_pair (CORETYPE_UNKNOWN, additional_length_offset);
> +    }
> +  else if (suffix == "f32")
> +    {
> +      return std::make_pair (CORETYPE_F32, additional_length_offset);
> +    }
> +  else if (suffix == "f64")
> +    {
> +      return std::make_pair (CORETYPE_F64, additional_length_offset);
> +    }
> +  else if (suffix == "i8")
> +    {
> +      return std::make_pair (CORETYPE_I8, additional_length_offset);
> +    }
> +  else if (suffix == "i16")
> +    {
> +      return std::make_pair (CORETYPE_I16, additional_length_offset);
> +    }
> +  else if (suffix == "i32")
> +    {
> +      return std::make_pair (CORETYPE_I32, additional_length_offset);
> +    }
> +  else if (suffix == "i64")
> +    {
> +      return std::make_pair (CORETYPE_I64, additional_length_offset);
> +    }
> +  else if (suffix == "i128")
> +    {
> +      return std::make_pair (CORETYPE_I128, additional_length_offset);
> +    }
> +  else if (suffix == "isize")
> +    {
> +      return std::make_pair (CORETYPE_ISIZE, additional_length_offset);
> +    }
> +  else if (suffix == "u8")
> +    {
> +      return std::make_pair (CORETYPE_U8, additional_length_offset);
> +    }
> +  else if (suffix == "u16")
> +    {
> +      return std::make_pair (CORETYPE_U16, additional_length_offset);
> +    }
> +  else if (suffix == "u32")
> +    {
> +      return std::make_pair (CORETYPE_U32, additional_length_offset);
> +    }
> +  else if (suffix == "u64")
> +    {
> +      return std::make_pair (CORETYPE_U64, additional_length_offset);
> +    }
> +  else if (suffix == "u128")
> +    {
> +      return std::make_pair (CORETYPE_U128, additional_length_offset);
> +    }
> +  else if (suffix == "usize")
> +    {
> +      return std::make_pair (CORETYPE_USIZE, additional_length_offset);
> +    }
> +  else
> +    {
> +      rust_error_at (get_current_location (), "unknown number suffix %qs",
> +                    suffix.c_str ());
> +
> +      return std::make_pair (CORETYPE_UNKNOWN, additional_length_offset);
> +    }
> +}
> +
> +// Parses in the exponent part (if any) of a float literal.
> +std::pair<std::string, int>
> +Lexer::parse_in_exponent_part ()
> +{
> +  int additional_length_offset = 0;
> +  std::string str;
> +  if (current_char == 'E' || current_char == 'e')
> +    {
> +      // add exponent to string as strtod works with it
> +      str += current_char;
> +      skip_input ();
> +      current_char = peek_input ();
> +
> +      additional_length_offset++;
> +
> +      // special - and + handling
> +      if (current_char == '-')
> +       {
> +         str += '-';
> +
> +         skip_input ();
> +         current_char = peek_input ();
> +
> +         additional_length_offset++;
> +       }
> +      else if (current_char == '+')
> +       {
> +         // don't add + but still skip input
> +         skip_input ();
> +         current_char = peek_input ();
> +
> +         additional_length_offset++;
> +       }
> +
> +      // parse another decimal number for exponent
> +      auto str_length = parse_in_decimal ();
> +      str += std::get<0> (str_length);
> +      additional_length_offset += std::get<1> (str_length);
> +    }
> +  return std::make_pair (str, additional_length_offset);
> +}
> +
> +// Parses a decimal integer.
> +std::tuple<std::string, int, bool>
> +Lexer::parse_in_decimal ()
> +{
> +  /* A pure decimal contains only digits.  */
> +  bool pure_decimal = true;
> +  int additional_length_offset = 0;
> +  std::string str;
> +  while (ISDIGIT (current_char) || current_char == '_')
> +    {
> +      if (current_char == '_')
> +       {
> +         pure_decimal = false;
> +         // don't add _ to number
> +         skip_input ();
> +         current_char = peek_input ();
> +
> +         additional_length_offset++;
> +
> +         continue;
> +       }
> +
> +      additional_length_offset++;
> +
> +      str += current_char;
> +      skip_input ();
> +      current_char = peek_input ();
> +    }
> +  return std::make_tuple (str, additional_length_offset, pure_decimal);
> +}
> +
> +/* Parses escapes (and string continues) in "byte" strings and characters. Does
> + * not support unicode. */
> +std::tuple<char, int, bool>
> +Lexer::parse_escape (char opening_char)
> +{
> +  int additional_length_offset = 0;
> +  char output_char = 0;
> +
> +  // skip to actual letter
> +  skip_input ();
> +  current_char = peek_input ();
> +  additional_length_offset++;
> +
> +  switch (current_char)
> +    {
> +      case 'x': {
> +       auto hex_escape_pair = parse_partial_hex_escape ();
> +       long hexLong = hex_escape_pair.first;
> +       additional_length_offset += hex_escape_pair.second;
> +
> +       if (hexLong > 255 || hexLong < 0)
> +         rust_error_at (
> +           get_current_location (),
> +           "byte \\x escape %<\\x%x%> out of range - allows up to %<\\xFF%>",
> +           static_cast<unsigned int> (hexLong));
> +       /* TODO: restore capital for escape output - gcc pretty-printer doesn't
> +        * support %X directly */
> +       char hexChar = static_cast<char> (hexLong);
> +
> +       output_char = hexChar;
> +      }
> +      break;
> +    case 'n':
> +      output_char = '\n';
> +      break;
> +    case 'r':
> +      output_char = '\r';
> +      break;
> +    case 't':
> +      output_char = '\t';
> +      break;
> +    case '\\':
> +      output_char = '\\';
> +      break;
> +    case '0':
> +      output_char = '\0';
> +      break;
> +    case '\'':
> +      output_char = '\'';
> +      break;
> +    case '"':
> +      output_char = '"';
> +      break;
> +    case 'u':
> +      rust_error_at (get_current_location (),
> +                    "cannot have a unicode escape \\u in a byte %s",
> +                    opening_char == '\'' ? "character" : "string");
> +      // Try to parse it anyway, just to skip it
> +      parse_partial_unicode_escape ();
> +      return std::make_tuple (output_char, additional_length_offset, false);
> +    case '\r':
> +    case '\n':
> +      // string continue
> +      return std::make_tuple (0, parse_partial_string_continue (), true);
> +    default:
> +      rust_error_at (get_current_location (),
> +                    "unknown escape sequence %<\\%c%>", current_char);
> +      // returns false if no parsing could be done
> +      // return false;
> +      return std::make_tuple (output_char, additional_length_offset, false);
> +      break;
> +    }
> +  // all non-special cases (string continue) should skip their used char
> +  skip_input ();
> +  current_char = peek_input ();
> +  additional_length_offset++;
> +
> +  // returns true if parsing was successful
> +  // return true;
> +  return std::make_tuple (output_char, additional_length_offset, false);
> +}
> +
> +/* Parses an escape (or string continue) in a string or character. Supports
> + * unicode escapes. */
> +std::tuple<Codepoint, int, bool>
> +Lexer::parse_utf8_escape (char opening_char)
> +{
> +  Codepoint output_char;
> +  int additional_length_offset = 0;
> +
> +  // skip to actual letter
> +  skip_input ();
> +  current_char = peek_input ();
> +  additional_length_offset++;
> +
> +  switch (current_char)
> +    {
> +      case 'x': {
> +       auto hex_escape_pair = parse_partial_hex_escape ();
> +       long hexLong = hex_escape_pair.first;
> +       additional_length_offset += hex_escape_pair.second;
> +
> +       if (hexLong > 127 || hexLong < 0)
> +         rust_error_at (
> +           get_current_location (),
> +           "ascii \\x escape %<\\x%x%> out of range - allows up to %<\\x7F%>",
> +           static_cast<unsigned int> (hexLong));
> +       /* TODO: restore capital for escape output - gcc pretty-printer doesn't
> +        * support %X directly */
> +       char hexChar = static_cast<char> (hexLong);
> +
> +       output_char = hexChar;
> +      }
> +      break;
> +    case 'n':
> +      output_char = '\n';
> +      break;
> +    case 'r':
> +      output_char = '\r';
> +      break;
> +    case 't':
> +      output_char = '\t';
> +      break;
> +    case '\\':
> +      output_char = '\\';
> +      break;
> +    case '0':
> +      output_char = '\0';
> +      break;
> +    case '\'':
> +      output_char = '\'';
> +      break;
> +    case '"':
> +      output_char = '"';
> +      break;
> +      case 'u': {
> +       auto unicode_escape_pair = parse_partial_unicode_escape ();
> +       output_char = unicode_escape_pair.first;
> +       additional_length_offset += unicode_escape_pair.second;
> +
> +       return std::make_tuple (output_char, additional_length_offset, false);
> +      }
> +      break;
> +    case '\r':
> +    case '\n':
> +      // string continue
> +      return std::make_tuple (0, parse_partial_string_continue (), true);
> +    default:
> +      rust_error_at (get_current_location (),
> +                    "unknown escape sequence %<\\%c%>", current_char);
> +      // returns false if no parsing could be done
> +      // return false;
> +      return std::make_tuple (output_char, additional_length_offset, false);
> +      break;
> +    }
> +  /* all non-special cases (unicode, string continue) should skip their used
> +   * char */
> +  skip_input ();
> +  current_char = peek_input ();
> +  additional_length_offset++;
> +
> +  // returns true if parsing was successful
> +  // return true;
> +  return std::make_tuple (output_char, additional_length_offset, false);
> +}
> +
> +// Parses the body of a string continue that has been found in an escape.
> +int
> +Lexer::parse_partial_string_continue ()
> +{
> +  int additional_length_offset = 1;
> +
> +  // string continue
> +  while (is_whitespace (current_char))
> +    {
> +      if (current_char == '\n')
> +       {
> +         current_line++;
> +         current_column = 1;
> +         // tell line_table that new line starts
> +         start_line (current_line, max_column_hint);
> +
> +         // reset "length"
> +         additional_length_offset = 1;
> +
> +         // get next char
> +         skip_input ();
> +         current_char = peek_input ();
> +
> +         continue;
> +       }
> +
> +      skip_input ();
> +      current_char = peek_input ();
> +      additional_length_offset++;
> +    }
> +
> +  return additional_length_offset;
> +}
> +
> +/* Parses the body of a '\x' escape. Note that it does not check that the number
> + * is valid and smaller than 255. */
> +std::pair<long, int>
> +Lexer::parse_partial_hex_escape ()
> +{
> +  // hex char string (null-terminated)
> +  char hexNum[3] = {0, 0, 0};
> +
> +  // first hex char
> +  current_char = peek_input (1);
> +  int additional_length_offset = 1;
> +
> +  if (!is_x_digit (current_char))
> +    {
> +      rust_error_at (get_current_location (),
> +                    "invalid character %<\\x%c%> in \\x sequence",
> +                    current_char);
> +      return std::make_pair (0, 0);
> +    }
> +  hexNum[0] = current_char;
> +
> +  // second hex char
> +  skip_input ();
> +  current_char = peek_input (1);
> +  additional_length_offset++;
> +
> +  if (!is_x_digit (current_char))
> +    {
> +      rust_error_at (get_current_location (),
> +                    "invalid character %<\\x%c%c%> in \\x sequence", hexNum[0],
> +                    current_char);
> +      return std::make_pair (0, 1);
> +    }
> +  skip_input ();
> +  hexNum[1] = current_char;
> +
> +  long hexLong = std::strtol (hexNum, nullptr, 16);
> +
> +  return std::make_pair (hexLong, additional_length_offset);
> +}
> +
> +// Parses the body of a unicode escape.
> +std::pair<Codepoint, int>
> +Lexer::parse_partial_unicode_escape ()
> +{
> +  skip_input ();
> +  current_char = peek_input ();
> +  int additional_length_offset = 0;
> +
> +  if (current_char != '{')
> +    {
> +      rust_error_at (get_current_location (),
> +                    "unicode escape should start with %<{%>");
> +      /* Skip what should probaby have been between brackets.  */
> +      while (is_x_digit (current_char) || current_char == '_')
> +       {
> +         skip_input ();
> +         current_char = peek_input ();
> +         additional_length_offset++;
> +       }
> +      return std::make_pair (Codepoint (0), additional_length_offset);
> +    }
> +
> +  skip_input ();
> +  current_char = peek_input ();
> +  additional_length_offset++;
> +
> +  if (current_char == '_')
> +    {
> +      rust_error_at (get_current_location (),
> +                    "unicode escape cannot start with %<_%>");
> +      skip_input ();
> +      current_char = peek_input ();
> +      additional_length_offset++;
> +      // fallthrough and try to parse the rest anyway
> +    }
> +
> +  // parse unicode escape - 1-6 hex digits
> +  std::string num_str;
> +  num_str.reserve (6);
> +
> +  // loop through to add entire hex number to string
> +  while (is_x_digit (current_char) || current_char == '_')
> +    {
> +      if (current_char == '_')
> +       {
> +         // don't add _ to number
> +         skip_input ();
> +         current_char = peek_input ();
> +
> +         additional_length_offset++;
> +
> +         continue;
> +       }
> +
> +      additional_length_offset++;
> +
> +      // add raw hex numbers
> +      num_str += current_char;
> +
> +      skip_input ();
> +      current_char = peek_input ();
> +    }
> +
> +  if (current_char == '}')
> +    {
> +      skip_input ();
> +      current_char = peek_input ();
> +      additional_length_offset++;
> +    }
> +  else
> +    {
> +      // actually an error, but allow propagation anyway Assume that
> +      // wrong bracketm whitespace or single/double quotes are wrong
> +      // termination, otherwise it is a wrong character, then skip to the actual
> +      // terminator.
> +      if (current_char == '{' || is_whitespace (current_char)
> +         || current_char == '\'' || current_char == '"')
> +       {
> +         rust_error_at (get_current_location (),
> +                        "expected terminating %<}%> in unicode escape");
> +         return std::make_pair (Codepoint (0), additional_length_offset);
> +       }
> +      else
> +       {
> +         rust_error_at (get_current_location (),
> +                        "invalid character %<%c%> in unicode escape",
> +                        current_char);
> +         while (current_char != '}' && current_char != '{'
> +                && !is_whitespace (current_char) && current_char != '\''
> +                && current_char != '"')
> +           {
> +             skip_input ();
> +             current_char = peek_input ();
> +             additional_length_offset++;
> +           }
> +         // Consume the actual closing bracket if found
> +         if (current_char == '}')
> +           {
> +             skip_input ();
> +             current_char = peek_input ();
> +             additional_length_offset++;
> +           }
> +         return std::make_pair (Codepoint (0), additional_length_offset);
> +       }
> +    }
> +
> +  // ensure 1-6 hex characters
> +  if (num_str.length () > 6 || num_str.length () < 1)
> +    {
> +      rust_error_at (get_current_location (),
> +                    "unicode escape should be between 1 and 6 hex "
> +                    "characters; it is %lu",
> +                    (unsigned long) num_str.length ());
> +      // return false;
> +      return std::make_pair (Codepoint (0), additional_length_offset);
> +    }
> +
> +  unsigned long hex_num = std::strtoul (num_str.c_str (), nullptr, 16);
> +
> +  if (hex_num > 0xd7ff && hex_num < 0xe000)
> +    {
> +      rust_error_at (
> +       get_current_location (),
> +       "unicode escape cannot be a surrogate value (D800 to DFFF)");
> +      return std::make_pair (Codepoint (0), additional_length_offset);
> +    }
> +
> +  if (hex_num > 0x10ffff)
> +    {
> +      rust_error_at (get_current_location (),
> +                    "unicode escape cannot be larger than 10FFFF");
> +      return std::make_pair (Codepoint (0), additional_length_offset);
> +    }
> +
> +  // return true;
> +  return std::make_pair (Codepoint (static_cast<uint32_t> (hex_num)),
> +                        additional_length_offset);
> +}
> +
> +// Parses a byte character.
> +TokenPtr
> +Lexer::parse_byte_char (Location loc)
> +{
> +  skip_input ();
> +  current_column++;
> +  // make current char the next character
> +  current_char = peek_input ();
> +
> +  int length = 1;
> +
> +  // char to save
> +  char byte_char = 0;
> +
> +  // detect escapes
> +  if (current_char == '\\')
> +    {
> +      auto escape_length_pair = parse_escape ('\'');
> +      byte_char = std::get<0> (escape_length_pair);
> +      length += std::get<1> (escape_length_pair);
> +
> +      current_char = peek_input ();
> +
> +      if (current_char != '\'')
> +       {
> +         rust_error_at (get_current_location (), "unclosed %<byte char%>");
> +       }
> +
> +      skip_input ();
> +      current_char = peek_input ();
> +      length++; // go to next char
> +    }
> +  else if (current_char != '\'')
> +    {
> +      // otherwise, get character from direct input character
> +      byte_char = current_char;
> +
> +      skip_input ();
> +      current_char = peek_input ();
> +      length++;
> +
> +      if (current_char != '\'')
> +       {
> +         rust_error_at (get_current_location (), "unclosed %<byte char%>");
> +       }
> +
> +      skip_input ();
> +      current_char = peek_input ();
> +      length++; // go to next char
> +    }
> +  else
> +    {
> +      rust_error_at (get_current_location (),
> +                    "no character inside %<%> for %<byte char%>");
> +    }
> +
> +  current_column += length;
> +
> +  return Token::make_byte_char (loc, byte_char);
> +}
> +
> +// Parses a byte string.
> +TokenPtr
> +Lexer::parse_byte_string (Location loc)
> +{
> +  // byte string
> +
> +  // skip quote character
> +  skip_input ();
> +  current_column++;
> +
> +  std::string str;
> +  str.reserve (16); // some sensible default
> +
> +  int length = 1;
> +  current_char = peek_input ();
> +
> +  while (current_char != '"' && current_char != EOF)
> +    {
> +      if (current_char == '\\')
> +       {
> +         auto escape_length_pair = parse_escape ('"');
> +         char output_char = std::get<0> (escape_length_pair);
> +
> +         if (output_char == 0 && std::get<2> (escape_length_pair))
> +           length = std::get<1> (escape_length_pair) - 1;
> +         else
> +           length += std::get<1> (escape_length_pair);
> +
> +         if (output_char != 0 || !std::get<2> (escape_length_pair))
> +           str += output_char;
> +
> +         continue;
> +       }
> +
> +      length++;
> +
> +      str += current_char;
> +      skip_input ();
> +      current_char = peek_input ();
> +    }
> +
> +  current_column += length;
> +
> +  if (current_char == '"')
> +    {
> +      current_column++;
> +
> +      skip_input ();
> +      current_char = peek_input ();
> +    }
> +  else if (current_char == EOF)
> +    {
> +      rust_error_at (get_current_location (), "unended byte string literal");
> +      return Token::make (END_OF_FILE, get_current_location ());
> +    }
> +  else
> +    {
> +      gcc_unreachable ();
> +    }
> +
> +  str.shrink_to_fit ();
> +
> +  return Token::make_byte_string (loc, std::move (str));
> +}
> +
> +// Parses a raw byte string.
> +TokenPtr
> +Lexer::parse_raw_byte_string (Location loc)
> +{
> +  // raw byte string literals
> +  std::string str;
> +  str.reserve (16); // some sensible default
> +
> +  int length = 1;
> +  int hash_count = 0;
> +
> +  // get hash count at beginnning
> +  skip_input ();
> +  current_char = peek_input ();
> +  length++;
> +  while (current_char == '#')
> +    {
> +      hash_count++;
> +      length++;
> +
> +      skip_input ();
> +      current_char = peek_input ();
> +    }
> +
> +  if (current_char != '"')
> +    {
> +      rust_error_at (get_current_location (),
> +                    "raw byte string has no opening %<\"%>");
> +    }
> +
> +  skip_input ();
> +  current_char = peek_input ();
> +  length++;
> +
> +  while (true)
> +    {
> +      if (current_char == '"')
> +       {
> +         bool enough_hashes = true;
> +
> +         for (int i = 0; i < hash_count; i++)
> +           {
> +             if (peek_input (i + 1) != '#')
> +               {
> +                 enough_hashes = false;
> +                 break;
> +               }
> +           }
> +
> +         if (enough_hashes)
> +           {
> +             // skip enough input and peek enough input
> +             skip_input (hash_count);
> +             current_char = peek_input ();
> +             length += hash_count + 1;
> +             break;
> +           }
> +       }
> +
> +      if ((unsigned char) current_char > 127)
> +       {
> +         rust_error_at (get_current_location (),
> +                        "character %<%c%> in raw byte string out of range",
> +                        current_char);
> +         current_char = 0;
> +       }
> +
> +      length++;
> +
> +      str += current_char;
> +      skip_input ();
> +      current_char = peek_input ();
> +    }
> +
> +  current_column += length;
> +
> +  str.shrink_to_fit ();
> +
> +  return Token::make_byte_string (loc, std::move (str));
> +}
> +
> +// Parses a raw identifier.
> +TokenPtr
> +Lexer::parse_raw_identifier (Location loc)
> +{
> +  // raw identifier
> +  std::string str;
> +  str.reserve (16); // default
> +
> +  skip_input ();
> +  current_char = peek_input ();
> +
> +  current_column += 2;
> +
> +  bool first_is_underscore = current_char == '_';
> +
> +  int length = 0;
> +  current_char = peek_input ();
> +  // loop through entire name
> +  while (ISALPHA (current_char) || ISDIGIT (current_char)
> +        || current_char == '_')
> +    {
> +      length++;
> +
> +      str += current_char;
> +      skip_input ();
> +      current_char = peek_input ();
> +    }
> +
> +  current_column += length;
> +
> +  // if just a single underscore, not an identifier
> +  if (first_is_underscore && length == 1)
> +    rust_error_at (get_current_location (),
> +                  "%<_%> is not a valid raw identifier");
> +
> +  if (str == "crate" || str == "extern" || str == "self" || str == "super"
> +      || str == "Self")
> +    {
> +      rust_error_at (get_current_location (),
> +                    "%qs is a forbidden raw identifier", str.c_str ());
> +
> +      return nullptr;
> +    }
> +  else
> +    {
> +      str.shrink_to_fit ();
> +
> +      return Token::make_identifier (loc, std::move (str));
> +    }
> +}
> +
> +// skip broken string input (unterminated strings)
> +void
> +Lexer::skip_broken_string_input (int current_char)
> +{
> +  while (current_char != '"' && current_char != EOF)
> +    {
> +      if (current_char == '\n')
> +       {
> +         current_line++;
> +         current_column = 1;
> +       }
> +      else
> +       {
> +         current_column++;
> +       }
> +      skip_input ();
> +      current_char = peek_input ();
> +    }
> +  if (current_char == '"')
> +    {
> +      current_column++;
> +
> +      skip_input ();
> +      current_char = peek_input ();
> +    }
> +  rust_debug ("skipped to %d:%d due to bad quotes", current_line,
> +             current_column);
> +}
> +
> +// Parses a unicode string.
> +TokenPtr
> +Lexer::parse_string (Location loc)
> +{
> +  Codepoint current_char32;
> +
> +  std::string str;
> +  str.reserve (16); // some sensible default
> +
> +  int length = 1;
> +  current_char32 = peek_codepoint_input ();
> +
> +  // FIXME: This fails if the input ends. How do we check for EOF?
> +  while (current_char32.value != '"' && !current_char32.is_eof ())
> +    {
> +      if (current_char32.value == '\\')
> +       {
> +         // parse escape
> +         auto utf8_escape_pair = parse_utf8_escape ('\'');
> +         current_char32 = std::get<0> (utf8_escape_pair);
> +
> +         if (current_char32 == Codepoint (0) && std::get<2> (utf8_escape_pair))
> +           length = std::get<1> (utf8_escape_pair) - 1;
> +         else
> +           length += std::get<1> (utf8_escape_pair);
> +
> +         if (current_char32 != Codepoint (0)
> +             || !std::get<2> (utf8_escape_pair))
> +           str += current_char32;
> +
> +         // required as parsing utf8 escape only changes current_char
> +         current_char32 = peek_codepoint_input ();
> +
> +         continue;
> +       }
> +
> +      length += get_input_codepoint_length ();
> +
> +      str += current_char32;
> +      skip_codepoint_input ();
> +      current_char32 = peek_codepoint_input ();
> +    }
> +
> +  current_column += length;
> +
> +  if (current_char32.value == '"')
> +    {
> +      current_column++;
> +
> +      skip_input ();
> +      current_char = peek_input ();
> +    }
> +  else if (current_char32.is_eof ())
> +    {
> +      rust_error_at (get_current_location (), "unended string literal");
> +      return Token::make (END_OF_FILE, get_current_location ());
> +    }
> +  else
> +    {
> +      gcc_unreachable ();
> +    }
> +
> +  str.shrink_to_fit ();
> +  return Token::make_string (loc, std::move (str));
> +}
> +
> +// Parses an identifier or keyword.
> +TokenPtr
> +Lexer::parse_identifier_or_keyword (Location loc)
> +{
> +  std::string str;
> +  str.reserve (16); // default
> +  str += current_char;
> +
> +  bool first_is_underscore = current_char == '_';
> +
> +  int length = 1;
> +  current_char = peek_input ();
> +  // loop through entire name
> +  while (ISALPHA (current_char) || ISDIGIT (current_char)
> +        || current_char == '_')
> +    {
> +      length++;
> +
> +      str += current_char;
> +      skip_input ();
> +      current_char = peek_input ();
> +    }
> +
> +  current_column += length;
> +
> +  // if just a single underscore, not an identifier
> +  if (first_is_underscore && length == 1)
> +    return Token::make (UNDERSCORE, loc);
> +
> +  str.shrink_to_fit ();
> +
> +  TokenId keyword = classify_keyword (str);
> +  if (keyword == IDENTIFIER)
> +    return Token::make_identifier (loc, std::move (str));
> +  else
> +    return Token::make (keyword, loc);
> +}
> +
> +// Possibly returns a raw string token if it exists - otherwise returns null.
> +TokenPtr
> +Lexer::maybe_parse_raw_string (Location loc)
> +{
> +  int peek_index = 0;
> +  while (peek_input (peek_index) == '#')
> +    peek_index++;
> +
> +  if (peek_input (peek_index) == '"')
> +    return parse_raw_string (loc, peek_index);
> +  else
> +    return nullptr;
> +}
> +
> +// Returns a raw string token.
> +TokenPtr
> +Lexer::parse_raw_string (Location loc, int initial_hash_count)
> +{
> +  // raw string literals
> +  std::string str;
> +  str.reserve (16); // some sensible default
> +
> +  int length = 1 + initial_hash_count;
> +
> +  if (initial_hash_count > 0)
> +    skip_input (initial_hash_count - 1);
> +
> +  current_char = peek_input ();
> +
> +  if (current_char != '"')
> +    rust_error_at (get_current_location (), "raw string has no opening %<\"%>");
> +
> +  length++;
> +  skip_input ();
> +  Codepoint current_char32 = peek_codepoint_input ();
> +
> +  while (!current_char32.is_eof ())
> +    {
> +      if (current_char32.value == '"')
> +       {
> +         bool enough_hashes = true;
> +
> +         for (int i = 0; i < initial_hash_count; i++)
> +           {
> +             if (peek_input (i + 1) != '#')
> +               {
> +                 enough_hashes = false;
> +                 break;
> +               }
> +           }
> +
> +         if (enough_hashes)
> +           {
> +             // skip enough input and peek enough input
> +             skip_input (initial_hash_count);
> +             current_char = peek_input ();
> +             length += initial_hash_count + 1;
> +             break;
> +           }
> +       }
> +
> +      length++;
> +
> +      str += current_char32;
> +      skip_codepoint_input ();
> +      current_char32 = peek_codepoint_input ();
> +    }
> +
> +  current_column += length;
> +
> +  str.shrink_to_fit ();
> +
> +  return Token::make_string (loc, std::move (str));
> +}
> +
> +template <typename IsDigitFunc>
> +TokenPtr
> +Lexer::parse_non_decimal_int_literal (Location loc, IsDigitFunc is_digit_func,
> +                                     std::string existent_str, int base)
> +{
> +  int length = 1;
> +
> +  skip_input ();
> +  current_char = peek_input ();
> +
> +  length++;
> +
> +  // loop through to add entire number to string
> +  while (is_digit_func (current_char) || current_char == '_')
> +    {
> +      if (current_char == '_')
> +       {
> +         // don't add _ to number
> +         skip_input ();
> +         current_char = peek_input ();
> +
> +         length++;
> +
> +         continue;
> +       }
> +
> +      length++;
> +
> +      // add raw numbers
> +      existent_str += current_char;
> +      skip_input ();
> +      current_char = peek_input ();
> +    }
> +
> +  // convert value to decimal representation
> +  long dec_num = std::strtol (existent_str.c_str (), nullptr, base);
> +
> +  existent_str = std::to_string (dec_num);
> +
> +  // parse in type suffix if it exists
> +  auto type_suffix_pair = parse_in_type_suffix ();
> +  PrimitiveCoreType type_hint = type_suffix_pair.first;
> +  length += type_suffix_pair.second;
> +
> +  current_column += length;
> +
> +  if (type_hint == CORETYPE_F32 || type_hint == CORETYPE_F64)
> +    {
> +      rust_error_at (get_current_location (),
> +                    "invalid type suffix %qs for integer (%s) literal",
> +                    get_type_hint_string (type_hint),
> +                    base == 16
> +                      ? "hex"
> +                      : (base == 8 ? "octal"
> +                                   : (base == 2 ? "binary"
> +                                                : "<insert unknown base>")));
> +      return nullptr;
> +    }
> +  return Token::make_int (loc, std::move (existent_str), type_hint);
> +}
> +
> +// Parses a hex, binary or octal int literal.
> +TokenPtr
> +Lexer::parse_non_decimal_int_literals (Location loc)
> +{
> +  std::string str;
> +  str.reserve (16); // some sensible default
> +  str += current_char;
> +
> +  current_char = peek_input ();
> +
> +  if (current_char == 'x')
> +    {
> +      // hex (integer only)
> +      return parse_non_decimal_int_literal (loc, is_x_digit, str + "x", 16);
> +    }
> +  else if (current_char == 'o')
> +    {
> +      // octal (integer only)
> +      return parse_non_decimal_int_literal (loc, is_octal_digit,
> +                                           std::move (str), 8);
> +    }
> +  else if (current_char == 'b')
> +    {
> +      // binary (integer only)
> +      return parse_non_decimal_int_literal (loc, is_bin_digit, std::move (str),
> +                                           2);
> +    }
> +  else
> +    {
> +      return nullptr;
> +    }
> +}
> +
> +// Parses a decimal-based int literal or float literal.
> +TokenPtr
> +Lexer::parse_decimal_int_or_float (Location loc)
> +{
> +  std::string str;
> +  str.reserve (16); // some sensible default
> +  str += current_char;
> +
> +  int length = 1;
> +  bool first_zero = current_char == '0';
> +
> +  current_char = peek_input ();
> +
> +  // parse initial decimal integer (or first integer part of float) literal
> +  auto initial_decimal = parse_in_decimal ();
> +  str += std::get<0> (initial_decimal);
> +  length += std::get<1> (initial_decimal);
> +
> +  // detect float literal
> +  if (current_char == '.' && is_float_digit (peek_input (1)))
> +    {
> +      // float with a '.', parse another decimal into it
> +
> +      // add . to str
> +      str += current_char;
> +      skip_input ();
> +      current_char = peek_input ();
> +      length++;
> +
> +      // parse another decimal number for float
> +      auto second_decimal = parse_in_decimal ();
> +      str += std::get<0> (second_decimal);
> +      length += std::get<1> (second_decimal);
> +
> +      // parse in exponent part if it exists
> +      auto exponent_pair = parse_in_exponent_part ();
> +      str += exponent_pair.first;
> +      length += exponent_pair.second;
> +
> +      // parse in type suffix if it exists
> +      auto type_suffix_pair = parse_in_type_suffix ();
> +      PrimitiveCoreType type_hint = type_suffix_pair.first;
> +      length += type_suffix_pair.second;
> +
> +      if (type_hint != CORETYPE_F32 && type_hint != CORETYPE_F64
> +         && type_hint != CORETYPE_UNKNOWN)
> +       {
> +         rust_error_at (get_current_location (),
> +                        "invalid type suffix %qs for floating-point literal",
> +                        get_type_hint_string (type_hint));
> +         // ignore invalid type suffix as everything else seems fine
> +         type_hint = CORETYPE_UNKNOWN;
> +       }
> +
> +      current_column += length;
> +
> +      str.shrink_to_fit ();
> +      return Token::make_float (loc, std::move (str), type_hint);
> +    }
> +  else if (current_char == '.' && check_valid_float_dot_end (peek_input (1)))
> +    {
> +      // float that is just an integer with a terminating '.' character
> +
> +      // add . to str
> +      str += current_char;
> +      skip_input ();
> +      current_char = peek_input ();
> +      length++;
> +
> +      // add a '0' after the . to prevent ambiguity
> +      str += '0';
> +
> +      // type hint not allowed
> +
> +      current_column += length;
> +
> +      str.shrink_to_fit ();
> +      return Token::make_float (loc, std::move (str), CORETYPE_UNKNOWN);
> +    }
> +  else if (current_char == 'E' || current_char == 'e')
> +    {
> +      // exponent float with no '.' character
> +
> +      // parse exponent part
> +      auto exponent_pair = parse_in_exponent_part ();
> +      str += exponent_pair.first;
> +      length += exponent_pair.second;
> +
> +      // parse in type suffix if it exists
> +      auto type_suffix_pair = parse_in_type_suffix ();
> +      PrimitiveCoreType type_hint = type_suffix_pair.first;
> +      length += type_suffix_pair.second;
> +
> +      if (type_hint != CORETYPE_F32 && type_hint != CORETYPE_F64
> +         && type_hint != CORETYPE_UNKNOWN)
> +       {
> +         rust_error_at (get_current_location (),
> +                        "invalid type suffix %qs for floating-point literal",
> +                        get_type_hint_string (type_hint));
> +         // ignore invalid type suffix as everything else seems fine
> +         type_hint = CORETYPE_UNKNOWN;
> +       }
> +
> +      current_column += length;
> +
> +      str.shrink_to_fit ();
> +      return Token::make_float (loc, std::move (str), type_hint);
> +    }
> +  else
> +    {
> +      // is an integer
> +
> +      // parse in type suffix if it exists
> +      auto type_suffix_pair = parse_in_type_suffix ();
> +      PrimitiveCoreType type_hint = type_suffix_pair.first;
> +      /* A "real" pure decimal doesn't have a suffix and no zero prefix.  */
> +      if (type_hint == CORETYPE_UNKNOWN)
> +       {
> +         bool pure_decimal = std::get<2> (initial_decimal);
> +         if (pure_decimal && (!first_zero || str.size () == 1))
> +           type_hint = CORETYPE_PURE_DECIMAL;
> +       }
> +      length += type_suffix_pair.second;
> +
> +      current_column += length;
> +
> +      str.shrink_to_fit ();
> +      return Token::make_int (loc, std::move (str), type_hint);
> +    }
> +}
> +
> +TokenPtr
> +Lexer::parse_char_or_lifetime (Location loc)
> +{
> +  Codepoint current_char32;
> +
> +  int length = 1;
> +
> +  current_char32 = peek_codepoint_input ();
> +  if (current_char32.is_eof ())
> +    return nullptr;
> +
> +  // parse escaped char literal
> +  if (current_char32.value == '\\')
> +    {
> +      // parse escape
> +      auto utf8_escape_pair = parse_utf8_escape ('\'');
> +      current_char32 = std::get<0> (utf8_escape_pair);
> +      length += std::get<1> (utf8_escape_pair);
> +
> +      if (peek_codepoint_input ().value != '\'')
> +       {
> +         rust_error_at (get_current_location (), "unended character literal");
> +       }
> +      else
> +       {
> +         skip_codepoint_input ();
> +         current_char = peek_input ();
> +         length++;
> +       }
> +
> +      current_column += length;
> +
> +      return Token::make_char (loc, current_char32);
> +    }
> +  else
> +    {
> +      skip_codepoint_input ();
> +
> +      if (peek_codepoint_input ().value == '\'')
> +       {
> +         // parse non-escaped char literal
> +
> +         // skip the ' character
> +         skip_input ();
> +         current_char = peek_input ();
> +
> +         // TODO fix due to different widths of utf-8 chars?
> +         current_column += 3;
> +
> +         return Token::make_char (loc, current_char32);
> +       }
> +      else if (ISDIGIT (current_char32.value) || ISALPHA (current_char32.value)
> +              || current_char32.value == '_')
> +       {
> +         // parse lifetime name
> +         std::string str;
> +         str += current_char32;
> +         length++;
> +
> +         current_char = peek_input ();
> +         while (ISDIGIT (current_char) || ISALPHA (current_char)
> +                || current_char == '_')
> +           {
> +             str += current_char;
> +             skip_input ();
> +             current_char = peek_input ();
> +             length++;
> +           }
> +
> +         current_column += length;
> +
> +         str.shrink_to_fit ();
> +         return Token::make_lifetime (loc, std::move (str));
> +       }
> +      else
> +       {
> +         rust_error_at (
> +           get_current_location (),
> +           "expected %' after character constant in character literal");
> +         return nullptr;
> +       }
> +    }
> +}
> +
> +// Returns the length of the codepoint at the current position.
> +int
> +Lexer::get_input_codepoint_length ()
> +{
> +  uint8_t input = peek_input ();
> +
> +  if ((int8_t) input == EOF)
> +    return 0;
> +
> +  if (input < 128)
> +    {
> +      // ascii -- 1 byte
> +      // return input;
> +
> +      return 1;
> +    }
> +  else if ((input & 0xC0) == 0x80)
> +    {
> +      // invalid (continuation; can't be first char)
> +      // return 0xFFFE;
> +
> +      return 0;
> +    }
> +  else if ((input & 0xE0) == 0xC0)
> +    {
> +      // 2 bytes
> +      uint8_t input2 = peek_input (1);
> +      if ((input2 & 0xC0) != 0x80)
> +       return 0;
> +      // return 0xFFFE;
> +
> +      // uint32_t output = ((input & 0x1F) << 6) | ((input2 & 0x3F) << 0);
> +      // return output;
> +      return 2;
> +    }
> +  else if ((input & 0xF0) == 0xE0)
> +    {
> +      // 3 bytes
> +      uint8_t input2 = peek_input (1);
> +      if ((input2 & 0xC0) != 0x80)
> +       return 0;
> +      // return 0xFFFE;
> +
> +      uint8_t input3 = peek_input (2);
> +      if ((input3 & 0xC0) != 0x80)
> +       return 0;
> +      // return 0xFFFE;
> +
> +      /*uint32_t output
> +       = ((input & 0x0F) << 12) | ((input2 & 0x3F) << 6) | ((input3 & 0x3F) <<
> +      0); return output;*/
> +      return 3;
> +    }
> +  else if ((input & 0xF8) == 0xF0)
> +    {
> +      // 4 bytes
> +      uint8_t input2 = peek_input (1);
> +      if ((input2 & 0xC0) != 0x80)
> +       return 0;
> +      // return 0xFFFE;
> +
> +      uint8_t input3 = peek_input (2);
> +      if ((input3 & 0xC0) != 0x80)
> +       return 0;
> +      // return 0xFFFE;
> +
> +      uint8_t input4 = peek_input (3);
> +      if ((input4 & 0xC0) != 0x80)
> +       return 0;
> +      // return 0xFFFE;
> +
> +      /*uint32_t output = ((input & 0x07) << 18) | ((input2 & 0x3F) << 12)
> +                       | ((input3 & 0x3F) << 6) | ((input4 & 0x3F) << 0);
> +      return output;*/
> +      return 4;
> +    }
> +  else
> +    {
> +      rust_error_at (get_current_location (),
> +                    "invalid UTF-8 [FIRST] (too long)");
> +      return 0;
> +    }
> +}
> +
> +// Returns the codepoint at the current position.
> +Codepoint
> +Lexer::peek_codepoint_input ()
> +{
> +  uint8_t input = peek_input ();
> +
> +  if ((int8_t) input == EOF)
> +    return Codepoint::eof ();
> +
> +  if (input < 128)
> +    {
> +      // ascii -- 1 byte
> +      return {input};
> +    }
> +  else if ((input & 0xC0) == 0x80)
> +    {
> +      // invalid (continuation; can't be first char)
> +      return {0xFFFE};
> +    }
> +  else if ((input & 0xE0) == 0xC0)
> +    {
> +      // 2 bytes
> +      uint8_t input2 = peek_input (1);
> +      if ((input2 & 0xC0) != 0x80)
> +       return {0xFFFE};
> +
> +      uint32_t output = ((input & 0x1F) << 6) | ((input2 & 0x3F) << 0);
> +      return {output};
> +    }
> +  else if ((input & 0xF0) == 0xE0)
> +    {
> +      // 3 bytes
> +      uint8_t input2 = peek_input (1);
> +      if ((input2 & 0xC0) != 0x80)
> +       return {0xFFFE};
> +
> +      uint8_t input3 = peek_input (2);
> +      if ((input3 & 0xC0) != 0x80)
> +       return {0xFFFE};
> +
> +      uint32_t output = ((input & 0x0F) << 12) | ((input2 & 0x3F) << 6)
> +                       | ((input3 & 0x3F) << 0);
> +      return {output};
> +    }
> +  else if ((input & 0xF8) == 0xF0)
> +    {
> +      // 4 bytes
> +      uint8_t input2 = peek_input (1);
> +      if ((input2 & 0xC0) != 0x80)
> +       return {0xFFFE};
> +
> +      uint8_t input3 = peek_input (2);
> +      if ((input3 & 0xC0) != 0x80)
> +       return {0xFFFE};
> +
> +      uint8_t input4 = peek_input (3);
> +      if ((input4 & 0xC0) != 0x80)
> +       return {0xFFFE};
> +
> +      uint32_t output = ((input & 0x07) << 18) | ((input2 & 0x3F) << 12)
> +                       | ((input3 & 0x3F) << 6) | ((input4 & 0x3F) << 0);
> +      return {output};
> +    }
> +  else
> +    {
> +      rust_error_at (get_current_location (),
> +                    "invalid UTF-8 [SECND] (too long)");
> +      return {0xFFFE};
> +    }
> +}
> +
> +void
> +Lexer::skip_codepoint_input ()
> +{
> +  int toSkip = get_input_codepoint_length ();
> +  gcc_assert (toSkip >= 1);
> +
> +  skip_input (toSkip - 1);
> +}
> +
> +int
> +Lexer::test_get_input_codepoint_n_length (int n_start_offset)
> +{
> +  uint8_t input = peek_input (n_start_offset);
> +
> +  if (input < 128)
> +    {
> +      // ascii -- 1 byte
> +      // return input;
> +      return 1;
> +    }
> +  else if ((input & 0xC0) == 0x80)
> +    {
> +      // invalid (continuation; can't be first char)
> +      // return 0xFFFE;
> +      return 0;
> +    }
> +  else if ((input & 0xE0) == 0xC0)
> +    {
> +      // 2 bytes
> +      uint8_t input2 = peek_input (n_start_offset + 1);
> +      if ((input2 & 0xC0) != 0x80)
> +       // return 0xFFFE;
> +       return 0;
> +
> +      // uint32_t output = ((input & 0x1F) << 6) | ((input2 & 0x3F) << 0);
> +      // return output;
> +      return 2;
> +    }
> +  else if ((input & 0xF0) == 0xE0)
> +    {
> +      // 3 bytes
> +      uint8_t input2 = peek_input (n_start_offset + 1);
> +      if ((input2 & 0xC0) != 0x80)
> +       // return 0xFFFE;
> +       return 0;
> +
> +      uint8_t input3 = peek_input (n_start_offset + 2);
> +      if ((input3 & 0xC0) != 0x80)
> +       // return 0xFFFE;
> +       return 0;
> +
> +      /*uint32_t output
> +       = ((input & 0x0F) << 12) | ((input2 & 0x3F) << 6) | ((input3 & 0x3F) <<
> +      0); return output;*/
> +      return 3;
> +    }
> +  else if ((input & 0xF8) == 0xF0)
> +    {
> +      // 4 bytes
> +      uint8_t input2 = peek_input (n_start_offset + 1);
> +      if ((input2 & 0xC0) != 0x80)
> +       // return 0xFFFE;
> +       return 0;
> +
> +      uint8_t input3 = peek_input (n_start_offset + 2);
> +      if ((input3 & 0xC0) != 0x80)
> +       // return 0xFFFE;
> +       return 0;
> +
> +      uint8_t input4 = peek_input (n_start_offset + 3);
> +      if ((input4 & 0xC0) != 0x80)
> +       // return 0xFFFE;
> +       return 0;
> +
> +      /*uint32_t output = ((input & 0x07) << 18) | ((input2 & 0x3F) << 12)
> +                       | ((input3 & 0x3F) << 6) | ((input4 & 0x3F) << 0);
> +      return output;*/
> +      return 4;
> +    }
> +  else
> +    {
> +      rust_error_at (get_current_location (),
> +                    "invalid UTF-8 [THIRD] (too long)");
> +      return 0;
> +    }
> +}
> +
> +// peeks the codepoint input at n codepoints ahead of current codepoint - try
> +// not to use
> +Codepoint
> +Lexer::test_peek_codepoint_input (int n)
> +{
> +  int totalOffset = 0;
> +
> +  // add up all offsets into total offset? does this do what I want?
> +  for (int i = 0; i < n; i++)
> +    {
> +      totalOffset += test_get_input_codepoint_n_length (totalOffset);
> +    }
> +  // issues: this would have (at least) O(n) lookup time, not O(1) like the
> +  // rest?
> +
> +  // TODO: implement if still needed
> +
> +  // error out of function as it is not implemented
> +  gcc_assert (1 == 0);
> +  return {0};
> +  /*
> +         uint8_t input = peek_input();
> +
> +         if (input < 128) {
> +             // ascii -- 1 byte
> +             return input;
> +         } else if ((input & 0xC0) == 0x80) {
> +             // invalid (continuation; can't be first char)
> +             return 0xFFFE;
> +         } else if ((input & 0xE0) == 0xC0) {
> +             // 2 bytes
> +             uint8_t input2 = peek_input(1);
> +             if ((input2 & 0xC0) != 0x80)
> +                 return 0xFFFE;
> +
> +             uint32_t output = ((input & 0x1F) << 6) | ((input2 & 0x3F) << 0);
> +             return output;
> +         } else if ((input & 0xF0) == 0xE0) {
> +             // 3 bytes
> +             uint8_t input2 = peek_input(1);
> +             if ((input2 & 0xC0) != 0x80)
> +                 return 0xFFFE;
> +
> +             uint8_t input3 = peek_input(2);
> +             if ((input3 & 0xC0) != 0x80)
> +                 return 0xFFFE;
> +
> +             uint32_t output
> +               = ((input & 0x0F) << 12) | ((input2 & 0x3F) << 6) | ((input3 &
> +     0x3F) << 0); return output; } else if ((input & 0xF8) == 0xF0) {
> +             // 4 bytes
> +             uint8_t input2 = peek_input(1);
> +             if ((input2 & 0xC0) != 0x80)
> +                 return 0xFFFE;
> +
> +             uint8_t input3 = peek_input(2);
> +             if ((input3 & 0xC0) != 0x80)
> +                 return 0xFFFE;
> +
> +             uint8_t input4 = peek_input(3);
> +             if ((input4 & 0xC0) != 0x80)
> +                 return 0xFFFE;
> +
> +             uint32_t output = ((input & 0x07) << 18) | ((input2 & 0x3F) << 12)
> +                               | ((input3 & 0x3F) << 6) | ((input4 & 0x3F) <<
> +     0); return output; } else { rust_error_at(get_current_location(), "invalid
> +     UTF-8 (too long)"); return 0xFFFE;
> +         }*/
> +}
> +
> +void
> +Lexer::split_current_token (TokenId new_left, TokenId new_right)
> +{
> +  /* TODO: assert that this TokenId is a "simple token" like punctuation and not
> +   * like "IDENTIFIER"? */
> +  Location current_loc = peek_token ()->get_locus ();
> +  TokenPtr new_left_tok = Token::make (new_left, current_loc);
> +  TokenPtr new_right_tok = Token::make (new_right, current_loc + 1);
> +
> +  token_queue.replace_current_value (std::move (new_left_tok));
> +  token_queue.insert (1, std::move (new_right_tok));
> +}
> +
> +void
> +Lexer::start_line (int current_line, int current_column)
> +{
> +  if (line_map)
> +    line_map->start_line (current_line, current_column);
> +}
> +
> +} // namespace Rust
> diff --git a/gcc/rust/lex/rust-lex.h b/gcc/rust/lex/rust-lex.h
> new file mode 100644
> index 00000000000..d5a6c53719f
> --- /dev/null
> +++ b/gcc/rust/lex/rust-lex.h
> @@ -0,0 +1,271 @@
> +// 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_LEX_H
> +#define RUST_LEX_H
> +
> +#include "rust-linemap.h"
> +#include "rust-buffered-queue.h"
> +#include "rust-token.h"
> +
> +namespace Rust {
> +// Simple wrapper for FILE* that simplifies destruction.
> +struct RAIIFile
> +{
> +private:
> +  FILE *file;
> +  const char *filename;
> +
> +  void close ()
> +  {
> +    if (file != nullptr && file != stdin)
> +      fclose (file);
> +  }
> +
> +public:
> +  RAIIFile (const char *filename) : filename (filename)
> +  {
> +    if (strcmp (filename, "-") == 0)
> +      file = stdin;
> +    else
> +      file = fopen (filename, "r");
> +  }
> +
> +  /**
> +   * Create a RAIIFile from an existing instance of FILE*
> +   */
> +  RAIIFile (FILE *raw, const char *filename = nullptr)
> +    : file (raw), filename (filename)
> +  {}
> +
> +  RAIIFile (const RAIIFile &other) = delete;
> +  RAIIFile &operator= (const RAIIFile &other) = delete;
> +
> +  // have to specify setting file to nullptr, otherwise unintended fclose occurs
> +  RAIIFile (RAIIFile &&other) : file (other.file), filename (other.filename)
> +  {
> +    other.file = nullptr;
> +  }
> +
> +  RAIIFile &operator= (RAIIFile &&other)
> +  {
> +    close ();
> +    file = other.file;
> +    filename = other.filename;
> +    other.file = nullptr;
> +
> +    return *this;
> +  }
> +
> +  static RAIIFile create_error () { return RAIIFile (nullptr, nullptr); }
> +
> +  ~RAIIFile () { close (); }
> +
> +  FILE *get_raw () { return file; }
> +  const char *get_filename () { return filename; }
> +
> +  bool ok () const { return file; }
> +};
> +
> +class Lexer
> +{
> +private:
> +  // Request new Location for current column in line_table
> +  Location get_current_location ();
> +
> +  // Skips the current input char.
> +  void skip_input ();
> +  // Advances current input char to n + 1 chars ahead of current position.
> +  void skip_input (int n);
> +
> +  // Returns char n chars ahead of current position.
> +  int peek_input ();
> +  // Peeks the current char.
> +  int peek_input (int n);
> +
> +  // Classifies keyword (i.e. gets id for keyword).
> +  TokenId classify_keyword (const std::string &str);
> +
> +  // Builds a token from the input queue.
> +  TokenPtr build_token ();
> +
> +  std::tuple<std::string, int, bool> parse_in_decimal ();
> +  std::pair<std::string, int> parse_in_exponent_part ();
> +  std::pair<PrimitiveCoreType, int> parse_in_type_suffix ();
> +  std::tuple<char, int, bool> parse_escape (char opening_char);
> +  std::tuple<Codepoint, int, bool> parse_utf8_escape (char opening_char);
> +  int parse_partial_string_continue ();
> +  std::pair<long, int> parse_partial_hex_escape ();
> +  std::pair<Codepoint, int> parse_partial_unicode_escape ();
> +
> +  int get_input_codepoint_length ();
> +  int test_get_input_codepoint_n_length (int n_start_offset);
> +  Codepoint peek_codepoint_input ();
> +  Codepoint test_peek_codepoint_input (int n);
> +  void skip_codepoint_input ();
> +  void skip_broken_string_input (int current_char);
> +
> +  TokenPtr parse_byte_char (Location loc);
> +  TokenPtr parse_byte_string (Location loc);
> +  TokenPtr parse_raw_byte_string (Location loc);
> +  TokenPtr parse_raw_identifier (Location loc);
> +  TokenPtr parse_string (Location loc);
> +  TokenPtr maybe_parse_raw_string (Location loc);
> +  TokenPtr parse_raw_string (Location loc, int initial_hash_count);
> +  TokenPtr parse_non_decimal_int_literals (Location loc);
> +  TokenPtr parse_decimal_int_or_float (Location loc);
> +  TokenPtr parse_char_or_lifetime (Location loc);
> +  TokenPtr parse_identifier_or_keyword (Location loc);
> +
> +  template <typename IsDigitFunc>
> +  TokenPtr parse_non_decimal_int_literal (Location loc,
> +                                         IsDigitFunc is_digit_func,
> +                                         std::string existent_str, int base);
> +
> +public:
> +  // Construct lexer with input file and filename provided
> +  Lexer (const char *filename, RAIIFile input, Linemap *linemap);
> +
> +  // Lex the contents of a string instead of a file
> +  Lexer (const std::string &input);
> +
> +  // dtor
> +  ~Lexer ();
> +
> +  // don't allow copy semantics (for now, at least)
> +  Lexer (const Lexer &other) = delete;
> +  Lexer &operator= (const Lexer &other) = delete;
> +
> +  // enable move semantics
> +  Lexer (Lexer &&other) = default;
> +  Lexer &operator= (Lexer &&other) = default;
> +
> +  // Returns token n tokens ahead of current position.
> +  const_TokenPtr peek_token (int n) { return token_queue.peek (n); }
> +  // Peeks the current token.
> +  const_TokenPtr peek_token () { return peek_token (0); }
> +
> +  // Advances current token to n + 1 tokens ahead of current position.
> +  void skip_token (int n) { token_queue.skip (n); }
> +  // Skips the current token.
> +  void skip_token () { skip_token (0); }
> +
> +  // Replaces the current token with a specified token.
> +  void replace_current_token (TokenPtr replacement);
> +  // FIXME: don't use anymore
> +
> +  /* Splits the current token into two. Intended for use with nested generics
> +   * closes (i.e. T<U<X>> where >> is wrongly lexed as one token). Note that
> +   * this will only work with "simple" tokens like punctuation. */
> +  void split_current_token (TokenId new_left, TokenId new_right);
> +
> +  Linemap *get_line_map () { return line_map; }
> +  std::string get_filename () { return std::string (input.get_filename ()); }
> +
> +private:
> +  void start_line (int current_line, int current_column);
> +
> +  // File for use as input.
> +  RAIIFile input;
> +  // TODO is this actually required? could just have file storage in InputSource
> +
> +  // Current line number.
> +  int current_line;
> +  // Current column number.
> +  int current_column;
> +  // Current character.
> +  int current_char;
> +  // Line map.
> +  Linemap *line_map;
> +
> +  /* Max column number that can be quickly allocated - higher may require
> +   * allocating new linemap */
> +  static const int max_column_hint = 80;
> +
> +  // Input source wrapper thing.
> +  class InputSource
> +  {
> +  public:
> +    virtual ~InputSource () {}
> +
> +    // Overload operator () to return next char from input stream.
> +    virtual int next () = 0;
> +  };
> +
> +  class FileInputSource : public InputSource
> +  {
> +  private:
> +    // Input source file.
> +    FILE *input;
> +
> +  public:
> +    // Create new input source from file.
> +    FileInputSource (FILE *input) : input (input) {}
> +
> +    int next () override { return fgetc (input); }
> +  };
> +
> +  class BufferInputSource : public InputSource
> +  {
> +  private:
> +    const std::string &buffer;
> +    size_t offs;
> +
> +  public:
> +    // Create new input source from file.
> +    BufferInputSource (const std::string &b, size_t offset)
> +      : buffer (b), offs (offset)
> +    {}
> +
> +    int next () override
> +    {
> +      if (offs >= buffer.size ())
> +       return EOF;
> +
> +      return buffer.at (offs++);
> +    }
> +  };
> +
> +  // The input source for the lexer.
> +  // InputSource input_source;
> +  // Input file queue.
> +  std::unique_ptr<InputSource> raw_input_source;
> +  buffered_queue<int, InputSource &> input_queue;
> +
> +  // Token source wrapper thing.
> +  struct TokenSource
> +  {
> +    // The lexer object that will use this TokenSource.
> +    Lexer *lexer;
> +
> +    // Create a new TokenSource with given lexer.
> +    TokenSource (Lexer *parLexer) : lexer (parLexer) {}
> +
> +    // Overload operator () to build token in lexer.
> +    TokenPtr next () { return lexer->build_token (); }
> +  };
> +
> +  // The token source for the lexer.
> +  // TokenSource token_source;
> +  // Token stream queue.
> +  buffered_queue<std::shared_ptr<Token>, TokenSource> token_queue;
> +};
> +
> +} // namespace Rust
> +
> +#endif
> diff --git a/gcc/rust/lex/rust-token.cc b/gcc/rust/lex/rust-token.cc
> new file mode 100644
> index 00000000000..68313c20b1c
> --- /dev/null
> +++ b/gcc/rust/lex/rust-token.cc
> @@ -0,0 +1,135 @@
> +// 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/>.
> +
> +#include "rust-token.h"
> +
> +#include "rust-diagnostics.h" // for error_at
> +
> +namespace Rust {
> +// Hackily defined way to get token description for enum value using x-macros
> +const char *
> +get_token_description (TokenId id)
> +{
> +  switch (id)
> +    {
> +#define RS_TOKEN(name, descr)                                                  \
> +  case name:                                                                   \
> +    return descr;
> +#define RS_TOKEN_KEYWORD(x, y) RS_TOKEN (x, y)
> +      RS_TOKEN_LIST
> +#undef RS_TOKEN_KEYWORD
> +#undef RS_TOKEN
> +    default:
> +      gcc_unreachable ();
> +    }
> +}
> +
> +/* Hackily defined way to get token description as a string for enum value using
> + * x-macros */
> +const char *
> +token_id_to_str (TokenId id)
> +{
> +  switch (id)
> +    {
> +#define RS_TOKEN(name, _)                                                      \
> +  case name:                                                                   \
> +    return #name;
> +#define RS_TOKEN_KEYWORD(x, y) RS_TOKEN (x, y)
> +      RS_TOKEN_LIST
> +#undef RS_TOKEN_KEYWORD
> +#undef RS_TOKEN
> +    default:
> +      gcc_unreachable ();
> +    }
> +}
> +
> +const char *
> +get_type_hint_string (PrimitiveCoreType type)
> +{
> +  switch (type)
> +    {
> +    case CORETYPE_BOOL:
> +      return "bool";
> +    case CORETYPE_CHAR:
> +      return "char";
> +    case CORETYPE_STR:
> +      return "str";
> +    // case CORETYPE_INT:
> +    case CORETYPE_ISIZE:
> +      return "isize";
> +    // case CORETYPE_UINT:
> +    case CORETYPE_USIZE:
> +      return "usize";
> +    case CORETYPE_F32:
> +      return "f32";
> +    case CORETYPE_F64:
> +      return "f64";
> +    case CORETYPE_I8:
> +      return "i8";
> +    case CORETYPE_I16:
> +      return "i16";
> +    case CORETYPE_I32:
> +      return "i32";
> +    case CORETYPE_I64:
> +      return "i64";
> +    case CORETYPE_I128:
> +      return "i128";
> +    case CORETYPE_U8:
> +      return "u8";
> +    case CORETYPE_U16:
> +      return "u16";
> +    case CORETYPE_U32:
> +      return "u32";
> +    case CORETYPE_U64:
> +      return "u64";
> +    case CORETYPE_U128:
> +      return "u128";
> +    case CORETYPE_PURE_DECIMAL:
> +      return "pure_decimal";
> +    case CORETYPE_UNKNOWN:
> +    default:
> +      return "unknown";
> +    }
> +}
> +
> +const char *
> +Token::get_type_hint_str () const
> +{
> +  return get_type_hint_string (type_hint);
> +}
> +
> +const std::string &
> +Token::get_str () const
> +{
> +  // FIXME: attempt to return null again
> +  // gcc_assert(str != NULL);
> +
> +  // HACK: allow referencing an empty string
> +  static const std::string empty = "";
> +
> +  if (str == NULL)
> +    {
> +      rust_error_at (get_locus (),
> +                    "attempted to get string for %<%s%>, which has no string. "
> +                    "returning empty string instead",
> +                    get_token_description ());
> +      return empty;
> +    }
> +  return *str;
> +}
> +} // namespace Rust
> diff --git a/gcc/rust/lex/rust-token.h b/gcc/rust/lex/rust-token.h
> new file mode 100644
> index 00000000000..3fa46a2cebe
> --- /dev/null
> +++ b/gcc/rust/lex/rust-token.h
> @@ -0,0 +1,455 @@
> +// 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_TOKEN_H
> +#define RUST_TOKEN_H
> +
> +#include "rust-linemap.h"
> +#include "rust-codepoint.h"
> +
> +// order: config, system, coretypes, input
> +#include "config.h"
> +#include "system.h"
> +#include "coretypes.h"
> +#include "input.h"
> +
> +namespace Rust {
> +// "Primitive core types" in Rust - the different int and float types, as well
> +// as some others
> +enum PrimitiveCoreType
> +{
> +  CORETYPE_UNKNOWN,
> +  // named primitives
> +  CORETYPE_BOOL,
> +  CORETYPE_CHAR,
> +  CORETYPE_STR,
> +  // okay technically int and uint are arch-dependent (pointer size)
> +  CORETYPE_INT,
> +  CORETYPE_UINT,
> +  // numbered number primitives
> +  CORETYPE_F32,
> +  CORETYPE_F64,
> +  CORETYPE_I8,
> +  CORETYPE_I16,
> +  CORETYPE_I32,
> +  CORETYPE_I64,
> +  CORETYPE_I128,
> +  CORETYPE_U8,
> +  CORETYPE_U16,
> +  CORETYPE_U32,
> +  CORETYPE_U64,
> +  CORETYPE_U128,
> +  // Pure decimals are used for tuple index.
> +  // Also means there is no type hint.
> +  CORETYPE_PURE_DECIMAL,
> +  // arch-dependent pointer sizes
> +  CORETYPE_ISIZE = CORETYPE_INT,
> +  CORETYPE_USIZE = CORETYPE_UINT
> +};
> +
> +// RS_TOKEN(name, description)
> +// RS_TOKEN_KEYWORD(name, identifier)
> +//
> +// Keep RS_TOKEN_KEYWORD sorted
> +
> +/* note that abstract, async, become, box, do, final, macro, override, priv,
> + * try, typeof, unsized, virtual, and yield are unused */
> +#define RS_TOKEN_LIST                                                          \
> +  RS_TOKEN (FIRST_TOKEN, "<first-token-marker>")                               \
> +  RS_TOKEN (END_OF_FILE, "end of file")                                        \
> +  RS_TOKEN (EXCLAM, "!")                                                       \
> +  RS_TOKEN (NOT_EQUAL, "!=")                                                   \
> +  RS_TOKEN (PERCENT, "%")                                                      \
> +  RS_TOKEN (PERCENT_EQ, "%=")                                                  \
> +  RS_TOKEN (AMP, "&")                                                          \
> +  RS_TOKEN (AMP_EQ, "&=")                                                      \
> +  RS_TOKEN (LOGICAL_AND, "&&")                                                 \
> +  RS_TOKEN (ASTERISK, "*")                                                     \
> +  RS_TOKEN (ASTERISK_EQ, "*=")                                                 \
> +  RS_TOKEN (PLUS, "+")                                                         \
> +  RS_TOKEN (PLUS_EQ, "+=")                                                     \
> +  RS_TOKEN (COMMA, ",")                                                        \
> +  RS_TOKEN (MINUS, "-")                                                        \
> +  RS_TOKEN (MINUS_EQ, "-=")                                                    \
> +  RS_TOKEN (RETURN_TYPE, "->")                                                 \
> +  RS_TOKEN (DOT, ".")                                                          \
> +  RS_TOKEN (DOT_DOT, "..")                                                     \
> +  RS_TOKEN (DOT_DOT_EQ, "..=")                                                 \
> +  RS_TOKEN (ELLIPSIS, "...")                                                   \
> +  RS_TOKEN (DIV, "/")                                                          \
> +  RS_TOKEN (DIV_EQ, "/=")                                                      \
> +  RS_TOKEN (COLON, ":")                                                        \
> +  RS_TOKEN (SEMICOLON, ";")                                                    \
> +  RS_TOKEN (LEFT_SHIFT, "<<")                                                  \
> +  RS_TOKEN (LEFT_SHIFT_EQ, "<<=")                                              \
> +  RS_TOKEN (LEFT_ANGLE, "<")                                                   \
> +  RS_TOKEN (LESS_OR_EQUAL, "<=")                                               \
> +  RS_TOKEN (EQUAL, "=")                                                        \
> +  RS_TOKEN (EQUAL_EQUAL, "==")                                                 \
> +  RS_TOKEN (MATCH_ARROW, "=>")                                                 \
> +  RS_TOKEN (RIGHT_ANGLE, ">")                                                  \
> +  RS_TOKEN (GREATER_OR_EQUAL, ">=")                                            \
> +  RS_TOKEN (RIGHT_SHIFT, ">>")                                                 \
> +  RS_TOKEN (RIGHT_SHIFT_EQ, ">>=")                                             \
> +  RS_TOKEN (PATTERN_BIND, "@")                                                 \
> +  RS_TOKEN (TILDE, "~")                                                        \
> +  RS_TOKEN (BACKSLASH, "\\")                                                   \
> +  RS_TOKEN (BACKTICK, "`")                                                     \
> +  RS_TOKEN (CARET, "^")                                                        \
> +  RS_TOKEN (CARET_EQ, "^=")                                                    \
> +  RS_TOKEN (PIPE, "|")                                                         \
> +  RS_TOKEN (PIPE_EQ, "|=")                                                     \
> +  RS_TOKEN (OR, "||")                                                          \
> +  RS_TOKEN (QUESTION_MARK, "?")                                                \
> +  RS_TOKEN (HASH, "#")                                                         \
> +  /* from here on, dodgy and may not be correct. not operators and may be      \
> +   * symbols */                                                                \
> +  /* RS_TOKEN(SPACE, " ") probably too dodgy */                                \
> +  /* RS_TOKEN(NEWLINE, "\n")*/                                                 \
> +  RS_TOKEN (SCOPE_RESOLUTION, "::") /* dodgy */                                \
> +  RS_TOKEN (SINGLE_QUOTE, "'") /* should i differentiate from lifetime? */     \
> +  RS_TOKEN (DOUBLE_QUOTE, "\"")                                                \
> +  RS_TOKEN (UNDERSCORE,                                                        \
> +           "_") /* TODO: treat as reserved word like mrustc instead? */       \
> +  RS_TOKEN (IDENTIFIER, "identifier")                                          \
> +  RS_TOKEN (INT_LITERAL,                                                       \
> +           "integer literal") /* do different int and float types need        \
> +                                 different literal types? */                  \
> +  RS_TOKEN (FLOAT_LITERAL, "float literal")                                    \
> +  RS_TOKEN (STRING_LITERAL, "string literal")                                  \
> +  RS_TOKEN (CHAR_LITERAL, "character literal")                                 \
> +  RS_TOKEN (BYTE_STRING_LITERAL, "byte string literal")                        \
> +  RS_TOKEN (BYTE_CHAR_LITERAL, "byte character literal")                       \
> +  RS_TOKEN (LIFETIME, "lifetime") /* TODO: improve token type */               \
> +  /* Have "interpolated" tokens (whatever that means)? identifer, path, type,  \
> +   * pattern, */                                                               \
> +  /* expression, statement, block, meta, item in mrustc (but not directly in   \
> +   * lexer). */                                                                \
> +  RS_TOKEN (LEFT_PAREN, "(")                                                   \
> +  RS_TOKEN (RIGHT_PAREN, ")")                                                  \
> +  RS_TOKEN (LEFT_CURLY, "{")                                                   \
> +  RS_TOKEN (RIGHT_CURLY, "}")                                                  \
> +  RS_TOKEN (LEFT_SQUARE, "[")                                                  \
> +  RS_TOKEN (RIGHT_SQUARE, "]")                                                 \
> +  /* Macros */                                                                 \
> +  RS_TOKEN (DOLLAR_SIGN, "$")                                                  \
> +  /* Doc Comments */                                                           \
> +  RS_TOKEN (INNER_DOC_COMMENT, "#![doc]")                                      \
> +  RS_TOKEN (OUTER_DOC_COMMENT, "#[doc]")                                       \
> +  /* have "weak" union and 'static keywords? */                                \
> +                                                                               \
> +  RS_TOKEN_KEYWORD (ABSTRACT, "abstract") /* unused */                         \
> +  RS_TOKEN_KEYWORD (AS, "as")                                                  \
> +  RS_TOKEN_KEYWORD (ASYNC, "async")   /* unused */                             \
> +  RS_TOKEN_KEYWORD (BECOME, "become") /* unused */                             \
> +  RS_TOKEN_KEYWORD (BOX, "box")              /* unused */                             \
> +  RS_TOKEN_KEYWORD (BREAK, "break")                                            \
> +  RS_TOKEN_KEYWORD (CONST, "const")                                            \
> +  RS_TOKEN_KEYWORD (CONTINUE, "continue")                                      \
> +  RS_TOKEN_KEYWORD (CRATE, "crate")                                            \
> +  /* FIXME: Do we need to add $crate (DOLLAR_CRATE) as a reserved kw? */       \
> +  RS_TOKEN_KEYWORD (DO, "do") /* unused */                                     \
> +  RS_TOKEN_KEYWORD (DYN, "dyn")                                                \
> +  RS_TOKEN_KEYWORD (ELSE, "else")                                              \
> +  RS_TOKEN_KEYWORD (ENUM_TOK, "enum")                                          \
> +  RS_TOKEN_KEYWORD (EXTERN_TOK, "extern")                                      \
> +  RS_TOKEN_KEYWORD (FALSE_LITERAL, "false")                                    \
> +  RS_TOKEN_KEYWORD (FINAL_TOK, "final") /* unused */                           \
> +  RS_TOKEN_KEYWORD (FN_TOK, "fn")                                              \
> +  RS_TOKEN_KEYWORD (FOR, "for")                                                \
> +  RS_TOKEN_KEYWORD (IF, "if")                                                  \
> +  RS_TOKEN_KEYWORD (IMPL, "impl")                                              \
> +  RS_TOKEN_KEYWORD (IN, "in")                                                  \
> +  RS_TOKEN_KEYWORD (LET, "let")                                                \
> +  RS_TOKEN_KEYWORD (LOOP, "loop")                                              \
> +  RS_TOKEN_KEYWORD (MACRO, "macro") /* unused */                               \
> +  RS_TOKEN_KEYWORD (MATCH_TOK, "match")                                        \
> +  RS_TOKEN_KEYWORD (MOD, "mod")                                                \
> +  RS_TOKEN_KEYWORD (MOVE, "move")                                              \
> +  RS_TOKEN_KEYWORD (MUT, "mut")                                                \
> +  RS_TOKEN_KEYWORD (OVERRIDE_TOK, "override") /* unused */                     \
> +  RS_TOKEN_KEYWORD (PRIV, "priv")            /* unused */                     \
> +  RS_TOKEN_KEYWORD (PUB, "pub")                                                \
> +  RS_TOKEN_KEYWORD (REF, "ref")                                                \
> +  RS_TOKEN_KEYWORD (RETURN_TOK, "return")                                      \
> +  RS_TOKEN_KEYWORD (SELF_ALIAS,                                                \
> +                   "Self") /* mrustc does not treat this as a reserved word*/ \
> +  RS_TOKEN_KEYWORD (SELF, "self")                                              \
> +  RS_TOKEN_KEYWORD (STATIC_TOK, "static")                                      \
> +  RS_TOKEN_KEYWORD (STRUCT_TOK, "struct")                                      \
> +  RS_TOKEN_KEYWORD (SUPER, "super")                                            \
> +  RS_TOKEN_KEYWORD (TRAIT, "trait")                                            \
> +  RS_TOKEN_KEYWORD (TRUE_LITERAL, "true")                                      \
> +  RS_TOKEN_KEYWORD (TRY, "try") /* unused */                                   \
> +  RS_TOKEN_KEYWORD (TYPE, "type")                                              \
> +  RS_TOKEN_KEYWORD (TYPEOF, "typeof") /* unused */                             \
> +  RS_TOKEN_KEYWORD (UNSAFE, "unsafe")                                          \
> +  RS_TOKEN_KEYWORD (UNSIZED, "unsized") /* unused */                           \
> +  RS_TOKEN_KEYWORD (USE, "use")                                                \
> +  RS_TOKEN_KEYWORD (VIRTUAL, "virtual") /* unused */                           \
> +  RS_TOKEN_KEYWORD (WHERE, "where")                                            \
> +  RS_TOKEN_KEYWORD (WHILE, "while")                                            \
> +  RS_TOKEN_KEYWORD (YIELD, "yield") /* unused */                               \
> +                                                                               \
> +  RS_TOKEN (LAST_TOKEN, "<last-token-marker>")
> +
> +// Contains all token types. Crappy implementation via x-macros.
> +enum TokenId
> +{
> +#define RS_TOKEN(name, _) name,
> +#define RS_TOKEN_KEYWORD(x, y) RS_TOKEN (x, y)
> +  RS_TOKEN_LIST
> +#undef RS_TOKEN_KEYWORD
> +#undef RS_TOKEN
> +};
> +
> +// dodgy "TokenPtr" declaration with Token forward declaration
> +class Token;
> +// A smart pointer (shared_ptr) to Token.
> +typedef std::shared_ptr<Token> TokenPtr;
> +// A smart pointer (shared_ptr) to a constant Token.
> +typedef std::shared_ptr<const Token> const_TokenPtr;
> +
> +// Hackily defined way to get token description for enum value using x-macros
> +const char *
> +get_token_description (TokenId id);
> +/* Hackily defined way to get token description as a string for enum value using
> + * x-macros */
> +const char *
> +token_id_to_str (TokenId id);
> +// Get type hint description as a string.
> +const char *
> +get_type_hint_string (PrimitiveCoreType type);
> +
> +// Represents a single token. Create using factory static methods.
> +class Token
> +{
> +private:
> +  // Token kind.
> +  TokenId token_id;
> +  // Token location.
> +  Location locus;
> +  // Associated text (if any) of token.
> +  std::unique_ptr<std::string> str;
> +  // TODO: maybe remove issues and just store std::string as value?
> +  /* Type hint for token based on lexer data (e.g. type suffix). Does not exist
> +   * for most tokens. */
> +  PrimitiveCoreType type_hint;
> +
> +  // Token constructor from token id and location. Has a null string.
> +  Token (TokenId token_id, Location location)
> +    : token_id (token_id), locus (location), str (nullptr),
> +      type_hint (CORETYPE_UNKNOWN)
> +  {}
> +
> +  // Token constructor from token id, location, and a string.
> +  Token (TokenId token_id, Location location, std::string &&paramStr)
> +    : token_id (token_id), locus (location),
> +      str (new std::string (std::move (paramStr))), type_hint (CORETYPE_UNKNOWN)
> +  {}
> +
> +  // Token constructor from token id, location, and a char.
> +  Token (TokenId token_id, Location location, char paramChar)
> +    : token_id (token_id), locus (location),
> +      str (new std::string (1, paramChar)), type_hint (CORETYPE_UNKNOWN)
> +  {}
> +
> +  // Token constructor from token id, location, and a "codepoint".
> +  Token (TokenId token_id, Location location, Codepoint paramCodepoint)
> +    : token_id (token_id), locus (location),
> +      str (new std::string (paramCodepoint.as_string ())),
> +      type_hint (CORETYPE_UNKNOWN)
> +  {}
> +
> +  // Token constructor from token id, location, a string, and type hint.
> +  Token (TokenId token_id, Location location, std::string &&paramStr,
> +        PrimitiveCoreType parType)
> +    : token_id (token_id), locus (location),
> +      str (new std::string (std::move (paramStr))), type_hint (parType)
> +  {}
> +
> +public:
> +  // No default constructor.
> +  Token () = delete;
> +  // Do not copy/assign tokens.
> +  Token (const Token &) = delete;
> +  Token &operator= (const Token &) = delete;
> +
> +  // Allow moving tokens.
> +  Token (Token &&other) = default;
> +  Token &operator= (Token &&other) = default;
> +
> +  ~Token () = default;
> +
> +  /* TODO: make_shared (which saves a heap allocation) does not work with the
> +   * private constructor */
> +
> +  // Makes and returns a new TokenPtr (with null string).
> +  static TokenPtr make (TokenId token_id, Location locus)
> +  {
> +    // return std::make_shared<Token> (token_id, locus);
> +    return TokenPtr (new Token (token_id, locus));
> +  }
> +
> +  // Makes and returns a new TokenPtr of type IDENTIFIER.
> +  static TokenPtr make_identifier (Location locus, std::string &&str)
> +  {
> +    // return std::make_shared<Token> (IDENTIFIER, locus, str);
> +    return TokenPtr (new Token (IDENTIFIER, locus, std::move (str)));
> +  }
> +
> +  // Makes and returns a new TokenPtr of type INT_LITERAL.
> +  static TokenPtr make_int (Location locus, std::string &&str,
> +                           PrimitiveCoreType type_hint = CORETYPE_UNKNOWN)
> +  {
> +    // return std::make_shared<Token> (INT_LITERAL, locus, str, type_hint);
> +    return TokenPtr (
> +      new Token (INT_LITERAL, locus, std::move (str), type_hint));
> +  }
> +
> +  // Makes and returns a new TokenPtr of type FLOAT_LITERAL.
> +  static TokenPtr make_float (Location locus, std::string &&str,
> +                             PrimitiveCoreType type_hint = CORETYPE_UNKNOWN)
> +  {
> +    // return std::make_shared<Token> (FLOAT_LITERAL, locus, str, type_hint);
> +    return TokenPtr (
> +      new Token (FLOAT_LITERAL, locus, std::move (str), type_hint));
> +  }
> +
> +  // Makes and returns a new TokenPtr of type STRING_LITERAL.
> +  static TokenPtr make_string (Location locus, std::string &&str)
> +  {
> +    // return std::make_shared<Token> (STRING_LITERAL, locus, str,
> +    // CORETYPE_STR);
> +    return TokenPtr (
> +      new Token (STRING_LITERAL, locus, std::move (str), CORETYPE_STR));
> +  }
> +
> +  // Makes and returns a new TokenPtr of type CHAR_LITERAL.
> +  static TokenPtr make_char (Location locus, Codepoint char_lit)
> +  {
> +    // return std::make_shared<Token> (CHAR_LITERAL, locus, char_lit);
> +    return TokenPtr (new Token (CHAR_LITERAL, locus, char_lit));
> +  }
> +
> +  // Makes and returns a new TokenPtr of type BYTE_CHAR_LITERAL.
> +  static TokenPtr make_byte_char (Location locus, char byte_char)
> +  {
> +    // return std::make_shared<Token> (BYTE_CHAR_LITERAL, locus, byte_char);
> +    return TokenPtr (new Token (BYTE_CHAR_LITERAL, locus, byte_char));
> +  }
> +
> +  // Makes and returns a new TokenPtr of type BYTE_STRING_LITERAL (fix).
> +  static TokenPtr make_byte_string (Location locus, std::string &&str)
> +  {
> +    // return std::make_shared<Token> (BYTE_STRING_LITERAL, locus, str);
> +    return TokenPtr (new Token (BYTE_STRING_LITERAL, locus, std::move (str)));
> +  }
> +
> +  // Makes and returns a new TokenPtr of type INNER_DOC_COMMENT.
> +  static TokenPtr make_inner_doc_comment (Location locus, std::string &&str)
> +  {
> +    return TokenPtr (new Token (INNER_DOC_COMMENT, locus, std::move (str)));
> +  }
> +
> +  // Makes and returns a new TokenPtr of type OUTER_DOC_COMMENT.
> +  static TokenPtr make_outer_doc_comment (Location locus, std::string &&str)
> +  {
> +    return TokenPtr (new Token (OUTER_DOC_COMMENT, locus, std::move (str)));
> +  }
> +
> +  // Makes and returns a new TokenPtr of type LIFETIME.
> +  static TokenPtr make_lifetime (Location locus, std::string &&str)
> +  {
> +    // return std::make_shared<Token> (LIFETIME, locus, str);
> +    return TokenPtr (new Token (LIFETIME, locus, std::move (str)));
> +  }
> +
> +  // Gets id of the token.
> +  TokenId get_id () const { return token_id; }
> +
> +  // Gets location of the token.
> +  Location get_locus () const { return locus; }
> +
> +  // Gets string description of the token.
> +  const std::string &
> +  get_str () const; /*{
> +// FIXME: put in header again when fix null problem
> +//gcc_assert(str != nullptr);
> +if (str == nullptr) {
> +error_at(get_locus(), "attempted to get string for '%s', which has no string.
> +returning empty string instead.", get_token_description()); return "";
> +}
> +return *str;
> +}*/
> +
> +  // Gets token's type hint info.
> +  PrimitiveCoreType get_type_hint () const
> +  {
> +    return type_hint == CORETYPE_PURE_DECIMAL ? CORETYPE_UNKNOWN : type_hint;
> +  }
> +
> +  // diagnostics (error reporting)
> +  const char *get_token_description () const
> +  {
> +    return Rust::get_token_description (token_id);
> +  }
> +
> +  // debugging
> +  const char *token_id_to_str () const
> +  {
> +    return Rust::token_id_to_str (token_id);
> +  }
> +
> +  // debugging
> +  const char *get_type_hint_str () const;
> +
> +  /* Returns whether the token is a literal of any type (int, float, char,
> +   * string, byte char, byte string). */
> +  bool is_literal () const
> +  {
> +    switch (token_id)
> +      {
> +      case INT_LITERAL:
> +      case FLOAT_LITERAL:
> +      case CHAR_LITERAL:
> +      case STRING_LITERAL:
> +      case BYTE_CHAR_LITERAL:
> +      case BYTE_STRING_LITERAL:
> +       return true;
> +      default:
> +       return false;
> +      }
> +  }
> +
> +  /* Returns whether the token actually has a string (regardless of whether it
> +   * should or not). */
> +  bool has_str () const { return str != nullptr; }
> +
> +  // Returns whether the token should have a string.
> +  bool should_have_str () const
> +  {
> +    return is_literal () || token_id == IDENTIFIER || token_id == LIFETIME;
> +  }
> +
> +  // Returns whether the token is a pure decimal int literal
> +  bool is_pure_decimal () const { return type_hint == CORETYPE_PURE_DECIMAL; }
> +};
> +} // namespace Rust
> +
> +#endif
> diff --git a/gcc/rust/rust-buffered-queue.h b/gcc/rust/rust-buffered-queue.h
> new file mode 100644
> index 00000000000..afcc4670cac
> --- /dev/null
> +++ b/gcc/rust/rust-buffered-queue.h
> @@ -0,0 +1,204 @@
> +// 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_BUFFERED_QUEUE_H
> +#define RUST_BUFFERED_QUEUE_H
> +
> +#include "rust-system.h"
> +
> +namespace Rust {
> +/* Buffered queue implementation. Items are of type T, queue source is of type
> + * Source. Note that this is owning of the source. */
> +template <typename T, typename Source> class buffered_queue
> +{
> +public:
> +  // Construct empty queue from Source src.
> +  buffered_queue (Source src) : source (src), start (0), end (0), buffer () {}
> +
> +  /* disable copying (since source is probably non-copyable)
> +   * TODO is this actually a good idea? If source is non-copyable, it would
> +   * just delete the copy constructor anyway.*/
> +  buffered_queue (const buffered_queue &other) = delete;
> +  buffered_queue &operator= (const buffered_queue &other) = delete;
> +
> +  // enable moving
> +  buffered_queue (buffered_queue &&other) = default;
> +  buffered_queue &operator= (buffered_queue &&other) = default;
> +
> +  // Returns token at position start + n (i.e. n tokens ahead).
> +  T peek (int n)
> +  {
> +    // n should not be behind
> +    rust_assert (n >= 0);
> +
> +    int num_queued_items = end - start;
> +    int num_items_required = n + 1;
> +
> +    // if required items go past end of queue, add them to queue
> +    if (num_items_required > num_queued_items)
> +      {
> +       int num_items_to_read = num_items_required - num_queued_items;
> +
> +       /* if queue length + extra items is larger than buffer size, expand
> +        * buffer */
> +       if (end + num_items_to_read > (int) buffer.size ())
> +         {
> +           // Resize the buffer by 1.5x
> +           int new_size = (buffer.size () + num_items_to_read);
> +           new_size += (new_size >> 1);
> +
> +           // old method:
> +           /*
> +                 // create new queue buffer with new size
> +                 std::vector<T> new_queue (new_size);
> +                 std::copy (buffer.begin () + start, buffer.begin () + end,
> +                            new_queue.begin ());
> +                 start = 0;
> +                 end = num_queued_items;
> +                 // TODO: would move be better here? optimisation for move with
> +                 // shared pointer?
> +
> +                 // swap member buffer and new queue buffer
> +                 std::swap (buffer, new_queue);
> +           */
> +
> +           // TODO: determine overhead of this approach vs copy. Should be
> +           // lower.
> +           std::vector<T> new_queue;
> +           new_queue.reserve (new_size);
> +           new_queue.insert (new_queue.begin (),
> +                             std::make_move_iterator (buffer.begin () + start),
> +                             std::make_move_iterator (buffer.begin () + end));
> +           start = 0;
> +           end = num_queued_items;
> +           // fill up rest of vector with junk so that indexing can work
> +           new_queue.insert (new_queue.begin () + end,
> +                             new_size - new_queue.size (), T ());
> +
> +           buffer = std::move (new_queue);
> +           /* this should be best method - std::move(range) would have
> +            * allocation problems; initial construction would require
> +            * reallocation upon resizing */
> +
> +           // validate that buffer is large enough now
> +           rust_assert (end + num_items_to_read <= (int) buffer.size ());
> +         }
> +
> +       /* iterate through buffer and invoke operator () on source on values
> +        * past original end */
> +       for (int i = 0; i < num_items_to_read; i++)
> +         buffer[end + i] = source.next ();
> +
> +       // move end based on additional items added
> +       end += num_items_to_read;
> +      }
> +
> +    rust_assert (0 <= start);
> +    rust_assert (start <= end);
> +    rust_assert (end <= (int) buffer.size ());
> +
> +    rust_assert (start + n < end);
> +
> +    // return value at start + n in buffer
> +    return buffer[start + n];
> +  }
> +
> +  /* TODO: add faster peek current token to remove overhead of conditional
> +   * branches? */
> +
> +  // Advances start by n + 1.
> +  void skip (int n)
> +  {
> +    // Call peek to ensure requested n is actually in queue.
> +    peek (n);
> +
> +    // Clear queue values from start to n (inclusive).
> +    for (int i = 0; i < (n + 1); i++)
> +      buffer[start + i] = T ();
> +
> +    // Move start forward by n + 1.
> +    start += (n + 1);
> +
> +    // Ensure start is not impossible somehow
> +    rust_assert (0 <= start);
> +    rust_assert (start <= end);
> +
> +    // Compact buffer if empty
> +    if (start == end)
> +      start = end = 0;
> +  }
> +
> +  /* Inserts element at front of vector. Really dirty hack with terrible
> +   * performance, only use when really needed. */
> +  void insert_at_front (T elem_to_insert)
> +  {
> +    // TODO: test as this may not work properly
> +
> +    // Insert actual element in buffer at start.
> +    buffer.insert (buffer.begin (), elem_to_insert);
> +
> +    /* Increase the end number since added element means all others have shifted
> +     * one along */
> +    end++;
> +  }
> +
> +  // Insert at arbitrary position (attempt)
> +  void insert (int index, T elem_to_insert)
> +  {
> +    // TODO: test as this may not work properly
> +
> +    // n should not be behind
> +    rust_assert (index >= 0);
> +
> +    // call peek to ensure that the items behind this (at least) are in queue
> +    if (index >= 1)
> +      peek (index - 1);
> +    else
> +      peek (index);
> +
> +    buffer.insert (buffer.begin () + start + index, std::move (elem_to_insert));
> +
> +    end++;
> +  }
> +
> +  // Replaces the current value in the buffer. Total HACK.
> +  void replace_current_value (T replacement)
> +  {
> +    // call peek to ensure value exists
> +    peek (0);
> +
> +    buffer[start] = std::move (replacement);
> +
> +    // don't move start or end
> +  }
> +
> +private:
> +  // Source of tokens for queue.
> +  Source source;
> +
> +  // Begin of range in buffer, inclusive.
> +  int start;
> +  // End of range in buffer, exclusive.
> +  int end;
> +
> +  // Queue buffer.
> +  std::vector<T> buffer;
> +};
> +} // namespace Rust
> +
> +#endif
> --
> 2.25.1
>

^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [PATCH Rust front-end v2 31/37] gccrs: Add GCC Rust front-end Make-lang.in
  2022-08-24 11:59 ` [PATCH Rust front-end v2 31/37] gccrs: Add GCC Rust front-end Make-lang.in herron.philip
@ 2022-09-14 13:34   ` Richard Biener
  2022-12-01 11:05     ` Thomas Schwinge
  0 siblings, 1 reply; 59+ messages in thread
From: Richard Biener @ 2022-09-14 13:34 UTC (permalink / raw)
  To: philip.herron; +Cc: gcc-patches, gcc-rust

On Wed, Aug 24, 2022 at 2:22 PM <herron.philip@googlemail.com> wrote:
>
> From: Philip Herron <philip.herron@embecosm.com>
>
> This is the Makefile for our front-end.
> ---
>  gcc/rust/Make-lang.in | 400 ++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 400 insertions(+)
>  create mode 100644 gcc/rust/Make-lang.in
>
> diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in
> new file mode 100644
> index 00000000000..f687cc2f667
> --- /dev/null
> +++ b/gcc/rust/Make-lang.in
> @@ -0,0 +1,400 @@
> +# Make-lang.in -- Top level -*- makefile -*- fragment for GCC Rust frontend.
> +
> +# Copyright (C) 2009-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/>.
> +
> +# This file provides the language dependent support in the main Makefile.
> +
> +#RUST_EXES = rust
> +
> +# Use strict warnings for this front end.
> +rust-warn = $(STRICT_WARN)
> +
> +# Installation name. Useful for cross compilers and used during install.
> +GCCRS_INSTALL_NAME := $(shell echo gccrs|sed '$(program_transform_name)')
> +GCCRS_TARGET_INSTALL_NAME := $(target_noncanonical)-$(shell echo gccrs|sed '$(program_transform_name)')
> +
> +# Define the names for selecting rust in LANGUAGES.
> +rust: rust1$(exeext)
> +
> +# Tell GNU make to ignore files by these names if they exist.
> +.PHONY: rust
> +
> +# removed GRS_CFLAGS from here
> +
> +CFLAGS-rust/rustspec.o += $(DRIVER_DEFINES)
> +
> +# Create the compiler driver gccrs.
> +# A compiler driver is the program that interprets command argument and can be called from the command
> +# line - e.g. gcc or g++, and not cc1, which is the actual compiler
> +
> +# Create driver objects
> +GCCRS_D_OBJS = \
> +   $(GCC_OBJS) \
> +   rust/rustspec.o \
> +   $(END)
> +
> +gccrs$(exeext): $(GCCRS_D_OBJS) $(EXTRA_GCC_OBJS) libcommon-target.a $(LIBDEPS)
> +       +$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) -o $@ \
> +         $(GCCRS_D_OBJS) $(EXTRA_GCC_OBJS) libcommon-target.a \
> +         $(EXTRA_GCC_LIBS) $(LIBS)
> +
> +# List of host object files used by the rust language - files for translation from the parse tree
> +# to GENERIC
> +# The compiler proper, not driver
> +GRS_OBJS = \
> +    rust/rust-lang.o \
> +    rust/rust-object-export.o \
> +    rust/rust-linemap.o \
> +    rust/rust-gcc-diagnostics.o \
> +    rust/rust-diagnostics.o \
> +    rust/rust-gcc.o \
> +    rust/rust-token.o \
> +    rust/rust-lex.o \
> +    rust/rust-cfg-parser.o \
> +    rust/rust-parse.o \
> +    rust/rust-ast-full-test.o \
> +    rust/rust-ast-dump.o \
> +    rust/rust-hir-dump.o \
> +    rust/rust-session-manager.o \
> +    rust/rust-compile.o \
> +    rust/rust-mangle.o \
> +    rust/rust-compile-resolve-path.o \
> +    rust/rust-macro-expand.o \
> +    rust/rust-attribute-visitor.o \
> +    rust/rust-macro-invoc-lexer.o \
> +    rust/rust-macro-substitute-ctx.o \
> +    rust/rust-macro-builtins.o \
> +    rust/rust-hir-full-test.o \
> +    rust/rust-hir-map.o \
> +    rust/rust-attributes.o \
> +    rust/rust-abi.o \
> +    rust/rust-ast-lower.o \
> +    rust/rust-ast-lower-base.o \
> +    rust/rust-ast-lower-pattern.o \
> +    rust/rust-ast-lower-item.o \
> +    rust/rust-name-resolver.o \
> +    rust/rust-ast-resolve.o \
> +    rust/rust-ast-resolve-base.o \
> +    rust/rust-ast-resolve-item.o \
> +    rust/rust-ast-resolve-pattern.o \
> +    rust/rust-ast-resolve-expr.o \
> +    rust/rust-ast-resolve-type.o \
> +    rust/rust-ast-resolve-path.o \
> +    rust/rust-ast-resolve-stmt.o \
> +    rust/rust-ast-resolve-struct-expr-field.o \
> +    rust/rust-hir-type-check.o \
> +    rust/rust-privacy-check.o \
> +    rust/rust-privacy-ctx.o \
> +    rust/rust-reachability.o \
> +    rust/rust-visibility-resolver.o \
> +    rust/rust-pub-restricted-visitor.o \
> +    rust/rust-privacy-reporter.o \
> +    rust/rust-tyty.o \
> +    rust/rust-tyty-call.o \
> +    rust/rust-tyctx.o \
> +    rust/rust-tyty-bounds.o \
> +    rust/rust-hir-type-check-util.o \
> +    rust/rust-hir-trait-resolve.o \
> +    rust/rust-hir-type-check-toplevel.o \
> +    rust/rust-hir-type-check-item.o \
> +    rust/rust-hir-type-check-type.o \
> +    rust/rust-hir-type-check-struct.o \
> +    rust/rust-hir-type-check-pattern.o \
> +    rust/rust-hir-type-check-expr.o \
> +    rust/rust-hir-type-check-stmt.o \
> +    rust/rust-hir-type-check-enumitem.o \
> +    rust/rust-hir-type-check-implitem.o \
> +    rust/rust-hir-dot-operator.o \
> +    rust/rust-coercion.o \
> +    rust/rust-casts.o \
> +    rust/rust-hir-type-check-base.o \
> +    rust/rust-autoderef.o \
> +    rust/rust-substitution-mapper.o \
> +    rust/rust-const-checker.o \
> +    rust/rust-lint-marklive.o \
> +    rust/rust-lint-unused-var.o \
> +    rust/rust-hir-type-check-path.o \
> +    rust/rust-unsafe-checker.o \
> +    rust/rust-compile-intrinsic.o \
> +    rust/rust-compile-pattern.o \
> +    rust/rust-compile-fnparam.o \
> +    rust/rust-base62.o \
> +    rust/rust-optional-test.o \
> +    rust/rust-compile-item.o \
> +    rust/rust-compile-implitem.o \
> +    rust/rust-compile-stmt.o \
> +    rust/rust-compile-expr.o \
> +    rust/rust-compile-type.o \
> +    rust/rust-compile-block.o \
> +    rust/rust-compile-struct-field-expr.o \
> +    rust/rust-constexpr.o \
> +    rust/rust-compile-base.o \
> +    rust/rust-tree.o \
> +    rust/rust-compile-context.o \
> +    rust/rust-export-metadata.o \
> +    rust/rust-imports.o \
> +    rust/rust-import-archive.o \
> +    rust/rust-extern-crate.o \
> +    $(END)
> +# removed object files from here
> +
> +# All language-specific object files for Rust.
> +RUST_ALL_OBJS = $(GRS_OBJS) $(RUST_TARGET_OBJS)
> +
> +rust_OBJS = $(RUST_ALL_OBJS) rust/rustspec.o
> +
> +# The compiler itself is called rust1 (formerly grs1)
> +rust1$(exeext): $(RUST_ALL_OBJS) attribs.o $(BACKEND) $(LIBDEPS)
> +       +$(LLINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) -o $@ \
> +             $(RUST_ALL_OBJS) attribs.o $(BACKEND) $(LIBS) $(BACKENDLIBS)
> +
> +# Build hooks.
> +
> +lang_checks += check-rust
> +lang_checks_parallelized += check-rust
> +check_rust_parallelize = 10
> +
> +# Copies its dependencies into the source directory. This generally should be used for generated files
> +# such as Bison output files which are not version-controlled, but should be included in any release
> +# tarballs. This target will be executed during a bootstrap if ‘--enable-generated-files-in-srcdir’
> +# was specified as a configure option.
> +rust.srcextra:
> +
> +rust.all.cross: gccrs$(exeext)
> +
> +# idk what this does but someone used it
> +rust.start.encap: gccrs$(exeext)
> +rust.rest.encap:
> +
> +# Build generated man pages for the front end from Texinfo manuals (see Man Page Generation), in the
> +# build directory. This target is only called if the necessary tools are available, but should ignore
> +# errors so as not to stop the build if errors occur; man pages are optional and the tools involved
> +# may be installed in a broken way.
> +rust.man:
> +
> +# Copies its dependencies into the source directory. These targets will be executed during a bootstrap
> +# if ‘--enable-generated-files-in-srcdir’ was specified as a configure option.
> +rust.srcman:
> +
> +# Clean hooks.
> +
> +rust.mostlyclean:
> +#      cd $(srcdir)/rust; rm -f *.o y.tab.h y.tab.c lex.yy.c
> +
> +rust.clean: rust.mostlyclean
> +
> +# Builds an etags TAGS file in the language subdirectory in the source tree.
> +# TODO: add more directories if I add more
> +rust.tags: force
> +       cd $(srcdir)/rust; \
> +       etags -o TAGS.sub *.y *.l *.cc *.h ast/*.h ast/*.cc lex/*.h lex/*.cc parse/*.h parse/*.cc; \
> +       etags --include TAGS.sub --include ../TAGS.sub
> +
> +# Build documentation hooks.
> +
> +# Build info documentation for the front end, in the build directory. This target is only called by
> +# ‘make bootstrap’ if a suitable version of makeinfo is available, so does not need to check for this,
> +# and should fail if an error occurs.
> +rust.info:
> +
> +rust.srcinfo:
> +
> +# Build DVI documentation for the front end, in the build directory. This should be done using
> +# $(TEXI2DVI), with appropriate -I arguments pointing to directories of included files.
> +rust.dvi:
> +
> +# Build PDF documentation for the front end, in the build directory. This should be done using
> +# $(TEXI2PDF), with appropriate -I arguments pointing to directories of included files.
> +rust.pdf:
> +
> +doc/rust.info:
> +doc/rust.dvi:
> +doc/rust.pdf:
> +
> +# Build HTML documentation for the front end, in the build directory.
> +rust.html:
> +
> +# Install hooks.
> +
> +# Install everything that is part of the front end, apart from the compiler executables listed in
> +# compilers in config-lang.in.
> +rust.install-common: installdirs
> +#      -rm -f $(DESTDIR)$(bindir)/$(GCCRS_INSTALL_NAME)$(exeext)
> +#      -rm -f $(DESTDIR)$(bindir)/$(GCCRS_TARGET_INSTALL_NAME)$(exeext)
> +#      $(INSTALL_PROGRAM) gccrs$(exeext) $(DESTDIR)$(bindir)/$(GCCRS_INSTALL_NAME)$(exeext)
> +#      if test -f $(DESTDIR)$(bindir)$(GCCRS_TARGET_INSTALL_NAME)$(exeext); then \
> +#        :; \
> +#      else \
> +#        cd $(DESTDIR)$(bindir) && \
> +#         $(LN) $(GCCRS_INSTALL_NAME)$(exeext) $(GCCRS_TARGET_INSTALL_NAME)$(exeext); \
> +#      fi
> +       -rm -f $(DESTDIR)$(bindir)/$(GCCRS_INSTALL_NAME)$(exeext)
> +       $(INSTALL_PROGRAM) gccrs$(exeext) $(DESTDIR)$(bindir)/$(GCCRS_INSTALL_NAME)$(exeext)
> +       rm -f $(DESTDIR)$(bindir)/$(GCCRS_TARGET_INSTALL_NAME)$(exeext); \
> +       ( cd $(DESTDIR)$(bindir) && \
> +      $(LN) $(GCCRS_INSTALL_NAME)$(exeext) $(GCCRS_TARGET_INSTALL_NAME)$(exeext) ); \
> +
> +# Install headers needed for plugins.
> +rust.install-plugin:
> +
> +# Uninstall files installed by installing the compiler. This is currently documented not to be
> +# supported, so the hook need not do anything.
> +rust.uninstall:
> +#      -rm -rf $(DESTDIR)/$(bindir)/$(GCCRS_INSTALL_NAME)$(exeext)
> +       -rm -f gccrs$(exeext) grs1$(exeext)
> +       -rm -f $(RUST_ALL_OBJS)
> +# ^those two are a maybe
> +
> +# Enable selftests for the rust frontend
> +selftest-rust: s-selftest-rust
> +
> +RUST_SELFTEST_FLAGS = -xrs $(SELFTEST_FLAGS)
> +RUST_SELFTEST_DEPS = rust1$(exeext) $(SELFTEST_DEPS)
> +
> +# Run the rust selftests
> +s-selftest-rust: $(RUST_SELFTEST_DEPS)
> +       $(GCC_FOR_TARGET) $(RUST_SELFTEST_FLAGS)
> +       $(STAMP) $@
> +
> +# Install info documentation for the front end, if it is present in the source directory. This target
> +# should have dependencies on info files that should be installed.
> +rust.install-info:
> +
> +rust.install-pdf:
> +
> +# Install man pages for the front end. This target should ignore errors.
> +rust.install-man:
> +
> +# Stage hooks:
> +# The toplevel makefile has already created stage?/rust at this point.
> +# Used for handling bootstrap
> +
> +rust.stage1: stage1-start
> +       -mv rust/*$(objext) stage1/rust
> +rust.stage2: stage2-start
> +       -mv rust/*$(objext) stage2/rust
> +rust.stage3: stage3-start
> +       -mv rust/*$(objext) stage3/rust
> +rust.stage4: stage4-start
> +       -mv rust/*$(objext) stage4/rust
> +rust.stageprofile: stageprofile-start
> +       -mv rust/*$(objext) stageprofile/rust
> +rust.stagefeedback: stagefeedback-start
> +       -mv rust/*$(objext) stagefeedback/rust
> +
> +CFLAGS-rust/rust-lang.o += -DDEFAULT_TARGET_VERSION=\"$(version)\" \
> +       -DDEFAULT_TARGET_MACHINE=\"$(target_noncanonical)\"
> +
> +# cross-folder includes - add new folders later
> +RUST_INCLUDES = -I $(srcdir)/rust \
> +       -I $(srcdir)/rust/lex \
> +       -I $(srcdir)/rust/parse \
> +       -I $(srcdir)/rust/ast \
> +       -I $(srcdir)/rust/analysis \
> +       -I $(srcdir)/rust/backend \
> +       -I $(srcdir)/rust/expand \
> +       -I $(srcdir)/rust/hir/tree \
> +       -I $(srcdir)/rust/hir \
> +       -I $(srcdir)/rust/resolve \
> +       -I $(srcdir)/rust/util \
> +       -I $(srcdir)/rust/typecheck \
> +       -I $(srcdir)/rust/checks/lints \
> +       -I $(srcdir)/rust/checks/errors \
> +       -I $(srcdir)/rust/checks/errors/privacy \
> +       -I $(srcdir)/rust/util \
> +        -I $(srcdir)/rust/metadata
> +
> +# add files that require cross-folder includes - currently rust-lang.o, rust-lex.o
> +CFLAGS-rust/rust-lang.o += $(RUST_INCLUDES)
> +CFLAGS-rust/rust-lex.o += $(RUST_INCLUDES)
> +CFLAGS-rust/rust-parse.o += $(RUST_INCLUDES)
> +CFLAGS-rust/rust-session-manager.o += $(RUST_INCLUDES)
> +
> +# TODO: possibly find a way to ensure C++11 compilation level here?
> +RUST_CXXFLAGS = -std=c++11 -Wno-unused-parameter -Werror=overloaded-virtual

You probably should inherit from $(CXXFLAGS) here which ensures C++11
compatibility.  Note you have to deal with non-g++ host compilers when not
bootstrapping so adding -Wno-unused-parameter -Werror=overload-virtual
needs to be guarded.

> +
> +# build all rust/lex files in rust folder, add cross-folder includes
> +rust/%.o: rust/lex/%.cc
> +       $(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $<
> +       $(POSTCOMPILE)
> +
> +# build all rust/parse files in rust folder, add cross-folder includes
> +rust/%.o: rust/parse/%.cc
> +       $(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $<
> +       $(POSTCOMPILE)
> +
> +# build rust/ast files in rust folder
> +rust/%.o: rust/ast/%.cc
> +       $(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $<
> +       $(POSTCOMPILE)
> +
> +# build rust/backend files in rust folder
> +rust/%.o: rust/backend/%.cc
> +       $(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $<
> +       $(POSTCOMPILE)
> +
> +# build rust/expand files in rust folder
> +rust/%.o: rust/expand/%.cc
> +       $(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $<
> +       $(POSTCOMPILE)
> +
> +# build rust/util files in rust folder
> +rust/%.o: rust/util/%.cc
> +       $(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $<
> +       $(POSTCOMPILE)
> +
> +# build rust/hir files in rust folder
> +rust/%.o: rust/hir/%.cc
> +       $(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $<
> +       $(POSTCOMPILE)
> +
> +# build rust/hir/tree files in rust folder
> +rust/%.o: rust/hir/tree/%.cc
> +       $(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $<
> +       $(POSTCOMPILE)
> +
> +# build rust/resolve files in rust folder
> +rust/%.o: rust/resolve/%.cc
> +       $(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $<
> +       $(POSTCOMPILE)
> +
> +# build rust/typecheck files in rust folder
> +rust/%.o: rust/typecheck/%.cc
> +       $(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $<
> +       $(POSTCOMPILE)
> +
> +# build rust/checks/lints files in rust folder
> +rust/%.o: rust/checks/lints/%.cc
> +       $(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $<
> +       $(POSTCOMPILE)
> +
> +# build rust/checks/errors files in rust folder
> +rust/%.o: rust/checks/errors/%.cc
> +       $(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $<
> +       $(POSTCOMPILE)
> +
> +# build privacy pass files in rust folder
> +rust/%.o: rust/checks/errors/privacy/%.cc
> +       $(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $<
> +       $(POSTCOMPILE)
> +
> +# build rust/metadata files in rust folder
> +rust/%.o: rust/metadata/%.cc
> +       $(COMPILE) $(RUST_CXXFLAGS) $(RUST_INCLUDES) $<
> +       $(POSTCOMPILE)
> --
> 2.25.1
>

^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [PATCH Rust front-end v2 09/37] gccrs: Add Lexer for Rust front-end
  2022-09-14 13:30   ` Richard Biener
@ 2022-09-14 13:39     ` Jakub Jelinek
  0 siblings, 0 replies; 59+ messages in thread
From: Jakub Jelinek @ 2022-09-14 13:39 UTC (permalink / raw)
  To: Richard Biener
  Cc: philip.herron, The Other, Arthur Cohen, Mark Wielaard, gcc-rust,
	gcc-patches

On Wed, Sep 14, 2022 at 03:30:39PM +0200, Richard Biener via Gcc-patches wrote:
> > +// 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/>.
> > +
> > +#include "rust-lex.h"
> > +
> > +#include "rust-system.h"      // for rust_assert and rust_unreachable
> > +#include "rust-diagnostics.h" // for rust_error_at
> > +#include "rust-linemap.h"
> > +#include "rust-session-manager.h"
> > +#include "safe-ctype.h"
> 
> just diving into a random patch here - I'm assuming I can take rust-lex.cc as
> a boiler-plate example for the #include structure.
> 
> In GCC all files should start with #including "config.h" followed by
> "system.h" where _all_ system, including C++ standard library headers
> should be pulled via system.h to allow working around OS and system
> compiler issues.
> 
> It might be that rust-system.h plays the role of config.h + system.h
> but then the rust-lex.h include is before it.
> 
> rust-codepoint.h including <string> is also problematic btw.

E.g. the Go FE has two parts, one very GCC specific that uses the explicit
config.h + system.h etc. includes, the other is generic and there it
includes go-system.h in every file first, where that starts with
#include <config.h>

various C++ standard includes

#include <system.h>
etc.

	Jakub


^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [PATCH Rust front-end v2 34/37] gccrs: add lang.opt
  2022-08-24 11:59 ` [PATCH Rust front-end v2 34/37] gccrs: add lang.opt herron.philip
@ 2022-09-14 13:39   ` Richard Biener
  2022-09-14 16:20     ` Thomas Schwinge
  0 siblings, 1 reply; 59+ messages in thread
From: Richard Biener @ 2022-09-14 13:39 UTC (permalink / raw)
  To: philip.herron; +Cc: gcc-patches, gcc-rust

On Wed, Aug 24, 2022 at 2:13 PM <herron.philip@googlemail.com> wrote:
>
> From: Philip Herron <philip.herron@embecosm.com>
>
> We have some rust specific langugage options note -fwrapv is enabled by
> default in the code. We are trying to respect options such as
> -Wunused-result which we get by porting over c++ no-discard for rust's
> must-use attribute, so we have enabled these by default directly here.

LGTM

Richard.

> ---
>  gcc/rust/lang.opt | 118 ++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 118 insertions(+)
>  create mode 100644 gcc/rust/lang.opt
>
> diff --git a/gcc/rust/lang.opt b/gcc/rust/lang.opt
> new file mode 100644
> index 00000000000..1f6855ede1d
> --- /dev/null
> +++ b/gcc/rust/lang.opt
> @@ -0,0 +1,118 @@
> +; Options for the Rust front end.
> +; Copyright (C) 2003-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/>.
> +
> +; See the GCC internals manual for a description of this file's format.
> +
> +; Please try to keep this file in ASCII collating order.
> +
> +; Describes command-line options used by this frontend
> +
> +Language
> +Rust
> +
> +I
> +Rust Joined Separate
> +; Documented in c.opt
> +
> +L
> +Rust Joined Separate
> +; Not documented
> +
> +Wall
> +Rust
> +; Documented in c.opt
> +
> +Wunused-variable
> +Rust Var(warn_unused_variable) Init(1) Warning
> +; documented in common.opt
> +
> +Wunused-const-variable
> +Rust Warning Alias(Wunused-const-variable=, 2, 0)
> +Warn when a const variable is unused.
> +
> +Wunused-const-variable=
> +Rust Joined RejectNegative UInteger Var(warn_unused_const_variable) Init(1) Warning LangEnabledBy(Rust,Wunused-variable, 1, 0) IntegerRange(0, 2)
> +Warn when a const variable is unused.
> +
> +Wunused-result
> +Rust Var(warn_unused_result) Init(1) Warning
> +Warn if a caller of a function, marked with attribute warn_unused_result, does not use its return value.
> +
> +frust-crate=
> +Rust Joined RejectNegative
> +-frust-crate=<name>             Set the crate name for the compilation
> +
> +frust-debug
> +Rust Var(flag_rust_debug)
> +Dump various Rust front end internals.
> +
> +frust-dump-
> +Rust Joined RejectNegative
> +-frust-dump-<type>     Dump Rust frontend internal information.
> +
> +frust-max-recursion-depth=
> +Rust RejectNegative Type(int) Var(rust_max_recursion_depth) Init(64)
> +-frust-max-recursion-depth=integer
> +
> +frust-mangling=
> +Rust Joined RejectNegative Enum(frust_mangling) Var(flag_rust_mangling)
> +-frust-mangling=[legacy|v0]     Choose which version to use for name mangling
> +
> +Enum
> +Name(frust_mangling) Type(int) UnknownError(unknown rust mangling option %qs)
> +
> +EnumValue
> +Enum(frust_mangling) String(legacy) Value(0)
> +
> +EnumValue
> +Enum(frust_mangling) String(v0) Value(1)
> +
> +frust-cfg=
> +Rust Joined RejectNegative
> +-frust-cfg=<name>             Set a config expansion option
> +
> +frust-edition=
> +Rust Joined RejectNegative Enum(frust_edition) Var(flag_rust_edition)
> +-frust-edition=[2015|2018|2021]             Choose which edition to use when compiling rust code
> +
> +Enum
> +Name(frust_edition) Type(int) UnknownError(unknown rust edition %qs)
> +
> +EnumValue
> +Enum(frust_edition) String(2015) Value(0)
> +
> +EnumValue
> +Enum(frust_edition) String(2018) Value(1)
> +
> +EnumValue
> +Enum(frust_edition) String(2021) Value(2)
> +
> +frust-embed-metadata
> +Rust Var(flag_rust_embed_metadata)
> +Flag to enable embeding metadata directly into object files
> +
> +frust-metadata-output=
> +Rust Joined RejectNegative
> +-frust-metadata-output=<path.rox>  Path to output crate metadata
> +
> +o
> +Rust Joined Separate
> +; Documented in common.opt
> +
> +; This comment is to ensure we retain the blank line above.
> --
> 2.25.1
>

^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [PATCH Rust front-end v2 32/37] gccrs: Add config-lang.in
  2022-08-24 11:59 ` [PATCH Rust front-end v2 32/37] gccrs: Add config-lang.in herron.philip
@ 2022-09-14 13:40   ` Richard Biener
  2023-02-20 13:33   ` Rust: Don't depend on unused 'target-libffi', 'target-libbacktrace' (was: [PATCH Rust front-end v2 32/37] gccrs: Add config-lang.in) Thomas Schwinge
  1 sibling, 0 replies; 59+ messages in thread
From: Richard Biener @ 2022-09-14 13:40 UTC (permalink / raw)
  To: philip.herron; +Cc: gcc-patches, gcc-rust

On Wed, Aug 24, 2022 at 2:19 PM <herron.philip@googlemail.com> wrote:
>
> From: Philip Herron <philip.herron@embecosm.com>
>
> This was a copy paste from gccgo front-end, we do not use any of the
> target_libs yet but we will need these when we support the libpanic crate.

LGTM

> ---
>  gcc/rust/config-lang.in | 34 ++++++++++++++++++++++++++++++++++
>  1 file changed, 34 insertions(+)
>  create mode 100644 gcc/rust/config-lang.in
>
> diff --git a/gcc/rust/config-lang.in b/gcc/rust/config-lang.in
> new file mode 100644
> index 00000000000..d2ff376032a
> --- /dev/null
> +++ b/gcc/rust/config-lang.in
> @@ -0,0 +1,34 @@
> +# config-lang.in -- Top level configure fragment for gcc Rust frontend.
> +
> +# Copyright (C) 2009-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/>.
> +
> +# Configure looks for the existence of this file to auto-config each language.
> +# We define several parameters used by configure:
> +#
> +# language     - name of language as it would appear in $(LANGUAGES)
> +# compilers    - value to add to $(COMPILERS)
> +
> +language="rust"
> +compilers="rust1\$(exeext)"
> +
> +build_by_default="no"
> +
> +target_libs="target-libffi target-libbacktrace"
> +
> +gtfiles="\$(srcdir)/rust/rust-lang.cc"
> --
> 2.25.1
>

^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [PATCH Rust front-end v2 33/37] gccrs: add lang-spec.h
  2022-08-24 11:59 ` [PATCH Rust front-end v2 33/37] gccrs: add lang-spec.h herron.philip
@ 2022-09-14 13:40   ` Richard Biener
  2022-10-14 16:33   ` Iain Buclaw
  1 sibling, 0 replies; 59+ messages in thread
From: Richard Biener @ 2022-09-14 13:40 UTC (permalink / raw)
  To: philip.herron; +Cc: gcc-patches, gcc-rust

On Wed, Aug 24, 2022 at 2:20 PM <herron.philip@googlemail.com> wrote:
>
> From: Philip Herron <philip.herron@embecosm.com>
>
> This specifies the extensions of the Rust language.

LGTM

> ---
>  gcc/rust/lang-specs.h | 26 ++++++++++++++++++++++++++
>  1 file changed, 26 insertions(+)
>  create mode 100644 gcc/rust/lang-specs.h
>
> diff --git a/gcc/rust/lang-specs.h b/gcc/rust/lang-specs.h
> new file mode 100644
> index 00000000000..9b14a559dd6
> --- /dev/null
> +++ b/gcc/rust/lang-specs.h
> @@ -0,0 +1,26 @@
> +/* lang-specs.h -- gcc driver specs for Rust frontend.
> +   Copyright (C) 2009-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/>.  */
> +
> +/* This is the contribution to the `default_compilers' array in gcc.cc
> +   for the Rust language.  */
> +
> +{".rs", "@rs", 0, 1, 0},
> +  {"@rs",
> +   "rust1 %i %(cc1_options) %{I*} %{L*} %D %{!fsyntax-only:%(invoke_as)}", 0, 1,
> +   0},
> --
> 2.25.1
>

^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [PATCH Rust front-end v2 07/37] gccrs: Add gcc-check-target check-rust
  2022-08-24 11:59 ` [PATCH Rust front-end v2 07/37] gccrs: Add gcc-check-target check-rust herron.philip
@ 2022-09-14 13:41   ` Richard Biener
  2022-09-14 14:04     ` Jakub Jelinek
  0 siblings, 1 reply; 59+ messages in thread
From: Richard Biener @ 2022-09-14 13:41 UTC (permalink / raw)
  To: philip.herron; +Cc: gcc-patches, gcc-rust

On Wed, Aug 24, 2022 at 2:08 PM <herron.philip@googlemail.com> wrote:
>
> From: Philip Herron <philip.herron@embecosm.com>
>
> This allows us to invoke the rust testsuite.

OK.

>
> ChangeLog:
>         * Makefile.def: Add autogen target
>         * Makefile.in: regenerate via autogen
> ---
>  Makefile.def | 1 +
>  Makefile.in  | 8 ++++++++
>  2 files changed, 9 insertions(+)
>
> diff --git a/Makefile.def b/Makefile.def
> index 3291b126b26..821016af3a2 100644
> --- a/Makefile.def
> +++ b/Makefile.def
> @@ -681,6 +681,7 @@ languages = { language=go;  gcc-check-target=check-go;
>  languages = { language=d;      gcc-check-target=check-d;
>                                 lib-check-target=check-target-libphobos; };
>  languages = { language=jit;    gcc-check-target=check-jit; };
> +languages = { language=rust;   gcc-check-target=check-rust; };
>
>  // Toplevel bootstrap
>  bootstrap_stage = { id=1 ; };
> diff --git a/Makefile.in b/Makefile.in
> index 1919dfee829..9ed2c0dec52 100644
> --- a/Makefile.in
> +++ b/Makefile.in
> @@ -60583,6 +60583,14 @@ check-gcc-jit:
>         (cd gcc && $(MAKE) $(GCC_FLAGS_TO_PASS) check-jit);
>  check-jit: check-gcc-jit
>
> +.PHONY: check-gcc-rust check-rust
> +check-gcc-rust:
> +       r=`${PWD_COMMAND}`; export r; \
> +       s=`cd $(srcdir); ${PWD_COMMAND}`; export s; \
> +       $(HOST_EXPORTS) \
> +       (cd gcc && $(MAKE) $(GCC_FLAGS_TO_PASS) check-rust);
> +check-rust: check-gcc-rust
> +
>
>  # The gcc part of install-no-fixedincludes, which relies on an intimate
>  # knowledge of how a number of gcc internal targets (inter)operate.  Delegate.
> --
> 2.25.1
>

^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [PATCH Rust front-end v2 07/37] gccrs: Add gcc-check-target check-rust
  2022-09-14 13:41   ` Richard Biener
@ 2022-09-14 14:04     ` Jakub Jelinek
  0 siblings, 0 replies; 59+ messages in thread
From: Jakub Jelinek @ 2022-09-14 14:04 UTC (permalink / raw)
  To: Richard Biener; +Cc: philip.herron, gcc-rust, gcc-patches

On Wed, Sep 14, 2022 at 03:41:48PM +0200, Richard Biener via Gcc-patches wrote:
> On Wed, Aug 24, 2022 at 2:08 PM <herron.philip@googlemail.com> wrote:
> >
> > From: Philip Herron <philip.herron@embecosm.com>
> >
> > This allows us to invoke the rust testsuite.
> 
> OK.
> 
> >
> > ChangeLog:
> >         * Makefile.def: Add autogen target
> >         * Makefile.in: regenerate via autogen

The changelog doesn't match what the patch does (Add rust language.
and Regenerate.), plus missing . at the end and in one case capital
letter after :

	Jakub


^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [PATCH Rust front-end v2 34/37] gccrs: add lang.opt
  2022-09-14 13:39   ` Richard Biener
@ 2022-09-14 16:20     ` Thomas Schwinge
  2022-09-15  6:23       ` Richard Biener
  0 siblings, 1 reply; 59+ messages in thread
From: Thomas Schwinge @ 2022-09-14 16:20 UTC (permalink / raw)
  To: Richard Biener, philip.herron; +Cc: gcc-rust, gcc-patches

Hi!

On 2022-09-14T15:39:47+0200, Richard Biener via Gcc-patches <gcc-patches@gcc.gnu.org> wrote:
> On Wed, Aug 24, 2022 at 2:13 PM <herron.philip@googlemail.com> wrote:
>>
>> From: Philip Herron <philip.herron@embecosm.com>
>>
>> We have some rust specific langugage options note -fwrapv is enabled by
>> default in the code. We are trying to respect options such as
>> -Wunused-result which we get by porting over c++ no-discard for rust's
>> must-use attribute, so we have enabled these by default directly here.
>
> LGTM

Actually, NACK.  ;-)

See <https://gcc.gnu.org/PR105914>
"gccrs setting warn_unused_variable breaks thousands of non-Rust tests".
The 'Init(1)' in 'gcc/rust/lang.opt' are in conflict with any (default)
'Init(0)' in other '*.opt' files -- remember that all '*.opt' files are
processed together by GCC's "options machinery", and thus
'gcc/rust/lang.opt' may (and here, does) affect non-Rust front end code.

I do have a patch series adding a 'LangInit(@var{language}, @var{value})'
option property, and patches to use this in a lot of places (here and in
other front ends' '*.opt' files), where currently we implement such logic
in '*.cc' files; '[...]_init_options_struct' functions via
'LANG_HOOKS_INIT_OPTIONS_STRUCT' (those then largely become empty and may
be removed).  I assume this idea to be uncontroversial; I "just" need to
get it polished and submitted...


Grüße
 Thomas


>>  gcc/rust/lang.opt | 118 ++++++++++++++++++++++++++++++++++++++++++++++
>>  1 file changed, 118 insertions(+)
>>  create mode 100644 gcc/rust/lang.opt
>>
>> diff --git a/gcc/rust/lang.opt b/gcc/rust/lang.opt
>> new file mode 100644
>> index 00000000000..1f6855ede1d
>> --- /dev/null
>> +++ b/gcc/rust/lang.opt
>> @@ -0,0 +1,118 @@
>> +; Options for the Rust front end.
>> +; Copyright (C) 2003-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/>.
>> +
>> +; See the GCC internals manual for a description of this file's format.
>> +
>> +; Please try to keep this file in ASCII collating order.
>> +
>> +; Describes command-line options used by this frontend
>> +
>> +Language
>> +Rust
>> +
>> +I
>> +Rust Joined Separate
>> +; Documented in c.opt
>> +
>> +L
>> +Rust Joined Separate
>> +; Not documented
>> +
>> +Wall
>> +Rust
>> +; Documented in c.opt
>> +
>> +Wunused-variable
>> +Rust Var(warn_unused_variable) Init(1) Warning
>> +; documented in common.opt
>> +
>> +Wunused-const-variable
>> +Rust Warning Alias(Wunused-const-variable=, 2, 0)
>> +Warn when a const variable is unused.
>> +
>> +Wunused-const-variable=
>> +Rust Joined RejectNegative UInteger Var(warn_unused_const_variable) Init(1) Warning LangEnabledBy(Rust,Wunused-variable, 1, 0) IntegerRange(0, 2)
>> +Warn when a const variable is unused.
>> +
>> +Wunused-result
>> +Rust Var(warn_unused_result) Init(1) Warning
>> +Warn if a caller of a function, marked with attribute warn_unused_result, does not use its return value.
>> +
>> +frust-crate=
>> +Rust Joined RejectNegative
>> +-frust-crate=<name>             Set the crate name for the compilation
>> +
>> +frust-debug
>> +Rust Var(flag_rust_debug)
>> +Dump various Rust front end internals.
>> +
>> +frust-dump-
>> +Rust Joined RejectNegative
>> +-frust-dump-<type>     Dump Rust frontend internal information.
>> +
>> +frust-max-recursion-depth=
>> +Rust RejectNegative Type(int) Var(rust_max_recursion_depth) Init(64)
>> +-frust-max-recursion-depth=integer
>> +
>> +frust-mangling=
>> +Rust Joined RejectNegative Enum(frust_mangling) Var(flag_rust_mangling)
>> +-frust-mangling=[legacy|v0]     Choose which version to use for name mangling
>> +
>> +Enum
>> +Name(frust_mangling) Type(int) UnknownError(unknown rust mangling option %qs)
>> +
>> +EnumValue
>> +Enum(frust_mangling) String(legacy) Value(0)
>> +
>> +EnumValue
>> +Enum(frust_mangling) String(v0) Value(1)
>> +
>> +frust-cfg=
>> +Rust Joined RejectNegative
>> +-frust-cfg=<name>             Set a config expansion option
>> +
>> +frust-edition=
>> +Rust Joined RejectNegative Enum(frust_edition) Var(flag_rust_edition)
>> +-frust-edition=[2015|2018|2021]             Choose which edition to use when compiling rust code
>> +
>> +Enum
>> +Name(frust_edition) Type(int) UnknownError(unknown rust edition %qs)
>> +
>> +EnumValue
>> +Enum(frust_edition) String(2015) Value(0)
>> +
>> +EnumValue
>> +Enum(frust_edition) String(2018) Value(1)
>> +
>> +EnumValue
>> +Enum(frust_edition) String(2021) Value(2)
>> +
>> +frust-embed-metadata
>> +Rust Var(flag_rust_embed_metadata)
>> +Flag to enable embeding metadata directly into object files
>> +
>> +frust-metadata-output=
>> +Rust Joined RejectNegative
>> +-frust-metadata-output=<path.rox>  Path to output crate metadata
>> +
>> +o
>> +Rust Joined Separate
>> +; Documented in common.opt
>> +
>> +; This comment is to ensure we retain the blank line above.
>> --
>> 2.25.1
>>
-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955

^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [PATCH Rust front-end v2 34/37] gccrs: add lang.opt
  2022-09-14 16:20     ` Thomas Schwinge
@ 2022-09-15  6:23       ` Richard Biener
  0 siblings, 0 replies; 59+ messages in thread
From: Richard Biener @ 2022-09-15  6:23 UTC (permalink / raw)
  To: Thomas Schwinge; +Cc: philip.herron, gcc-rust, gcc-patches

On Wed, Sep 14, 2022 at 6:20 PM Thomas Schwinge <thomas@codesourcery.com> wrote:
>
> Hi!
>
> On 2022-09-14T15:39:47+0200, Richard Biener via Gcc-patches <gcc-patches@gcc.gnu.org> wrote:
> > On Wed, Aug 24, 2022 at 2:13 PM <herron.philip@googlemail.com> wrote:
> >>
> >> From: Philip Herron <philip.herron@embecosm.com>
> >>
> >> We have some rust specific langugage options note -fwrapv is enabled by
> >> default in the code. We are trying to respect options such as
> >> -Wunused-result which we get by porting over c++ no-discard for rust's
> >> must-use attribute, so we have enabled these by default directly here.
> >
> > LGTM
>
> Actually, NACK.  ;-)
>
> See <https://gcc.gnu.org/PR105914>
> "gccrs setting warn_unused_variable breaks thousands of non-Rust tests".
> The 'Init(1)' in 'gcc/rust/lang.opt' are in conflict with any (default)
> 'Init(0)' in other '*.opt' files -- remember that all '*.opt' files are
> processed together by GCC's "options machinery", and thus
> 'gcc/rust/lang.opt' may (and here, does) affect non-Rust front end code.

Ah, I wondered about the duplicates adding to the list of supported FEs, I guess
that the awk script should check for obvious mismatches in other settings?

> I do have a patch series adding a 'LangInit(@var{language}, @var{value})'
> option property, and patches to use this in a lot of places (here and in
> other front ends' '*.opt' files), where currently we implement such logic
> in '*.cc' files; '[...]_init_options_struct' functions via
> 'LANG_HOOKS_INIT_OPTIONS_STRUCT' (those then largely become empty and may
> be removed).  I assume this idea to be uncontroversial; I "just" need to
> get it polished and submitted...
>
>
> Grüße
>  Thomas
>
>
> >>  gcc/rust/lang.opt | 118 ++++++++++++++++++++++++++++++++++++++++++++++
> >>  1 file changed, 118 insertions(+)
> >>  create mode 100644 gcc/rust/lang.opt
> >>
> >> diff --git a/gcc/rust/lang.opt b/gcc/rust/lang.opt
> >> new file mode 100644
> >> index 00000000000..1f6855ede1d
> >> --- /dev/null
> >> +++ b/gcc/rust/lang.opt
> >> @@ -0,0 +1,118 @@
> >> +; Options for the Rust front end.
> >> +; Copyright (C) 2003-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/>.
> >> +
> >> +; See the GCC internals manual for a description of this file's format.
> >> +
> >> +; Please try to keep this file in ASCII collating order.
> >> +
> >> +; Describes command-line options used by this frontend
> >> +
> >> +Language
> >> +Rust
> >> +
> >> +I
> >> +Rust Joined Separate
> >> +; Documented in c.opt
> >> +
> >> +L
> >> +Rust Joined Separate
> >> +; Not documented
> >> +
> >> +Wall
> >> +Rust
> >> +; Documented in c.opt
> >> +
> >> +Wunused-variable
> >> +Rust Var(warn_unused_variable) Init(1) Warning
> >> +; documented in common.opt
> >> +
> >> +Wunused-const-variable
> >> +Rust Warning Alias(Wunused-const-variable=, 2, 0)
> >> +Warn when a const variable is unused.
> >> +
> >> +Wunused-const-variable=
> >> +Rust Joined RejectNegative UInteger Var(warn_unused_const_variable) Init(1) Warning LangEnabledBy(Rust,Wunused-variable, 1, 0) IntegerRange(0, 2)
> >> +Warn when a const variable is unused.
> >> +
> >> +Wunused-result
> >> +Rust Var(warn_unused_result) Init(1) Warning
> >> +Warn if a caller of a function, marked with attribute warn_unused_result, does not use its return value.
> >> +
> >> +frust-crate=
> >> +Rust Joined RejectNegative
> >> +-frust-crate=<name>             Set the crate name for the compilation
> >> +
> >> +frust-debug
> >> +Rust Var(flag_rust_debug)
> >> +Dump various Rust front end internals.
> >> +
> >> +frust-dump-
> >> +Rust Joined RejectNegative
> >> +-frust-dump-<type>     Dump Rust frontend internal information.
> >> +
> >> +frust-max-recursion-depth=
> >> +Rust RejectNegative Type(int) Var(rust_max_recursion_depth) Init(64)
> >> +-frust-max-recursion-depth=integer
> >> +
> >> +frust-mangling=
> >> +Rust Joined RejectNegative Enum(frust_mangling) Var(flag_rust_mangling)
> >> +-frust-mangling=[legacy|v0]     Choose which version to use for name mangling
> >> +
> >> +Enum
> >> +Name(frust_mangling) Type(int) UnknownError(unknown rust mangling option %qs)
> >> +
> >> +EnumValue
> >> +Enum(frust_mangling) String(legacy) Value(0)
> >> +
> >> +EnumValue
> >> +Enum(frust_mangling) String(v0) Value(1)
> >> +
> >> +frust-cfg=
> >> +Rust Joined RejectNegative
> >> +-frust-cfg=<name>             Set a config expansion option
> >> +
> >> +frust-edition=
> >> +Rust Joined RejectNegative Enum(frust_edition) Var(flag_rust_edition)
> >> +-frust-edition=[2015|2018|2021]             Choose which edition to use when compiling rust code
> >> +
> >> +Enum
> >> +Name(frust_edition) Type(int) UnknownError(unknown rust edition %qs)
> >> +
> >> +EnumValue
> >> +Enum(frust_edition) String(2015) Value(0)
> >> +
> >> +EnumValue
> >> +Enum(frust_edition) String(2018) Value(1)
> >> +
> >> +EnumValue
> >> +Enum(frust_edition) String(2021) Value(2)
> >> +
> >> +frust-embed-metadata
> >> +Rust Var(flag_rust_embed_metadata)
> >> +Flag to enable embeding metadata directly into object files
> >> +
> >> +frust-metadata-output=
> >> +Rust Joined RejectNegative
> >> +-frust-metadata-output=<path.rox>  Path to output crate metadata
> >> +
> >> +o
> >> +Rust Joined Separate
> >> +; Documented in common.opt
> >> +
> >> +; This comment is to ensure we retain the blank line above.
> >> --
> >> 2.25.1
> >>
> -----------------
> Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955

^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [PATCH Rust front-end v2 33/37] gccrs: add lang-spec.h
  2022-08-24 11:59 ` [PATCH Rust front-end v2 33/37] gccrs: add lang-spec.h herron.philip
  2022-09-14 13:40   ` Richard Biener
@ 2022-10-14 16:33   ` Iain Buclaw
  1 sibling, 0 replies; 59+ messages in thread
From: Iain Buclaw @ 2022-10-14 16:33 UTC (permalink / raw)
  To: gcc-patches, philip.herron; +Cc: gcc-rust

Excerpts from herron.philip@googlemail.com's message of August 24, 2022 1:59 pm:
> From: Philip Herron <philip.herron@embecosm.com>
> 
> This specifies the extensions of the Rust language.
> ---
>  gcc/rust/lang-specs.h | 26 ++++++++++++++++++++++++++
>  1 file changed, 26 insertions(+)
>  create mode 100644 gcc/rust/lang-specs.h
> 
> diff --git a/gcc/rust/lang-specs.h b/gcc/rust/lang-specs.h
> new file mode 100644
> index 00000000000..9b14a559dd6
> --- /dev/null
> +++ b/gcc/rust/lang-specs.h
> @@ -0,0 +1,26 @@
> +/* lang-specs.h -- gcc driver specs for Rust frontend.
> +   Copyright (C) 2009-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/>.  */
> +
> +/* This is the contribution to the `default_compilers' array in gcc.cc
> +   for the Rust language.  */
> +
> +{".rs", "@rs", 0, 1, 0},
> +  {"@rs",

I've just noticed that you named the spec language "rs", I think you
mean to name it "@rust" instead.

Regards,
Iain.

^ permalink raw reply	[flat|nested] 59+ messages in thread

* Re: [PATCH Rust front-end v2 31/37] gccrs: Add GCC Rust front-end Make-lang.in
  2022-09-14 13:34   ` Richard Biener
@ 2022-12-01 11:05     ` Thomas Schwinge
  0 siblings, 0 replies; 59+ messages in thread
From: Thomas Schwinge @ 2022-12-01 11:05 UTC (permalink / raw)
  To: Richard Biener, arthur.cohen; +Cc: gcc-rust, gcc-patches

Hi!

On 2022-09-14T15:34:10+0200, Richard Biener via Gcc-patches <gcc-patches@gcc.gnu.org> wrote:
> On Wed, Aug 24, 2022 at 2:22 PM <herron.philip@googlemail.com> wrote:
>> --- /dev/null
>> +++ b/gcc/rust/Make-lang.in

>> +# TODO: possibly find a way to ensure C++11 compilation level here?
>> +RUST_CXXFLAGS = -std=c++11 -Wno-unused-parameter -Werror=overloaded-virtual
>
> You probably should inherit from $(CXXFLAGS) here which ensures C++11
> compatibility.

That was done in GCC/Rust commit da13bf4bbc46b399419c3e7f2c358a0efe3bdfdd
"make: Inherit CXXFLAGS, remove compiler-specific warnings", which
changed this to just 'RUST_CXXFLAGS = $(CXXFLAGS)'.

> Note you have to deal with non-g++ host compilers when not
> bootstrapping so adding -Wno-unused-parameter -Werror=overload-virtual
> needs to be guarded.

'-Werror=overloaded-virtual' is implied as by default, we have
'-Woverloaded-virtual' and '-Werror'.  (I've verified via putting
'class tmp : public Dump { void visit (int) {} };' into
'gcc/rust/ast/rust-ast-dump.cc', and getting a number of
'error: ‘virtual void Rust::AST::Dump::visit([...])’ was hidden'.)
(Maybe that isn't active for '--disable-bootstrap' builds, but that's
"OK".)

Remains only '-Wno-unused-parameter'.  That one should move into
'rust-warn', where we currently have:

>> +# Use strict warnings for this front end.
>> +rust-warn = $(STRICT_WARN)

Per GCC 4.8 documentation (baseline version to bootstrap GCC), we may use
'-Wno-[...]' without checking whether the corresponding '-W[...]' is
actually supported, so we may specify '-Wno-unused-parameter'
unconditionally, like existing ones, for example in 'gcc/Makefile.in':

    # These files are to have specific diagnostics suppressed, [...]
    gimple-match.o-warn = -Wno-unused
    generic-match.o-warn = -Wno-unused
    dfp.o-warn = -Wno-strict-aliasing

I thus understand that non-GCC compilers implement the same '-Wno-[...]'
behavior -- or maybe warning flags are not passed to those at all, at
stage 1 build where this is (only) relevant.

I've thus proposed <https://github.com/Rust-GCC/gccrs/pull/1670>
"'rust-warn += -Wno-unused-parameter'".


Grüße
 Thomas
-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955

^ permalink raw reply	[flat|nested] 59+ messages in thread

* Rust: Don't depend on unused 'target-libffi', 'target-libbacktrace' (was: [PATCH Rust front-end v2 32/37] gccrs: Add config-lang.in)
  2022-08-24 11:59 ` [PATCH Rust front-end v2 32/37] gccrs: Add config-lang.in herron.philip
  2022-09-14 13:40   ` Richard Biener
@ 2023-02-20 13:33   ` Thomas Schwinge
  1 sibling, 0 replies; 59+ messages in thread
From: Thomas Schwinge @ 2023-02-20 13:33 UTC (permalink / raw)
  To: gcc-patches; +Cc: gcc-rust, Philip Herron, Philip Herron

[-- Attachment #1: Type: text/plain, Size: 1310 bytes --]

Hi!

On 2022-08-24T12:59:51+0100, herron.philip@googlemail.com wrote:
> From: Philip Herron <philip.herron@embecosm.com>
>
> This was a copy paste from gccgo front-end, we do not use any of the
> target_libs yet but we will need these when we support the libpanic crate.

> --- /dev/null
> +++ b/gcc/rust/config-lang.in

> +target_libs="target-libffi target-libbacktrace"

(By the way, this setting of 'target_libs' was not present in the v1
<https://inbox.sourceware.org/gcc-patches/20220727134040.843750-2-philip.herron@embecosm.com>
"[PATCH Rust front-end v1 1/4] Add skeleton Rust front-end folder".)

So there's the issue that not all GCC target configurations support
building those libraries.  Given that they're indeed unused, is it be OK
to push the attached
"Rust: Don't depend on unused 'target-libffi', 'target-libbacktrace'"?
(..., and once we get to the point where we'd like to use libffi and/or
libbacktrace, then think about how to handle those GCC target
configurations.)


Grüße
 Thomas


-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Rust-Don-t-depend-on-unused-target-libffi-target-lib.patch --]
[-- Type: text/x-diff, Size: 991 bytes --]

From 5d85939a3e3ebcfcf3f2ac9d3f2e01cbb1736578 Mon Sep 17 00:00:00 2001
From: Thomas Schwinge <thomas@codesourcery.com>
Date: Mon, 20 Feb 2023 13:01:50 +0100
Subject: [PATCH] Rust: Don't depend on unused 'target-libffi',
 'target-libbacktrace'

For example:

    configure: error: "libffi has not been ported to nvptx-unknown-none."

Follow-up to commit a75f038c069cc3a23b214854bedf04321fe88bc5
"gccrs: Add config-lang.in", which said:

> This was a copy/paste from gccgo front-end. We do not use any of the
> target_libs yet, [...]

	gcc/rust/
	* config-lang.in (target_libs): Remove.
---
 gcc/rust/config-lang.in | 2 --
 1 file changed, 2 deletions(-)

diff --git a/gcc/rust/config-lang.in b/gcc/rust/config-lang.in
index 89055be5cd4..aac66c9b962 100644
--- a/gcc/rust/config-lang.in
+++ b/gcc/rust/config-lang.in
@@ -29,6 +29,4 @@ compilers="rust1\$(exeext)"
 
 build_by_default="no"
 
-target_libs="target-libffi target-libbacktrace"
-
 gtfiles="\$(srcdir)/rust/rust-lang.cc"
-- 
2.25.1


^ permalink raw reply	[flat|nested] 59+ messages in thread

end of thread, other threads:[~2023-02-20 13:33 UTC | newest]

Thread overview: 59+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-08-24 11:59 Rust frontend patches v2 herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 01/37] Use DW_ATE_UTF for the Rust 'char' type herron.philip
2022-08-24 14:28   ` Jason Merrill
2022-08-24 11:59 ` [PATCH Rust front-end v2 02/37] gccrs: Add nessecary hooks for a Rust front-end testsuite herron.philip
2022-09-10  4:05   ` Mike Stump
2022-08-24 11:59 ` [PATCH Rust front-end v2 03/37] gccrs: Add Debug info testsuite herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 04/37] gccrs: Add link cases testsuite herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 05/37] gccrs: Add general compilation test cases herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 06/37] gccrs: Add execution " herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 07/37] gccrs: Add gcc-check-target check-rust herron.philip
2022-09-14 13:41   ` Richard Biener
2022-09-14 14:04     ` Jakub Jelinek
2022-08-24 11:59 ` [PATCH Rust front-end v2 08/37] gccrs: Add the Rust front-end AST data structures herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 09/37] gccrs: Add Lexer for Rust front-end herron.philip
2022-09-14 13:30   ` Richard Biener
2022-09-14 13:39     ` Jakub Jelinek
2022-08-24 11:59 ` [PATCH Rust front-end v2 10/37] gccrs: Add Parser " herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 11/37] gccrs: Add expansion pass for the " herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 12/37] gccrs: Add name resolution pass to " herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 13/37] gccrs: Add second intermedite representation called HIR herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 14/37] gccrs: Add AST to HIR lowering pass herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 15/37] gccrs: Add wrapper for make_unique herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 16/37] gccrs: Add port of FNV hash used during legacy symbol mangling herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 17/37] gccrs: Add Rust ABI enum helpers herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 18/37] gccrs: Add Base62 implementation herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 19/37] gccrs: Add implementation of Optional herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 20/37] gccrs: Add attributes checker herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 21/37] gccrs: Add helpers mappings canonical path and lang items herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 22/37] gccrs: Add type resolution and trait solving pass herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 23/37] gccrs: Add unsafe checks for Rust herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 24/37] gccrs: Add const checker herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 25/37] gccrs: Add privacy checks herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 26/37] gccrs: Add dead code scan on HIR herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 27/37] gccrs: Add unused variable scan herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 28/37] gccrs: Add metadata ouptput pass herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 29/37] gccrs: HIR to GCC GENERIC lowering herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 30/37] gccrs: These are wrappers ported from reusing gccgo herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 31/37] gccrs: Add GCC Rust front-end Make-lang.in herron.philip
2022-09-14 13:34   ` Richard Biener
2022-12-01 11:05     ` Thomas Schwinge
2022-08-24 11:59 ` [PATCH Rust front-end v2 32/37] gccrs: Add config-lang.in herron.philip
2022-09-14 13:40   ` Richard Biener
2023-02-20 13:33   ` Rust: Don't depend on unused 'target-libffi', 'target-libbacktrace' (was: [PATCH Rust front-end v2 32/37] gccrs: Add config-lang.in) Thomas Schwinge
2022-08-24 11:59 ` [PATCH Rust front-end v2 33/37] gccrs: add lang-spec.h herron.philip
2022-09-14 13:40   ` Richard Biener
2022-10-14 16:33   ` Iain Buclaw
2022-08-24 11:59 ` [PATCH Rust front-end v2 34/37] gccrs: add lang.opt herron.philip
2022-09-14 13:39   ` Richard Biener
2022-09-14 16:20     ` Thomas Schwinge
2022-09-15  6:23       ` Richard Biener
2022-08-24 11:59 ` [PATCH Rust front-end v2 35/37] gccrs: add compiler driver herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 36/37] gccrs: compiler proper interface kicks off the pipeline herron.philip
2022-08-24 11:59 ` [PATCH Rust front-end v2 37/37] gccrs: Add README, CONTRIBUTING and compiler logo herron.philip
2022-08-25  9:46 ` Rust frontend patches v2 Philip Herron
2022-08-25  9:52   ` Martin Liška
2022-08-25 10:18     ` Philip Herron
2022-08-25 12:50       ` Frank Ch. Eigler
2022-08-25 13:44         ` Philip Herron
2022-08-25 11:13     ` Mark Wielaard

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).