Repository: mun-lang/mun (language: rust)

Issue:
Update to official inkwell crate
Currently, Mun uses LLVM 7.x.

It is beneficial for us to update to the latest LLVM but this requires some work to be compatible with the latest Inkwell.


Discussion / hints:
@joshuawarner32 has commenced work on this

Fix patch:
diff --git a/.github/actions/install-llvm/dist/index.js b/.github/actions/install-llvm/dist/index.js
index 6c6f85e58..6c13f27d7 100644
--- a/.github/actions/install-llvm/dist/index.js
+++ b/.github/actions/install-llvm/dist/index.js
@@ -1114,18 +1114,18 @@ async function execute(cmd) {
 (async () => {
     try {
         if(isLinux) {
-            await exec.exec("sudo apt install llvm-7 llvm-7-* liblld-7*");
+            await exec.exec("sudo apt install llvm-8 llvm-8-* liblld-8*");
         } else if(isMacOS) {
-            await exec.exec("brew install llvm@7")
-            let llvmPath = await execute("brew --prefix llvm@7");
+            await exec.exec("brew install llvm@8")
+            let llvmPath = await execute("brew --prefix llvm@8");
             core.addPath(`${llvmPath}/bin`)   
         } else if(isWindows) {
-            let llvmCachedPath = tc.find("llvm", "7.1.0", "windows-x64");
+            let llvmCachedPath = tc.find("llvm", "8.0.1", "windows-x64");
             if(!llvmCachedPath) {
                 let _7zPath = path.join(__dirname, '..', 'externals', '7zr.exe');
-                llvmCachedPath = await tc.downloadTool("https://github.com/mun-lang/llvm-package-windows/releases/download/v7.1.0/llvm-7.1.0-windows-x64-msvc15.7z")
+                llvmCachedPath = await tc.downloadTool("https://github.com/mun-lang/llvm-package-windows/releases/download/v8.0.1/llvm-8.0.1-windows-x64-msvc15.7z")
                     .then(downloadPath => tc.extract7z(downloadPath, null, _7zPath))
-                    .then(extractPath => tc.cacheDir(extractPath, "llvm", "7.1.0", "windows-x64"));
+                    .then(extractPath => tc.cacheDir(extractPath, "llvm", "8.0.1", "windows-x64"));
             }
             core.addPath(`${llvmCachedPath}/bin`)
             core.exportVariable('LIBCLANG_PATH', `${llvmCachedPath}/bin`)
diff --git a/.github/actions/install-llvm/index.js b/.github/actions/install-llvm/index.js
index 597b8d669..6009b64f0 100644
--- a/.github/actions/install-llvm/index.js
+++ b/.github/actions/install-llvm/index.js
@@ -30,18 +30,18 @@ export async function execute(cmd) {
 (async () => {
     try {
         if(isLinux) {
-            await exec.exec("sudo apt install llvm-7 llvm-7-* liblld-7*");
+            await exec.exec("sudo apt install llvm-8 llvm-8-* liblld-8*");
         } else if(isMacOS) {
-            await exec.exec("brew install llvm@7")
-            let llvmPath = await execute("brew --prefix llvm@7");
+            await exec.exec("brew install llvm@8")
+            let llvmPath = await execute("brew --prefix llvm@8");
             core.addPath(`${llvmPath}/bin`)   
         } else if(isWindows) {
-            let llvmCachedPath = tc.find("llvm", "7.1.0", "windows-x64");
+            let llvmCachedPath = tc.find("llvm", "8.0.1", "windows-x64");
             if(!llvmCachedPath) {
                 let _7zPath = path.join(__dirname, '..', 'externals', '7zr.exe');
-                llvmCachedPath = await tc.downloadTool("https://github.com/mun-lang/llvm-package-windows/releases/download/v7.1.0/llvm-7.1.0-windows-x64-msvc15.7z")
+                llvmCachedPath = await tc.downloadTool("https://github.com/mun-lang/llvm-package-windows/releases/download/v8.0.1/llvm-8.0.1-windows-x64-msvc15.7z")
                     .then(downloadPath => tc.extract7z(downloadPath, null, _7zPath))
-                    .then(extractPath => tc.cacheDir(extractPath, "llvm", "7.1.0", "windows-x64"));
+                    .then(extractPath => tc.cacheDir(extractPath, "llvm", "8.0.1", "windows-x64"));
             }
             core.addPath(`${llvmCachedPath}/bin`)
             core.exportVariable('LIBCLANG_PATH', `${llvmCachedPath}/bin`)
diff --git a/README.md b/README.md
index 6438f3de2..e0c2ff804 100644
--- a/README.md
+++ b/README.md
@@ -153,7 +153,7 @@ rustup](https://www.rust-lang.org/tools/install).
 
 #### LLVM
 
-Mun targets LLVM 7.1.0. Installing LLVM is platform dependant and as such can be
+Mun targets LLVM 8.0.1. Installing LLVM is platform dependant and as such can be
 a pain. The following steps are how we install LLVM on [our CI
 runners](.github/actions/install-llvm/index.js):
 
@@ -161,7 +161,7 @@ runners](.github/actions/install-llvm/index.js):
   of LLVM, e.g.:
   ```bash
   # Ubuntu 18.04
-  sudo apt install llvm-7 llvm-7-* liblld-7*
+  sudo apt install llvm-8 llvm-8-* liblld-8*
   ```
 * **Arch Linux** The binary version of LLVM can currently only be installed
   using an AUR helper, such as `yay`:
@@ -181,21 +181,21 @@ runners](.github/actions/install-llvm/index.js):
   symlink; e.g. `ln -s /usr/lib/libtinfo.so.6 /usr/lib/libtinfo.so.5`),
   otherwise download the library.
 * **macOS**: [Brew](https://brew.sh/) contains a binary distribution of LLVM
-  7.1.0. However, as it's not the latest version, it won't be added to the path.
+  8.0.1. However, as it's not the latest version, it won't be added to the path.
   We are using [llvm-sys](https://crates.io/crates/llvm-sys) to manage version,
-  but another option is to export the `LLVM_SYS_70_PREFIX` variable, which will
+  but another option is to export the `LLVM_SYS_80_PREFIX` variable, which will
   not clutter your `PATH`. To install:
   ```bash
-  brew install llvm@7
+  brew install llvm@8
   # Export LLVM_SYS_PREFIX to not clubber PATH
-  export LLVM_SYS_PREFIX=$(brew --prefix llvm@7)
+  export LLVM_SYS_PREFIX=$(brew --prefix llvm@8)
   ```
 * **windows**: Binary distrubutions are available for Windows on the LLVM
   website, but they do not contain a number of libraries that are required by
   Mun. To avoid having to go to the trouble of compiling LLVM yourself, we
   created a [repository](https://github.com/mun-lang/llvm-package-windows) that
   automatically compiles the required binaries. It also contains a
-  [release](https://github.com/mun-lang/llvm-package-windows/releases/download/v7.1.0/llvm-7.1.0-windows-x64-msvc15.7z)
+  [release](https://github.com/mun-lang/llvm-package-windows/releases/v8.0.1)
   that you can download and extract to your machine. Once downloaded and
   extracted, add the `<extract_dir>/bin` folder to the `PATH` environment
   variable.
diff --git a/crates/mun_codegen/Cargo.toml b/crates/mun_codegen/Cargo.toml
index c4150806b..0b7020d76 100644
--- a/crates/mun_codegen/Cargo.toml
+++ b/crates/mun_codegen/Cargo.toml
@@ -17,7 +17,7 @@ abi = { version = "=0.2.0", path = "../mun_abi", package = "mun_abi" }
 hir = { version = "=0.2.0", path = "../mun_hir", package = "mun_hir" }
 mun_codegen_macros = { path = "../mun_codegen_macros", package = "mun_codegen_macros" }
 mun_target = { version = "=0.2.0", path = "../mun_target" }
-mun_lld = { version = "=70.2.0", path = "../mun_lld" }
+mun_lld = { version = "=80.0.0", path = "../mun_lld" }
 anyhow = "1.0.31"
 thiserror = "1.0.19"
 salsa = "0.15.0"
@@ -26,12 +26,8 @@ array-init="0.1.0"
 tempfile = "3"
 paste = "0.1.6"
 parking_lot = "0.10"
-by_address = "1.0"
-
-[dependencies.inkwell]
-git = "https://github.com/mun-lang/inkwell"
-rev = "4448bcd"
-features = ["llvm7-0"]
+inkwell = { version = "=0.1.0-llvm8sample", features = ["llvm8-0"]}
+by_address = "1.0.4"
 
 [dev-dependencies]
 abi = { path = "../mun_abi", package = "mun_abi" }
diff --git a/crates/mun_codegen/src/assembly.rs b/crates/mun_codegen/src/assembly.rs
index 453da91b4..aeba47690 100644
--- a/crates/mun_codegen/src/assembly.rs
+++ b/crates/mun_codegen/src/assembly.rs
@@ -1,8 +1,11 @@
-use crate::{IrDatabase, ModuleBuilder};
+use crate::code_gen::{CodeGenContext, ModuleBuilder};
+use crate::db::CodeGenDatabase;
+use inkwell::context::Context;
 use std::path::Path;
 use std::sync::Arc;
 use tempfile::NamedTempFile;
 
+/// An `Assembly` is a reference to a Mun library stored on disk.
 #[derive(Debug)]
 pub struct Assembly {
     file: NamedTempFile,
@@ -30,14 +33,23 @@ impl Assembly {
     }
 }
 
-/// Create a new temporary file that contains the linked object
-pub fn assembly_query(db: &dyn IrDatabase, file_id: hir::FileId) -> Arc<Assembly> {
+/// Builds an assembly for the specified file
+pub(crate) fn build_assembly(db: &dyn CodeGenDatabase, file_id: hir::FileId) -> Arc<Assembly> {
+    // Construct a temporary file for the assembly
     let file = NamedTempFile::new().expect("could not create temp file for shared object");
 
-    let module_builder = ModuleBuilder::new(db, file_id).expect("could not create ModuleBuilder");
+    // Setup the code generation context
+    let inkwell_context = Context::create();
+    let code_gen_context = CodeGenContext::new(&inkwell_context, db);
+
+    // Construct the module
+    let module_builder =
+        ModuleBuilder::new(&code_gen_context, file_id).expect("could not create ModuleBuilder");
     let obj_file = module_builder
         .build()
         .expect("unable to create object file");
+
+    // Translate the object file into a shared object
     obj_file
         .into_shared_object(file.path())
         .expect("could not link object file");
diff --git a/crates/mun_codegen/src/code_gen.rs b/crates/mun_codegen/src/code_gen.rs
index 6d77baba7..61fae2fe2 100644
--- a/crates/mun_codegen/src/code_gen.rs
+++ b/crates/mun_codegen/src/code_gen.rs
@@ -1,189 +1,18 @@
-use crate::code_gen::linker::LinkerError;
-use crate::db::StructMapping;
-use crate::value::{IrTypeContext, IrValueContext};
-use crate::IrDatabase;
-use hir::FileId;
-use inkwell::targets::TargetData;
 use inkwell::{
     module::Module,
     passes::{PassManager, PassManagerBuilder},
-    targets::{CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine},
     OptimizationLevel,
 };
-use mun_target::spec;
-use parking_lot::RwLock;
-use std::collections::HashMap;
-use std::io::{self, Write};
-use std::{path::Path, sync::Arc};
-use tempfile::NamedTempFile;
-use thiserror::Error;
 
-mod linker;
+mod context;
+mod error;
+mod module_builder;
+mod object_file;
 pub mod symbols;
 
-#[derive(Debug, Error)]
-enum CodeGenerationError {
-    #[error("{0}")]
-    LinkerError(#[source] LinkerError),
-    #[error("error linking modules: {0}")]
-    ModuleLinkerError(String),
-    #[error("unknown target triple: {0}")]
-    UnknownTargetTriple(String),
-    #[error("error creating target machine")]
-    CouldNotCreateTargetMachine,
-    #[error("error creating object file")]
-    CouldNotCreateObjectFile(io::Error),
-    #[error("error generating machine code")]
-    CodeGenerationError(String),
-}
-
-impl From<LinkerError> for CodeGenerationError {
-    fn from(e: LinkerError) -> Self {
-        CodeGenerationError::LinkerError(e)
-    }
-}
-
-pub struct ObjectFile {
-    target: spec::Target,
-    obj_file: NamedTempFile,
-}
-
-impl ObjectFile {
-    /// Constructs a new object file from the specified `module` for `target`
-    pub fn new(
-        target: &spec::Target,
-        target_machine: &TargetMachine,
-        module: Arc<inkwell::module::Module>,
-    ) -> Result<Self, anyhow::Error> {
-        let obj = target_machine
-            .write_to_memory_buffer(&module, FileType::Object)
-            .map_err(|e| CodeGenerationError::CodeGenerationError(e.to_string()))?;
-
-        let mut obj_file = tempfile::NamedTempFile::new()
-            .map_err(CodeGenerationError::CouldNotCreateObjectFile)?;
-        obj_file
-            .write(obj.as_slice())
-            .map_err(CodeGenerationError::CouldNotCreateObjectFile)?;
-
-        Ok(Self {
-            target: target.clone(),
-            obj_file,
-        })
-    }
-
-    /// Links the object file into a shared object.
-    pub fn into_shared_object(self, output_path: &Path) -> Result<(), anyhow::Error> {
-        // Construct a linker for the target
-        let mut linker = linker::create_with_target(&self.target);
-        linker.add_object(self.obj_file.path())?;
-
-        // Link the object
-        linker.build_shared_object(&output_path)?;
-        linker.finalize()?;
-
-        Ok(())
-    }
-}
-
-/// A struct that can be used to build an LLVM `Module`.
-pub struct ModuleBuilder<'a> {
-    db: &'a dyn IrDatabase,
-    file_id: FileId,
-    _target: inkwell::targets::Target,
-    target_machine: inkwell::targets::TargetMachine,
-    assembly_module: Arc<inkwell::module::Module>,
-}
-
-impl<'a> ModuleBuilder<'a> {
-    /// Constructs module for the given `hir::FileId` at the specified output file location.
-    pub fn new(db: &'a dyn IrDatabase, file_id: FileId) -> Result<Self, anyhow::Error> {
-        let target = db.target();
-
-        // Construct a module for the assembly
-        let assembly_module = Arc::new(
-            db.context()
-                .create_module(db.file_relative_path(file_id).as_str()),
-        );
-
-        // Initialize the x86 target
-        Target::initialize_x86(&InitializationConfig::default());
-
-        // Retrieve the LLVM target using the specified target.
-        let llvm_target = Target::from_triple(&target.llvm_target)
-            .map_err(|e| CodeGenerationError::UnknownTargetTriple(e.to_string()))?;
-        assembly_module.set_target(&llvm_target);
-
-        // Construct target machine for machine code generation
-        let target_machine = llvm_target
-            .create_target_machine(
-                &target.llvm_target,
-                &target.options.cpu,
-                &target.options.features,
-                db.optimization_lvl(),
-                RelocMode::PIC,
-                CodeModel::Default,
-            )
-            .ok_or(CodeGenerationError::CouldNotCreateTargetMachine)?;
-
-        Ok(Self {
-            db,
-            file_id,
-            _target: llvm_target,
-            target_machine,
-            assembly_module,
-        })
-    }
-
-    /// Constructs an object file.
-    pub fn build(self) -> Result<ObjectFile, anyhow::Error> {
-        let group_ir = self.db.group_ir(self.file_id);
-        let file = self.db.file_ir(self.file_id);
-
-        // Clone the LLVM modules so that we can modify it without modifying the cached value.
-        self.assembly_module
-            .link_in_module(group_ir.llvm_module.clone())
-            .map_err(|e| CodeGenerationError::ModuleLinkerError(e.to_string()))?;
-
-        self.assembly_module
-            .link_in_module(file.llvm_module.clone())
-            .map_err(|e| CodeGenerationError::ModuleLinkerError(e.to_string()))?;
-
-        let target_data = self.db.target_data();
-        let struct_types = self.db.type_to_struct_mapping();
-        let type_context = IrTypeContext {
-            context: &self.assembly_module.get_context(),
-            target_data: target_data.as_ref(),
-            struct_types: struct_types.as_ref(),
-        };
-
-        let value_context = IrValueContext {
-            type_context: &type_context,
-            context: type_context.context,
-            module: &self.assembly_module,
-        };
-
-        // Generate the `get_info` method.
-        symbols::gen_reflection_ir(
-            self.db,
-            &value_context,
-            &file.api,
-            &group_ir.dispatch_table,
-            &group_ir.type_table,
-        );
-
-        // Optimize the assembly module
-        optimize_module(&self.assembly_module, self.db.optimization_lvl());
-
-        // Debug print the IR
-        //println!("{}", assembly_module.print_to_string().to_string());
-
-        ObjectFile::new(
-            &self.db.target(),
-            &self.target_machine,
-            self.assembly_module,
-        )
-    }
-}
+pub use context::CodeGenContext;
+pub use error::CodeGenerationError;
+pub use module_builder::ModuleBuilder;
 
 /// Optimizes the specified LLVM `Module` using the default passes for the given
 /// `OptimizationLevel`.
@@ -195,17 +24,3 @@ fn optimize_module(module: &Module, optimization_lvl: OptimizationLevel) {
     pass_builder.populate_module_pass_manager(&module_pass_manager);
     module_pass_manager.run_on(module);
 }
-
-/// Create an inkwell TargetData from the target in the database
-pub(crate) fn target_data_query(db: &dyn IrDatabase) -> Arc<TargetData> {
-    Arc::new(TargetData::create(&db.target().data_layout))
-}
-
-/// Returns a mapping from struct type to a struct type in the context. This is a query because the
-/// value of struct type depends on the target we compile for.
-pub(crate) fn type_to_struct_mapping_query(
-    db: &dyn IrDatabase,
-) -> by_address::ByAddress<Arc<StructMapping>> {
-    let _ = db.target_data();
-    by_address::ByAddress(Arc::new(RwLock::new(HashMap::default())))
-}
diff --git a/crates/mun_codegen/src/code_gen/context.rs b/crates/mun_codegen/src/code_gen/context.rs
new file mode 100644
index 000000000..3f24a4e5c
--- /dev/null
+++ b/crates/mun_codegen/src/code_gen/context.rs
@@ -0,0 +1,46 @@
+use crate::{ir::ty::HirTypeCache, CodeGenDatabase};
+use inkwell::{context::Context, module::Module, targets::TargetMachine, types::StructType};
+use std::{cell::RefCell, collections::HashMap, sync::Arc};
+
+pub struct CodeGenContext<'db, 'ink> {
+    /// The current LLVM context
+    pub context: &'ink Context,
+
+    /// The Salsa HIR database
+    pub db: &'db dyn hir::HirDatabase,
+
+    /// A mapping from Rust types' full path (without lifetime) to inkwell types
+    pub rust_types: RefCell<HashMap<&'static str, StructType<'ink>>>,
+
+    /// A mapping from HIR types to LLVM struct types
+    pub hir_types: HirTypeCache<'db, 'ink>,
+
+    /// The optimization level
+    pub optimization_level: inkwell::OptimizationLevel,
+
+    /// The target to generate code for
+    pub target_machine: Arc<TargetMachine>,
+}
+
+impl<'db, 'ink> CodeGenContext<'db, 'ink> {
+    /// Constructs a new `CodeGenContext` from an LLVM context and a `CodeGenDatabase`.
+    pub fn new(context: &'ink Context, db: &'db dyn CodeGenDatabase) -> Self {
+        let target_machine = db.target_machine().0;
+        Self {
+            context,
+            rust_types: RefCell::new(HashMap::default()),
+            hir_types: HirTypeCache::new(context, db.upcast(), target_machine.get_target_data()),
+            optimization_level: db.optimization_level(),
+            target_machine,
+            db: db.upcast(),
+        }
+    }
+
+    /// Constructs a new `Module` with the specified name and initialized for the target.
+    pub fn create_module(&self, name: impl AsRef<str>) -> Module<'ink> {
+        let module = self.context.create_module(name.as_ref());
+        module.set_data_layout(&self.target_machine.get_target_data().get_data_layout());
+        module.set_triple(&self.target_machine.get_triple());
+        module
+    }
+}
diff --git a/crates/mun_codegen/src/code_gen/error.rs b/crates/mun_codegen/src/code_gen/error.rs
new file mode 100644
index 000000000..210c890ee
--- /dev/null
+++ b/crates/mun_codegen/src/code_gen/error.rs
@@ -0,0 +1,12 @@
+use std::io;
+use thiserror::Error;
+
+#[derive(Debug, Error)]
+pub enum CodeGenerationError {
+    #[error("error linking modules: {0}")]
+    ModuleLinkerError(String),
+    #[error("error creating object file")]
+    CouldNotCreateObjectFile(io::Error),
+    #[error("error generating machine code")]
+    CodeGenerationError(String),
+}
diff --git a/crates/mun_codegen/src/code_gen/module_builder.rs b/crates/mun_codegen/src/code_gen/module_builder.rs
new file mode 100644
index 000000000..721215400
--- /dev/null
+++ b/crates/mun_codegen/src/code_gen/module_builder.rs
@@ -0,0 +1,93 @@
+use crate::code_gen::object_file::ObjectFile;
+use crate::code_gen::{optimize_module, symbols, CodeGenContext, CodeGenerationError};
+use crate::ir::file::gen_file_ir;
+use crate::ir::file_group::gen_file_group_ir;
+use crate::value::{IrTypeContext, IrValueContext};
+use hir::FileId;
+use inkwell::module::{Linkage, Module};
+
+/// A struct that can be used to build an LLVM `Module`.
+pub struct ModuleBuilder<'db, 'ink, 'ctx> {
+    code_gen: &'ctx CodeGenContext<'db, 'ink>,
+    file_id: FileId,
+    assembly_module: Module<'ink>,
+}
+
+impl<'db, 'ink, 'ctx> ModuleBuilder<'db, 'ink, 'ctx> {
+    /// Constructs a module for the given `hir::FileId` using the provided `CodeGenContext`.
+    pub fn new(
+        code_gen: &'ctx CodeGenContext<'db, 'ink>,
+        file_id: FileId,
+    ) -> Result<Self, anyhow::Error> {
+        // Construct a module for the assembly
+        let assembly_name = code_gen.db.file_relative_path(file_id);
+        let assembly_module = code_gen.create_module(assembly_name);
+
+        Ok(Self {
+            code_gen,
+            file_id,
+            assembly_module,
+        })
+    }
+
+    /// Constructs an object file.
+    pub fn build(self) -> Result<ObjectFile, anyhow::Error> {
+        let group_ir = gen_file_group_ir(self.code_gen, self.file_id);
+        let file = gen_file_ir(self.code_gen, &group_ir, self.file_id);
+
+        // Clone the LLVM modules so that we can modify it without modifying the cached value.
+        self.assembly_module
+            .link_in_module(group_ir.llvm_module.clone())
+            .map_err(|e| CodeGenerationError::ModuleLinkerError(e.to_string()))?;
+
+        self.assembly_module
+            .link_in_module(file.llvm_module.clone())
+            .map_err(|e| CodeGenerationError::ModuleLinkerError(e.to_string()))?;
+
+        if self.code_gen.db.target().options.is_like_windows {
+            // Add the useless `_fltused` symbol to indicate that the object file supports
+            // floating-point values. This is required for Windows.
+            let fltused =
+                self.assembly_module
+                    .add_global(self.code_gen.context.i32_type(), None, "_fltused");
+            fltused.set_initializer(&self.code_gen.context.i32_type().const_int(1, false));
+            fltused.set_linkage(Linkage::External);
+        }
+
+        let target_data = self.code_gen.target_machine.get_target_data();
+        let type_context = IrTypeContext {
+            context: &self.code_gen.context,
+            target_data: &target_data,
+            struct_types: &self.code_gen.rust_types,
+        };
+
+        let value_context = IrValueContext {
+            type_context: &type_context,
+            context: type_context.context,
+            module: &self.assembly_module,
+        };
+
+        // Generate the `get_info` method.
+        symbols::gen_reflection_ir(
+            self.code_gen.db,
+            &value_context,
+            &file.api,
+            &group_ir.dispatch_table,
+            &group_ir.type_table,
+            &self.code_gen.hir_types,
+            self.code_gen.optimization_level,
+        );
+
+        // Optimize the assembly module
+        optimize_module(&self.assembly_module, self.code_gen.optimization_level);
+
+        // Debug print the IR
+        //println!("{}", assembly_module.print_to_string().to_string());
+
+        ObjectFile::new(
+            &self.code_gen.db.target(),
+            &self.code_gen.target_machine,
+            &self.assembly_module,
+        )
+    }
+}
diff --git a/crates/mun_codegen/src/code_gen/object_file.rs b/crates/mun_codegen/src/code_gen/object_file.rs
new file mode 100644
index 000000000..89ac44171
--- /dev/null
+++ b/crates/mun_codegen/src/code_gen/object_file.rs
@@ -0,0 +1,49 @@
+use crate::code_gen::CodeGenerationError;
+use crate::linker;
+use inkwell::targets::{FileType, TargetMachine};
+use mun_target::spec;
+use std::io::Write;
+use std::path::Path;
+use tempfile::NamedTempFile;
+
+pub struct ObjectFile {
+    target: spec::Target,
+    obj_file: NamedTempFile,
+}
+
+impl ObjectFile {
+    /// Constructs a new object file from the specified `module` for `target`
+    pub fn new(
+        target: &spec::Target,
+        target_machine: &TargetMachine,
+        module: &inkwell::module::Module,
+    ) -> Result<Self, anyhow::Error> {
+        let obj = target_machine
+            .write_to_memory_buffer(&module, FileType::Object)
+            .map_err(|e| CodeGenerationError::CodeGenerationError(e.to_string()))?;
+
+        let mut obj_file = tempfile::NamedTempFile::new()
+            .map_err(CodeGenerationError::CouldNotCreateObjectFile)?;
+        obj_file
+            .write(obj.as_slice())
+            .map_err(CodeGenerationError::CouldNotCreateObjectFile)?;
+
+        Ok(Self {
+            target: target.clone(),
+            obj_file,
+        })
+    }
+
+    /// Links the object file into a shared object.
+    pub fn into_shared_object(self, output_path: &Path) -> Result<(), anyhow::Error> {
+        // Construct a linker for the target
+        let mut linker = linker::create_with_target(&self.target);
+        linker.add_object(self.obj_file.path())?;
+
+        // Link the object
+        linker.build_shared_object(&output_path)?;
+        linker.finalize()?;
+
+        Ok(())
+    }
+}
diff --git a/crates/mun_codegen/src/code_gen/symbols.rs b/crates/mun_codegen/src/code_gen/symbols.rs
index 7c24a007f..2821ec8a0 100644
--- a/crates/mun_codegen/src/code_gen/symbols.rs
+++ b/crates/mun_codegen/src/code_gen/symbols.rs
@@ -1,25 +1,27 @@
-use crate::ir::types as ir;
-use crate::ir::{
-    dispatch_table::{DispatchTable, DispatchableFunction},
-    function,
-    type_table::TypeTable,
+use crate::{
+    ir::ty::HirTypeCache,
+    ir::types as ir,
+    ir::{
+        dispatch_table::{DispatchTable, DispatchableFunction},
+        function,
+        type_table::TypeTable,
+    },
+    type_info::TypeInfo,
+    value::{AsValue, CanInternalize, Global, IrValueContext, IterAsIrValue, Value},
 };
-use crate::type_info::TypeInfo;
-use crate::value::{AsValue, CanInternalize, Global, IrValueContext, IterAsIrValue, Value};
-use crate::IrDatabase;
-use hir::Ty;
+use hir::{HirDatabase, Ty};
 use inkwell::{attributes::Attribute, module::Linkage, AddressSpace};
-use std::collections::HashSet;
-use std::ffi::CString;
+use std::{collections::HashSet, ffi::CString};
 
 /// Construct a `MunFunctionPrototype` struct for the specified HIR function.
-fn gen_prototype_from_function(
-    db: &dyn IrDatabase,
-    context: &IrValueContext,
+fn gen_prototype_from_function<'ink>(
+    db: &dyn HirDatabase,
+    context: &IrValueContext<'ink, '_, '_>,
     function: hir::Function,
-) -> ir::FunctionPrototype {
+    hir_types: &HirTypeCache,
+) -> ir::FunctionPrototype<'ink> {
     let module = context.module;
-    let name = function.name(db.upcast()).to_string();
+    let name = function.name(db).to_string();
 
     // Internalize the name of the function prototype
     let name_str = CString::new(name.clone())
@@ -27,15 +29,15 @@ fn gen_prototype_from_function(
         .intern(format!("fn_sig::<{}>::name", &name), context);
 
     // Get the `ir::TypeInfo` pointer for the return type of the function
-    let fn_sig = function.ty(db.upcast()).callable_sig(db.upcast()).unwrap();
-    let return_type = gen_signature_return_type(db, context, fn_sig.ret().clone());
+    let fn_sig = function.ty(db).callable_sig(db).unwrap();
+    let return_type = gen_signature_return_type(context, fn_sig.ret(), hir_types);
 
     // Construct an array of pointers to `ir::TypeInfo`s for the arguments of the prototype
     let arg_types = fn_sig
         .params()
         .iter()
         .map(|ty| {
-            TypeTable::get(module, &db.type_info(ty.clone()), context)
+            TypeTable::get(module, &hir_types.type_info(ty), context)
                 .expect("expected a TypeInfo for a prototype argument but it was not found")
         })
         .into_const_private_pointer_or_null(format!("fn_sig::<{}>::arg_types", &name), context);
@@ -51,10 +53,10 @@ fn gen_prototype_from_function(
 }
 
 /// Construct a `MunFunctionPrototype` struct for the specified dispatch table function.
-fn gen_prototype_from_dispatch_entry(
-    context: &IrValueContext,
+fn gen_prototype_from_dispatch_entry<'ink>(
+    context: &IrValueContext<'ink, '_, '_>,
     function: &DispatchableFunction,
-) -> ir::FunctionPrototype {
+) -> ir::FunctionPrototype<'ink> {
     let module = context.module;
 
     // Internalize the name of the function prototype
@@ -95,27 +97,27 @@ fn gen_prototype_from_dispatch_entry(
 
 /// Given a function, construct a pointer to a `ir::TypeInfo` global that represents the return type
 /// of the function; or `null` if the return type is empty.
-fn gen_signature_return_type(
-    db: &dyn IrDatabase,
-    context: &IrValueContext,
-    ret_type: Ty,
-) -> Value<*const ir::TypeInfo> {
+fn gen_signature_return_type<'ink>(
+    context: &IrValueContext<'ink, '_, '_>,
+    ret_type: &Ty,
+    hir_types: &HirTypeCache,
+) -> Value<'ink, *const ir::TypeInfo<'ink>> {
     gen_signature_return_type_from_type_info(
         context,
         if ret_type.is_empty() {
             None
         } else {
-            Some(db.type_info(ret_type))
+            Some(hir_types.type_info(ret_type))
         },
     )
 }
 
 /// Given a function, construct a pointer to a `ir::TypeInfo` global that represents the return type
 /// of the function; or `null` if the return type is empty.
-fn gen_signature_return_type_from_type_info(
-    context: &IrValueContext,
+fn gen_signature_return_type_from_type_info<'ink>(
+    context: &IrValueContext<'ink, '_, '_>,
     ret_type: Option<TypeInfo>,
-) -> Value<*const ir::TypeInfo> {
+) -> Value<'ink, *const ir::TypeInfo<'ink>> {
     ret_type
         .map(|info| {
             TypeTable::get(context.module, &info, context)
@@ -126,15 +128,16 @@ fn gen_signature_return_type_from_type_info(
 
 /// Construct a global that holds a reference to all functions. e.g.:
 /// MunFunctionDefinition[] definitions = { ... }
-fn get_function_definition_array<'a>(
-    db: &dyn IrDatabase,
-    context: &IrValueContext,
+fn get_function_definition_array<'ink, 'a>(
+    db: &dyn HirDatabase,
+    context: &IrValueContext<'ink, '_, '_>,
     functions: impl Iterator<Item = &'a hir::Function>,
-) -> Global<[ir::FunctionDefinition]> {
+    hir_types: &HirTypeCache,
+) -> Global<'ink, [ir::FunctionDefinition<'ink>]> {
     let module = context.module;
     functions
         .map(|f| {
-            let name = f.name(db.upcast()).to_string();
+            let name = f.name(db).to_string();
 
             // Get the function from the cloned module and modify the linkage of the function.
             let value = module
@@ -146,7 +149,7 @@ fn get_function_definition_array<'a>(
             value.set_linkage(Linkage::Private);
 
             // Generate the signature from the function
-            let prototype = gen_prototype_from_function(db, context, *f);
+            let prototype = gen_prototype_from_function(db, context, *f, hir_types);
             ir::FunctionDefinition {
                 prototype,
                 fn_ptr: Value::<*const fn()>::with_cast(
@@ -163,10 +166,10 @@ fn get_function_definition_array<'a>(
 /// ```c
 /// MunDispatchTable dispatchTable = { ... }
 /// ```
-fn gen_dispatch_table(
-    context: &IrValueContext,
-    dispatch_table: &DispatchTable,
-) -> ir::DispatchTable {
+fn gen_dispatch_table<'ink>(
+    context: &IrValueContext<'ink, '_, '_>,
+    dispatch_table: &DispatchTable<'ink>,
+) -> ir::DispatchTable<'ink> {
     let module = context.module;
 
     // Generate an internal array that holds all the function prototypes
@@ -197,17 +200,19 @@ fn gen_dispatch_table(
 /// Constructs IR that exposes the types and symbols in the specified module. A function called
 /// `get_info` is constructed that returns a struct `MunAssemblyInfo`. See the `mun_abi` crate
 /// for the ABI that `get_info` exposes.
-pub(super) fn gen_reflection_ir(
-    db: &dyn IrDatabase,
-    context: &IrValueContext,
+pub(super) fn gen_reflection_ir<'db, 'ink>(
+    db: &'db dyn HirDatabase,
+    context: &IrValueContext<'ink, '_, '_>,
     api: &HashSet<hir::Function>,
-    dispatch_table: &DispatchTable,
-    type_table: &TypeTable,
+    dispatch_table: &DispatchTable<'ink>,
+    type_table: &TypeTable<'ink>,
+    hir_types: &HirTypeCache<'db, 'ink>,
+    optimization_level: inkwell::OptimizationLevel,
 ) {
     let module = context.module;
 
     let num_functions = api.len() as u32;
-    let functions = get_function_definition_array(db, context, api.iter());
+    let functions = get_function_definition_array(db, context, api.iter(), hir_types);
 
     // Get the TypeTable global
     let types = TypeTable::find_global(module)
@@ -230,17 +235,18 @@ pub(super) fn gen_reflection_ir(
     let dispatch_table = gen_dispatch_table(context, dispatch_table);
 
     // Construct the actual `get_info` function
-    gen_get_info_fn(db, context, module_info, dispatch_table);
-    gen_set_allocator_handle_fn(db, context);
-    gen_get_version_fn(db, context);
+    gen_get_info_fn(db, context, module_info, dispatch_table, optimization_level);
+    gen_set_allocator_handle_fn(context);
+    gen_get_version_fn(context);
 }
 
 /// Construct the actual `get_info` function.
-fn gen_get_info_fn(
-    db: &dyn IrDatabase,
-    context: &IrValueContext,
-    module_info: ir::ModuleInfo,
-    dispatch_table: ir::DispatchTable,
+fn gen_get_info_fn<'ink>(
+    db: &dyn HirDatabase,
+    context: &IrValueContext<'ink, '_, '_>,
+    module_info: ir::ModuleInfo<'ink>,
+    dispatch_table: ir::DispatchTable<'ink>,
+    optimization_level: inkwell::OptimizationLevel,
 ) {
     let target = db.target();
     let str_type = context.context.i8_type().ptr_type(AddressSpace::Generic);
@@ -257,9 +263,9 @@ fn gen_get_info_fn(
     // MunModuleInfo get_info() { ... }
     // ```
     let get_symbols_type = if target.options.is_like_windows {
-        Value::<fn(*mut ir::AssemblyInfo)>::get_ir_type(context.type_context)
+        Value::<'ink, fn(*mut ir::AssemblyInfo<'ink>)>::get_ir_type(context.type_context)
     } else {
-        Value::<fn() -> ir::AssemblyInfo>::get_ir_type(context.type_context)
+        Value::<'ink, fn() -> ir::AssemblyInfo<'ink>>::get_ir_type(context.type_context)
     };
 
     let get_symbols_fn =
@@ -276,9 +282,9 @@ fn gen_get_info_fn(
         );
     }
 
-    let builder = db.context().create_builder();
-    let body_ir = db.context().append_basic_block(&get_symbols_fn, "body");
-    builder.position_at_end(&body_ir);
+    let builder = context.context.create_builder();
+    let body_ir = context.context.append_basic_block(get_symbols_fn, "body");
+    builder.position_at_end(body_ir);
 
     // Get a pointer to the IR value that will hold the return value. Again this differs depending
     // on the C ABI.
@@ -321,24 +327,24 @@ fn gen_get_info_fn(
     }
 
     // Run the function optimizer on the generate function
-    function::create_pass_manager(&context.module, db.optimization_lvl()).run_on(&get_symbols_fn);
+    function::create_pass_manager(&context.module, optimization_level).run_on(&get_symbols_fn);
 }
 
 /// Generates a method `void set_allocator_handle(void*)` that stores the argument into the global
 /// `allocatorHandle`. This global is used internally to reference the allocator used by this
 /// munlib.
-fn gen_set_allocator_handle_fn(db: &dyn IrDatabase, context: &IrValueContext) {
+fn gen_set_allocator_handle_fn(context: &IrValueContext) {
     let set_allocator_handle_fn = context.module.add_function(
         "set_allocator_handle",
         Value::<fn(*const u8)>::get_ir_type(context.type_context),
         Some(Linkage::DLLExport),
     );
 
-    let builder = db.context().create_builder();
-    let body_ir = db
-        .context()
-        .append_basic_block(&set_allocator_handle_fn, "body");
-    builder.position_at_end(&body_ir);
+    let builder = context.context.create_builder();
+    let body_ir = context
+        .context
+        .append_basic_block(set_allocator_handle_fn, "body");
+    builder.position_at_end(body_ir);
 
     if let Some(allocator_handle_global) = context.module.get_global("allocatorHandle") {
         builder.build_store(
@@ -352,16 +358,16 @@ fn gen_set_allocator_handle_fn(db: &dyn IrDatabase, context: &IrValueContext) {
 
 /// Generates a `get_version` method that returns the current abi version.
 /// Specifically, it returns the abi version the function was generated in.
-fn gen_get_version_fn(db: &dyn IrDatabase, context: &IrValueContext) {
+fn gen_get_version_fn(context: &IrValueContext) {
     let get_version_fn = context.module.add_function(
         abi::GET_VERSION_FN_NAME,
         Value::<fn() -> u32>::get_ir_type(context.type_context),
         Some(Linkage::DLLExport),
     );
 
-    let builder = db.context().create_builder();
-    let body_ir = db.context().append_basic_block(&get_version_fn, "body");
-    builder.position_at_end(&body_ir);
+    let builder = context.context.create_builder();
+    let body_ir = context.context.append_basic_block(get_version_fn, "body");
+    builder.position_at_end(body_ir);
 
     builder.build_return(Some(&abi::ABI_VERSION.as_value(context).value));
 }
diff --git a/crates/mun_codegen/src/db.rs b/crates/mun_codegen/src/db.rs
index 46f71bd8c..6362f61f2 100644
--- a/crates/mun_codegen/src/db.rs
+++ b/crates/mun_codegen/src/db.rs
@@ -1,71 +1,55 @@
-#![allow(clippy::type_repetition_in_bounds)]
-
-use crate::{
-    assembly::Assembly,
-    ir::{file::FileIR, file_group::FileGroupIR},
-    type_info::TypeInfo,
-    CodeGenParams, Context,
-};
+use crate::Assembly;
 use by_address::ByAddress;
-use inkwell::{
-    targets::TargetData,
-    types::{AnyTypeEnum, StructType},
-    OptimizationLevel,
-};
-use parking_lot::RwLock;
-use std::any::TypeId;
-use std::collections::HashMap;
+use inkwell::targets::{CodeModel, InitializationConfig, RelocMode, Target, TargetTriple};
 use std::sync::Arc;
 
-pub type StructMapping = RwLock<HashMap<(&'static str, TypeId), StructType>>;
-
-/// The `IrDatabase` enables caching of intermediate in the process of LLVM IR generation. It uses
-/// [salsa](https://github.com/salsa-rs/salsa) for this purpose.
-#[salsa::query_group(IrDatabaseStorage)]
-pub trait IrDatabase: hir::HirDatabase + hir::Upcast<dyn hir::HirDatabase> {
-    /// Get the LLVM context that should be used for all generation steps.
+/// The `CodeGenDatabase` enables caching of code generation stages. Inkwell/LLVM objects are not
+/// stored in the cache because they are not thread-safe.
+///
+/// The main purpose of using this Salsa database is to enable caching of high-level objects based
+/// on changes to source files. Although the code generation cache is pretty granular there is still
+/// a benefit to not having to recompile assemblies if not required.
+#[salsa::query_group(CodeGenDatabaseStorage)]
+pub trait CodeGenDatabase: hir::HirDatabase + hir::Upcast<dyn hir::HirDatabase> {
+    /// Set the optimization level used to generate assemblies
     #[salsa::input]
-    fn context(&self) -> Arc<Context>;
-
-    /// Gets the optimization level for generation.
-    #[salsa::input]
-    fn optimization_lvl(&self) -> OptimizationLevel;
-
-    /// Returns the target machine's data layout for code generation.
-    #[salsa::invoke(crate::code_gen::target_data_query)]
-    fn target_data(&self) -> Arc<TargetData>;
+    fn optimization_level(&self) -> inkwell::OptimizationLevel;
 
-    /// Returns a mapping from type to a struct value. This is essentially a cache value which is
-    /// a bit strange to store in the database since its mutable. However, multiple queries need
-    /// to synchronize with this so that is why we store it here nonetheless. Also the mapping
-    /// should be invalidated if the `target_data` changes.
-    #[salsa::invoke(crate::code_gen::type_to_struct_mapping_query)]
-    fn type_to_struct_mapping(&self) -> ByAddress<Arc<StructMapping>>;
-
-    /// Given a type and code generation parameters, return the corresponding IR type.
-    #[salsa::invoke(crate::ir::ty::ir_query)]
-    fn type_ir(&self, ty: hir::Ty, params: CodeGenParams) -> AnyTypeEnum;
-
-    /// Given a struct, return the corresponding IR type.
-    #[salsa::invoke(crate::ir::ty::struct_ty_query)]
-    fn struct_ty(&self, s: hir::Struct) -> StructType;
-
-    /// Given a `hir::FileId` generate code that is shared among the group of files.
-    /// TODO: Currently, a group always consists of a single file. Need to add support for multiple
-    /// files using something like `FileGroupId`.
-    #[salsa::invoke(crate::ir::file_group::ir_query)]
-    fn group_ir(&self, file: hir::FileId) -> Arc<FileGroupIR>;
+    /// Returns the inkwell target machine that completely describes the code generation target. All
+    /// target-specific information should be accessible through this interface.
+    fn target_machine(&self) -> ByAddress<Arc<inkwell::targets::TargetMachine>>;
 
     /// Returns a fully linked shared object for the specified group of files.
     /// TODO: Current, a group always consists of a single file. Need to add support for multiple.
-    #[salsa::invoke(crate::assembly::assembly_query)]
+    #[salsa::invoke(crate::assembly::build_assembly)]
     fn assembly(&self, file: hir::FileId) -> Arc<Assembly>;
+}
 
-    /// Given a `hir::FileId` generate code for the module.
-    #[salsa::invoke(crate::ir::file::ir_query)]
-    fn file_ir(&self, file: hir::FileId) -> Arc<FileIR>;
-
-    /// Given a type, return the runtime `TypeInfo` that can be used to reflect the type.
-    #[salsa::invoke(crate::ir::ty::type_info_query)]
-    fn type_info(&self, ty: hir::Ty) -> TypeInfo;
+/// Constructs the primary interface to the complete machine description for the target machine. All
+/// target-specific information should be accessible through this interface.
+fn target_machine(db: &dyn CodeGenDatabase) -> ByAddress<Arc<inkwell::targets::TargetMachine>> {
+    // Get the HIR target
+    let target = db.target();
+
+    // Initialize the x86 target
+    Target::initialize_x86(&InitializationConfig::default());
+
+    // Retrieve the LLVM target using the specified target.
+    let target_triple = TargetTriple::create(&db.target().llvm_target);
+    let llvm_target = Target::from_triple(&target_triple)
+        .expect("could not find llvm target tripple for Mun target");
+
+    // Construct target machine for machine code generation
+    let target_machine = llvm_target
+        .create_target_machine(
+            &target_triple,
+            &target.options.cpu,
+            &target.options.features,
+            db.optimization_level(),
+            RelocMode::PIC,
+            CodeModel::Default,
+        )
+        .expect("could not create llvm target machine");
+
+    ByAddress(Arc::new(target_machine))
 }
diff --git a/crates/mun_codegen/src/intrinsics.rs b/crates/mun_codegen/src/intrinsics.rs
index c52fb51d7..e44d714d1 100644
--- a/crates/mun_codegen/src/intrinsics.rs
+++ b/crates/mun_codegen/src/intrinsics.rs
@@ -15,7 +15,7 @@ pub trait Intrinsic: Sync {
     fn prototype(&self, context: &Context, target: &TargetData) -> FunctionPrototype;
 
     /// Returns the IR type for the function
-    fn ir_type(&self, context: &Context, target: &TargetData) -> FunctionType;
+    fn ir_type<'ink>(&self, context: &'ink Context, target: &TargetData) -> FunctionType<'ink>;
 }
 
 intrinsics! {
diff --git a/crates/mun_codegen/src/intrinsics/macros.rs b/crates/mun_codegen/src/intrinsics/macros.rs
index 3a1e0a5f2..ea83deac5 100644
--- a/crates/mun_codegen/src/intrinsics/macros.rs
+++ b/crates/mun_codegen/src/intrinsics/macros.rs
@@ -14,7 +14,7 @@ macro_rules! intrinsics{
                         }
                     }
 
-                    fn ir_type(&self, context: &Context, target: &TargetData) -> FunctionType {
+                    fn ir_type<'ink>(&self, context: &'ink Context, target: &TargetData) -> FunctionType<'ink> {
                         let args = vec![$(<$arg as crate::ir::IsBasicIrType>::ir_type(context, target)),*];
                         <$ret as crate::ir::IsFunctionReturnType>::fn_type(context, target, &args, false)
                     }
diff --git a/crates/mun_codegen/src/ir.rs b/crates/mun_codegen/src/ir.rs
index 9af50e5d9..ef255b079 100644
--- a/crates/mun_codegen/src/ir.rs
+++ b/crates/mun_codegen/src/ir.rs
@@ -2,11 +2,10 @@ use crate::type_info::TypeInfo;
 use inkwell::context::Context;
 use inkwell::targets::TargetData;
 use inkwell::types::{
-    AnyType, AnyTypeEnum, BasicType, BasicTypeEnum, FloatType, FunctionType, IntType, PointerType,
+    AnyType, BasicType, BasicTypeEnum, FloatType, FunctionType, IntType, PointerType,
 };
 use inkwell::AddressSpace;
 
-pub mod adt;
 pub mod body;
 #[macro_use]
 pub(crate) mod dispatch_table;
@@ -18,119 +17,106 @@ pub mod ty;
 pub(crate) mod type_table;
 pub mod types;
 
-/// Try to down cast an `AnyTypeEnum` into a `BasicTypeEnum`.
-fn try_convert_any_to_basic(ty: AnyTypeEnum) -> Option<BasicTypeEnum> {
-    match ty {
-        AnyTypeEnum::ArrayType(t) => Some(t.into()),
-        AnyTypeEnum::FloatType(t) => Some(t.into()),
-        AnyTypeEnum::IntType(t) => Some(t.into()),
-        AnyTypeEnum::PointerType(t) => Some(t.into()),
-        AnyTypeEnum::StructType(t) => Some(t.into()),
-        AnyTypeEnum::VectorType(t) => Some(t.into()),
-        _ => None,
-    }
-}
-
 /// Defines that a type has a static representation in inkwell
-pub trait IsIrType {
-    type Type: AnyType;
+pub trait IsIrType<'ink> {
+    type Type: AnyType<'ink>;
 
-    fn ir_type(context: &Context, target: &TargetData) -> Self::Type;
+    fn ir_type(context: &'ink Context, target: &TargetData) -> Self::Type;
 }
 
 /// Defines that a type has a static represention in inkwell that can be described as a BasicType.
-pub trait IsBasicIrType {
-    fn ir_type(context: &Context, target: &TargetData) -> BasicTypeEnum;
+pub trait IsBasicIrType<'ink> {
+    fn ir_type(context: &'ink Context, target: &TargetData) -> BasicTypeEnum<'ink>;
 }
 
-impl<S: BasicType, T: IsIrType<Type = S>> IsBasicIrType for T {
-    fn ir_type(context: &Context, target: &TargetData) -> BasicTypeEnum {
+impl<'ink, S: BasicType<'ink>, T: IsIrType<'ink, Type = S>> IsBasicIrType<'ink> for T {
+    fn ir_type(context: &'ink Context, target: &TargetData) -> BasicTypeEnum<'ink> {
         Self::ir_type(context, target).as_basic_type_enum()
     }
 }
 
 /// Defines that a type can statically be used as a return type for a function
-pub trait IsFunctionReturnType {
+pub trait IsFunctionReturnType<'ink> {
     fn fn_type(
-        context: &Context,
+        context: &'ink Context,
         target: &TargetData,
-        arg_types: &[BasicTypeEnum],
+        arg_types: &[BasicTypeEnum<'ink>],
         is_var_args: bool,
-    ) -> FunctionType;
+    ) -> FunctionType<'ink>;
 }
 
 /// All types that statically have a BasicTypeEnum can also be used as a function return type
-impl<T: IsBasicIrType> IsFunctionReturnType for T {
+impl<'ink, T: IsBasicIrType<'ink>> IsFunctionReturnType<'ink> for T {
     fn fn_type(
-        context: &Context,
+        context: &'ink Context,
         target: &TargetData,
-        arg_types: &[BasicTypeEnum],
+        arg_types: &[BasicTypeEnum<'ink>],
         is_var_args: bool,
-    ) -> FunctionType {
+    ) -> FunctionType<'ink> {
         T::ir_type(context, target).fn_type(arg_types, is_var_args)
     }
 }
 
-impl IsFunctionReturnType for () {
+impl<'ink> IsFunctionReturnType<'ink> for () {
     fn fn_type(
-        context: &Context,
+        context: &'ink Context,
         _target: &TargetData,
-        arg_types: &[BasicTypeEnum],
+        arg_types: &[BasicTypeEnum<'ink>],
         is_var_args: bool,
-    ) -> FunctionType {
+    ) -> FunctionType<'ink> {
         context.void_type().fn_type(arg_types, is_var_args)
     }
 }
 
 /// Defines that a value can be converted to an inkwell type
-pub trait AsIrType {
-    type Type: AnyType;
+pub trait AsIrType<'ink> {
+    type Type: AnyType<'ink>;
 
-    fn as_ir_type(&self, context: &Context, target: &TargetData) -> Self::Type;
+    fn as_ir_type(&self, context: &'ink Context, target: &TargetData) -> Self::Type;
 }
 
-pub trait AsBasicIrType {
-    fn as_ir_type(&self, context: &Context, target: &TargetData) -> BasicTypeEnum;
+pub trait AsBasicIrType<'ink> {
+    fn as_ir_type(&self, context: &'ink Context, target: &TargetData) -> BasicTypeEnum<'ink>;
 }
 
-impl<S: BasicType, T: AsIrType<Type = S>> AsBasicIrType for T {
-    fn as_ir_type(&self, context: &Context, target: &TargetData) -> BasicTypeEnum {
+impl<'ink, S: BasicType<'ink>, T: AsIrType<'ink, Type = S>> AsBasicIrType<'ink> for T {
+    fn as_ir_type(&self, context: &'ink Context, target: &TargetData) -> BasicTypeEnum<'ink> {
         self.as_ir_type(context, target).as_basic_type_enum()
     }
 }
 
 /// Defines that a value can be used to construct a function type.
-pub trait AsFunctionReturnType {
+pub trait AsFunctionReturnType<'ink> {
     fn as_fn_type(
         &self,
-        context: &Context,
+        context: &'ink Context,
         target: &TargetData,
-        arg_types: &[BasicTypeEnum],
+        arg_types: &[BasicTypeEnum<'ink>],
         is_var_args: bool,
-    ) -> FunctionType;
+    ) -> FunctionType<'ink>;
 }
 
-impl<T: AsBasicIrType> AsFunctionReturnType for T {
+impl<'ink, T: AsBasicIrType<'ink>> AsFunctionReturnType<'ink> for T {
     fn as_fn_type(
         &self,
-        context: &Context,
+        context: &'ink Context,
         target: &TargetData,
-        arg_types: &[BasicTypeEnum],
+        arg_types: &[BasicTypeEnum<'ink>],
         is_var_args: bool,
-    ) -> FunctionType {
+    ) -> FunctionType<'ink> {
         self.as_ir_type(context, target)
             .fn_type(arg_types, is_var_args)
     }
 }
 
-impl AsFunctionReturnType for () {
+impl<'ink> AsFunctionReturnType<'ink> for () {
     fn as_fn_type(
         &self,
-        context: &Context,
+        context: &'ink Context,
         _target: &TargetData,
-        arg_types: &[BasicTypeEnum],
+        arg_types: &[BasicTypeEnum<'ink>],
         is_var_args: bool,
-    ) -> FunctionType {
+    ) -> FunctionType<'ink> {
         context.void_type().fn_type(arg_types, is_var_args)
     }
 }
@@ -140,10 +126,10 @@ macro_rules! impl_fundamental_ir_types {
         $ty:ty => $context_fun:ident():$inkwell_ty:ty
     ),+) => {
         $(
-            impl IsIrType for $ty {
+            impl<'ink> IsIrType<'ink> for $ty {
                 type Type = $inkwell_ty;
 
-                fn ir_type(context: &Context, _target: &TargetData) -> Self::Type {
+                fn ir_type(context: &'ink Context, _target: &TargetData) -> Self::Type {
                     context.$context_fun()
                 }
             }
@@ -152,81 +138,89 @@ macro_rules! impl_fundamental_ir_types {
 }
 
 impl_fundamental_ir_types!(
-    i8 => i8_type():IntType,
-    i16 => i16_type():IntType,
-    i32 => i32_type():IntType,
-    i64 => i64_type():IntType,
-    i128 => i128_type():IntType,
-
-    u8 => i8_type():IntType,
-    u16 => i16_type():IntType,
-    u32 => i32_type():IntType,
-    u64 => i64_type():IntType,
-    u128 => i128_type():IntType,
-
-    bool => bool_type():IntType,
-
-    f32 => f32_type():FloatType,
-    f64 => f64_type():FloatType
+    i8 => i8_type():IntType<'ink>,
+    i16 => i16_type():IntType<'ink>,
+    i32 => i32_type():IntType<'ink>,
+    i64 => i64_type():IntType<'ink>,
+    i128 => i128_type():IntType<'ink>,
+
+    u8 => i8_type():IntType<'ink>,
+    u16 => i16_type():IntType<'ink>,
+    u32 => i32_type():IntType<'ink>,
+    u64 => i64_type():IntType<'ink>,
+    u128 => i128_type():IntType<'ink>,
+
+    bool => bool_type():IntType<'ink>,
+
+    f32 => f32_type():FloatType<'ink>,
+    f64 => f64_type():FloatType<'ink>
 );
 
-impl IsIrType for usize {
-    type Type = IntType;
+impl<'ink> IsIrType<'ink> for usize {
+    type Type = IntType<'ink>;
 
-    fn ir_type(_context: &Context, target: &TargetData) -> Self::Type {
-        target.ptr_sized_int_type(None)
+    fn ir_type(context: &'ink Context, target: &TargetData) -> Self::Type {
+        match target.get_pointer_byte_size(None) {
+            4 => context.i32_type(),
+            8 => context.i64_type(),
+            _ => panic!("unsupported pointer byte size"),
+        }
     }
 }
 
-impl IsIrType for isize {
-    type Type = IntType;
+impl<'ink> IsIrType<'ink> for isize {
+    type Type = IntType<'ink>;
 
-    fn ir_type(_context: &Context, target: &TargetData) -> Self::Type {
-        target.ptr_sized_int_type(None)
+    fn ir_type(context: &'ink Context, target: &TargetData) -> Self::Type {
+        match target.get_pointer_byte_size(None) {
+            4 => context.i32_type(),
+            8 => context.i64_type(),
+            _ => panic!("unsupported pointer byte size"),
+        }
     }
 }
 
-pub trait IsPointerType {
-    fn ir_type(context: &Context, target: &TargetData) -> PointerType;
+pub trait IsPointerType<'ink> {
+    fn ir_type(context: &'ink Context, target: &TargetData) -> PointerType<'ink>;
 }
 
-impl<S: BasicType, T: IsIrType<Type = S>> IsPointerType for *const T {
-    fn ir_type(context: &Context, target: &TargetData) -> PointerType {
+impl<'ink, S: BasicType<'ink>, T: IsIrType<'ink, Type = S>> IsPointerType<'ink> for *const T {
+    fn ir_type(context: &'ink Context, target: &TargetData) -> PointerType<'ink> {
         T::ir_type(context, target).ptr_type(AddressSpace::Generic)
     }
 }
 
 // HACK: Manually add `*const TypeInfo`
-impl IsPointerType for *const TypeInfo {
-    fn ir_type(context: &Context, _target: &TargetData) -> PointerType {
+impl<'ink> IsPointerType<'ink> for *const TypeInfo {
+    fn ir_type(context: &'ink Context, _target: &TargetData) -> PointerType<'ink> {
         context.i8_type().ptr_type(AddressSpace::Generic)
     }
 }
 
 // HACK: Manually add `*const c_void`
-impl IsPointerType for *const std::ffi::c_void {
-    fn ir_type(context: &Context, _target: &TargetData) -> PointerType {
+impl<'ink> IsPointerType<'ink> for *const std::ffi::c_void {
+    fn ir_type(context: &'ink Context, _target: &TargetData) -> PointerType<'ink> {
         context.i8_type().ptr_type(AddressSpace::Generic)
     }
 }
 
 // HACK: Manually add `*mut c_void`
-impl IsPointerType for *mut std::ffi::c_void {
-    fn ir_type(context: &Context, _target: &TargetData) -> PointerType {
+impl<'ink> IsPointerType<'ink> for *mut std::ffi::c_void {
+    fn ir_type(context: &'ink Context, _target: &TargetData) -> PointerType<'ink> {
         context.i8_type().ptr_type(AddressSpace::Generic)
     }
 }
 
-impl<S: BasicType, T: IsIrType<Type = S>> IsPointerType for *mut T {
-    fn ir_type(context: &Context, target: &TargetData) -> PointerType {
+impl<'ink, S: BasicType<'ink>, T: IsIrType<'ink, Type = S>> IsPointerType<'ink> for *mut T {
+    fn ir_type(context: &'ink Context, target: &TargetData) -> PointerType<'ink> {
         T::ir_type(context, target).ptr_type(AddressSpace::Generic)
     }
 }
 
-impl<T: IsPointerType> IsIrType for T {
-    type Type = PointerType;
+impl<'ink, T: IsPointerType<'ink>> IsIrType<'ink> for T {
+    type Type = PointerType<'ink>;
 
-    fn ir_type(context: &Context, target: &TargetData) -> Self::Type {
+    fn ir_type(context: &'ink Context, target: &TargetData) -> Self::Type {
         T::ir_type(context, target)
     }
 }
diff --git a/crates/mun_codegen/src/ir/adt.rs b/crates/mun_codegen/src/ir/adt.rs
deleted file mode 100644
index 91a633e80..000000000
--- a/crates/mun_codegen/src/ir/adt.rs
+++ /dev/null
@@ -1,27 +0,0 @@
-//use crate::ir::module::Types;
-use crate::ir::try_convert_any_to_basic;
-use crate::{CodeGenParams, IrDatabase};
-use inkwell::types::{BasicTypeEnum, StructType};
-
-pub(super) fn gen_struct_decl(db: &dyn IrDatabase, s: hir::Struct) -> StructType {
-    let struct_type = db.struct_ty(s);
-    if struct_type.is_opaque() {
-        let field_types: Vec<BasicTypeEnum> = s
-            .fields(db.upcast())
-            .iter()
-            .map(|field| {
-                let field_type = field.ty(db.upcast());
-                try_convert_any_to_basic(db.type_ir(
-                    field_type,
-                    CodeGenParams {
-                        make_marshallable: false,
-                    },
-                ))
-                .expect("could not convert field type")
-            })
-            .collect();
-
-        struct_type.set_body(&field_types, false);
-    }
-    struct_type
-}
diff --git a/crates/mun_codegen/src/ir/body.rs b/crates/mun_codegen/src/ir/body.rs
index 6dbb5925d..e290fe98a 100644
--- a/crates/mun_codegen/src/ir/body.rs
+++ b/crates/mun_codegen/src/ir/body.rs
@@ -1,81 +1,81 @@
-use crate::intrinsics;
 use crate::{
-    ir::{dispatch_table::DispatchTable, try_convert_any_to_basic, type_table::TypeTable},
-    CodeGenParams, IrDatabase,
+    intrinsics,
+    ir::ty::HirTypeCache,
+    ir::types as ir,
+    ir::{dispatch_table::DispatchTable, type_table::TypeTable},
+    value::Global,
 };
 use hir::{
-    ArenaId, ArithOp, BinaryOp, Body, CmpOp, Expr, ExprId, HirDisplay, InferenceResult, Literal,
-    LogicOp, Name, Ordering, Pat, PatId, Path, Resolution, Resolver, Statement, TypeCtor, UnaryOp,
+    ArenaId, ArithOp, BinaryOp, Body, CmpOp, Expr, ExprId, HirDatabase, HirDisplay,
+    InferenceResult, Literal, LogicOp, Name, Ordering, Pat, PatId, Path, Resolution,
+    ResolveBitness, Resolver, Statement, TypeCtor, UnaryOp,
 };
 use inkwell::{
+    basic_block::BasicBlock,
     builder::Builder,
+    context::Context,
+    values::{AggregateValueEnum, GlobalValue, PointerValue},
     values::{BasicValueEnum, CallSiteValue, FloatValue, FunctionValue, IntValue, StructValue},
     AddressSpace, FloatPredicate, IntPredicate,
 };
 use std::{collections::HashMap, sync::Arc};
 
-use crate::ir::types as ir;
-use crate::value::Global;
-use hir::ResolveBitness;
-use inkwell::basic_block::BasicBlock;
-use inkwell::values::{AggregateValueEnum, GlobalValue, PointerValue};
-
-struct LoopInfo {
-    break_values: Vec<(
-        inkwell::values::BasicValueEnum,
-        inkwell::basic_block::BasicBlock,
-    )>,
-    exit_block: BasicBlock,
+struct LoopInfo<'ink> {
+    break_values: Vec<(BasicValueEnum<'ink>, BasicBlock<'ink>)>,
+    exit_block: BasicBlock<'ink>,
 }
 
 #[derive(Clone)]
-pub(crate) struct ExternalGlobals {
-    pub alloc_handle: Option<GlobalValue>,
-    pub dispatch_table: Option<GlobalValue>,
-    pub type_table: Option<Global<[*const ir::TypeInfo]>>,
+pub(crate) struct ExternalGlobals<'ink> {
+    pub alloc_handle: Option<GlobalValue<'ink>>,
+    pub dispatch_table: Option<GlobalValue<'ink>>,
+    pub type_table: Option<Global<'ink, [*const ir::TypeInfo<'ink>]>>,
 }
 
-pub(crate) struct BodyIrGenerator<'a, 'b> {
-    db: &'a dyn IrDatabase,
+pub(crate) struct BodyIrGenerator<'db, 'ink, 't> {
+    context: &'ink Context,
+    db: &'db dyn HirDatabase,
     body: Arc<Body>,
     infer: Arc<InferenceResult>,
-    builder: Builder,
-    fn_value: FunctionValue,
-    pat_to_param: HashMap<PatId, inkwell::values::BasicValueEnum>,
-    pat_to_local: HashMap<PatId, inkwell::values::PointerValue>,
+    builder: Builder<'ink>,
+    fn_value: FunctionValue<'ink>,
+    pat_to_param: HashMap<PatId, inkwell::values::BasicValueEnum<'ink>>,
+    pat_to_local: HashMap<PatId, inkwell::values::PointerValue<'ink>>,
     pat_to_name: HashMap<PatId, String>,
-    function_map: &'a HashMap<hir::Function, FunctionValue>,
-    dispatch_table: &'b DispatchTable,
-    type_table: &'b TypeTable,
-    active_loop: Option<LoopInfo>,
+    function_map: &'t HashMap<hir::Function, FunctionValue<'ink>>,
+    dispatch_table: &'t DispatchTable<'ink>,
+    type_table: &'t TypeTable<'ink>,
+    hir_types: &'t HirTypeCache<'db, 'ink>,
+    active_loop: Option<LoopInfo<'ink>>,
     hir_function: hir::Function,
-    params: CodeGenParams,
-    external_globals: ExternalGlobals,
+    external_globals: ExternalGlobals<'ink>,
 }
 
-impl<'a, 'b> BodyIrGenerator<'a, 'b> {
+impl<'db, 'ink, 't> BodyIrGenerator<'db, 'ink, 't> {
+    #[allow(clippy::too_many_arguments)]
     pub fn new(
-        db: &'a dyn IrDatabase,
-        function: (hir::Function, FunctionValue),
-        function_map: &'a HashMap<hir::Function, FunctionValue>,
-        dispatch_table: &'b DispatchTable,
-        type_table: &'b TypeTable,
-        params: CodeGenParams,
-        external_globals: ExternalGlobals,
+        context: &'ink Context,
+        db: &'db dyn HirDatabase,
+        function: (hir::Function, FunctionValue<'ink>),
+        function_map: &'t HashMap<hir::Function, FunctionValue<'ink>>,
+        dispatch_table: &'t DispatchTable<'ink>,
+        type_table: &'t TypeTable<'ink>,
+        external_globals: ExternalGlobals<'ink>,
+        hir_types: &'t HirTypeCache<'db, 'ink>,
     ) -> Self {
         let (hir_function, ir_function) = function;
 
         // Get the type information from the `hir::Function`
-        let body = hir_function.body(db.upcast());
-        let infer = hir_function.infer(db.upcast());
+        let body = hir_function.body(db);
+        let infer = hir_function.infer(db);
 
         // Construct a builder for the IR function
-        let context = db.context();
         let builder = context.create_builder();
-        let body_ir = context.append_basic_block(&ir_function, "body");
-        builder.position_at_end(&body_ir);
+        let body_ir = context.append_basic_block(ir_function, "body");
+        builder.position_at_end(body_ir);
 
         BodyIrGenerator {
+            context,
             db,
             body,
             infer,
@@ -89,8 +89,8 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
             type_table,
             active_loop: None,
             hir_function,
-            params,
             external_globals,
+            hir_types,
         }
     }
 
@@ -132,8 +132,8 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
         let block_ret_type = &self.infer[self.body.body_expr()];
         let fn_ret_type = self
             .hir_function
-            .ty(self.db.upcast())
-            .callable_sig(self.db.upcast())
+            .ty(self.db)
+            .callable_sig(self.db)
             .unwrap()
             .ret()
             .clone();
@@ -147,11 +147,7 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
     }
 
     pub fn gen_fn_wrapper(&mut self) {
-        let fn_sig = self
-            .hir_function
-            .ty(self.db.upcast())
-            .callable_sig(self.db.upcast())
-            .unwrap();
+        let fn_sig = self.hir_function.ty(self.db).callable_sig(self.db).unwrap();
         let args: Vec<BasicValueEnum> = fn_sig
             .params()
             .iter()
@@ -171,7 +167,7 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
             .collect();
 
         let ret_value = self
-            .gen_call(self.hir_function, &args)
+            .gen_call(self.hir_function, &args, false)
             .try_as_basic_value()
             .left();
 
@@ -179,8 +175,8 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
         if !call_return_type.is_never() {
             let fn_ret_type = self
                 .hir_function
-                .ty(self.db.upcast())
-                .callable_sig(self.db.upcast())
+                .ty(self.db)
+                .callable_sig(self.db)
                 .unwrap()
                 .ret()
                 .clone();
@@ -205,7 +201,7 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
 
     /// Generates IR for the specified expression. Dependending on the type of expression an IR
     /// value is returned.
-    fn gen_expr(&mut self, expr: ExprId) -> Option<inkwell::values::BasicValueEnum> {
+    fn gen_expr(&mut self, expr: ExprId) -> Option<inkwell::values::BasicValueEnum<'ink>> {
         let body = self.body.clone();
         match &body[expr] {
             Expr::Block {
@@ -213,7 +209,7 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
                 tail,
             } => self.gen_block(expr, statements, *tail),
             Expr::Path(ref p) => {
-                let resolver = hir::resolver_for_expr(self.body.clone(), self.db.upcast(), expr);
+                let resolver = hir::resolver_for_expr(self.body.clone(), self.db, expr);
                 Some(self.gen_path_expr(p, expr, &resolver))
             }
             Expr::Literal(lit) => Some(self.gen_literal(lit, expr)),
@@ -235,7 +231,7 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
                             .map(|expr| self.gen_expr(*expr).expect("expected a value"))
                             .collect();
 
-                        self.gen_call(def, &args)
+                        self.gen_call(def, &args, true)
                             .try_as_basic_value()
                             .left()
                             // If the called function is a void function it doesn't return anything.
@@ -247,7 +243,7 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
                             // This unit value will also be optimized out.
                             .or_else(|| match self.infer[expr] {
                                 hir::ty_app!(hir::TypeCtor::Never) => None,
-                                _ => Some(self.db.context().const_struct(&[], false).into()),
+                                _ => Some(self.context.const_struct(&[], false).into()),
                             })
                     }
                     Some(hir::CallableDef::Struct(_)) => Some(self.gen_named_tuple_lit(expr, args)),
@@ -272,7 +268,7 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
     }
 
     /// Generates an IR value that represents the given `Literal`.
-    fn gen_literal(&mut self, lit: &Literal, expr: ExprId) -> BasicValueEnum {
+    fn gen_literal(&mut self, lit: &Literal, expr: ExprId) -> BasicValueEnum<'ink> {
         match lit {
             Literal::Int(v) => {
                 let ty = match &self.infer[expr] {
@@ -285,7 +281,7 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
                     ),
                 };
 
-                let context = self.db.context();
+                let context = self.context;
                 let ir_ty = match ty.resolve(&self.db.target_data_layout()).bitness {
                     hir::IntBitness::X8 => context.i8_type().const_int(v.value as u64, false),
                     hir::IntBitness::X16 => context.i16_type().const_int(v.value as u64, false),
@@ -311,7 +307,7 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
                     _ => unreachable!("cannot construct an IR value for anything but a float type"),
                 };
 
-                let context = self.db.context();
+                let context = self.context;
                 let ir_ty = match ty.bitness.resolve(&self.db.target_data_layout()) {
                     hir::FloatBitness::X32 => context.f32_type().const_float(v.value),
                     hir::FloatBitness::X64 => context.f64_type().const_float(v.value),
@@ -321,7 +317,7 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
             }
 
             Literal::Bool(value) => {
-                let ty = self.db.context().bool_type();
+                let ty = self.context.bool_type();
                 if *value {
                     ty.const_all_ones().into()
                 } else {
@@ -334,18 +330,18 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
     }
 
     /// Constructs an empty struct value e.g. `{}`
-    fn gen_empty(&mut self) -> BasicValueEnum {
-        self.db.context().const_struct(&[], false).into()
+    fn gen_empty(&mut self) -> BasicValueEnum<'ink> {
+        self.context.const_struct(&[], false).into()
     }
 
     /// Allocate a struct literal either on the stack or the heap based on the type of the struct.
     fn gen_struct_alloc(
         &mut self,
         hir_struct: hir::Struct,
-        args: Vec<BasicValueEnum>,
-    ) -> BasicValueEnum {
+        args: Vec<BasicValueEnum<'ink>>,
+    ) -> BasicValueEnum<'ink> {
         // Construct the struct literal
-        let struct_ty = self.db.struct_ty(hir_struct);
+        let struct_ty = self.hir_types.get_struct_type(hir_struct);
         let mut value: AggregateValueEnum = struct_ty.get_undef().into();
         for (i, arg) in args.into_iter().enumerate() {
             value = self
@@ -368,8 +364,8 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
         &mut self,
         hir_struct: hir::Struct,
         struct_lit: StructValue,
-    ) -> BasicValueEnum {
-        let struct_ir_ty = self.db.struct_ty(hir_struct);
+    ) -> BasicValueEnum<'ink> {
+        let struct_ir_ty = self.hir_types.get_struct_type(hir_struct);
         let new_fn_ptr = self.dispatch_table.gen_intrinsic_lookup(
             self.external_globals.dispatch_table,
             &self.builder,
@@ -377,15 +373,16 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
         );
 
         let type_info_ptr = self.type_table.gen_type_info_lookup(
+            self.context,
             &self.builder,
-            &self.db.type_info(hir_struct.ty(self.db.upcast())),
+            &self.hir_types.type_info(&hir_struct.ty(self.db)),
             self.external_globals.type_table,
         );
 
         // HACK: We should be able to use pointers for built-in struct types like `TypeInfo` in intrinsics
         let type_info_ptr = self.builder.build_bitcast(
             type_info_ptr,
-            self.db.context().i8_type().ptr_type(AddressSpace::Generic),
+            self.context.i8_type().ptr_type(AddressSpace::Generic),
             "type_info_ptr_to_i8_ptr",
         );
 
@@ -439,7 +436,7 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
         &mut self,
         type_expr: ExprId,
         fields: &[hir::RecordLitField],
-    ) -> BasicValueEnum {
+    ) -> BasicValueEnum<'ink> {
         let struct_ty = self.infer[type_expr].clone();
         let hir_struct = struct_ty.as_struct().unwrap(); // Can only really get here if the type is a struct
         let fields: Vec<BasicValueEnum> = fields
@@ -451,7 +448,7 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
     }
 
     /// Generates IR for a named tuple literal, e.g. `Foo(1.23, 4)`
-    fn gen_named_tuple_lit(&mut self, type_expr: ExprId, args: &[ExprId]) -> BasicValueEnum {
+    fn gen_named_tuple_lit(&mut self, type_expr: ExprId, args: &[ExprId]) -> BasicValueEnum<'ink> {
         let struct_ty = self.infer[type_expr].clone();
         let hir_struct = struct_ty.as_struct().unwrap(); // Can only really get here if the type is a struct
         let args: Vec<BasicValueEnum> = args
@@ -463,7 +460,7 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
     }
 
     /// Generates IR for a unit struct literal, e.g `Foo`
-    fn gen_unit_struct_lit(&mut self, type_expr: ExprId) -> BasicValueEnum {
+    fn gen_unit_struct_lit(&mut self, type_expr: ExprId) -> BasicValueEnum<'ink> {
         let struct_ty = self.infer[type_expr].clone();
         let hir_struct = struct_ty.as_struct().unwrap(); // Can only really get here if the type is a struct
         self.gen_struct_alloc(hir_struct, Vec::new())
@@ -475,7 +472,7 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
         _tgt_expr: ExprId,
         statements: &[Statement],
         tail: Option<ExprId>,
-    ) -> Option<BasicValueEnum> {
+    ) -> Option<BasicValueEnum<'ink>> {
         for statement in statements.iter() {
             match statement {
                 Statement::Let {
@@ -499,8 +496,8 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
 
     /// Constructs a builder that should be used to emit an `alloca` instruction. These instructions
     /// should be at the start of the IR.
-    fn new_alloca_builder(&self) -> Builder {
-        let temp_builder = Builder::create();
+    fn new_alloca_builder(&self) -> Builder<'ink> {
+        let temp_builder = self.context.create_builder();
         let block = self
             .fn_value
             .get_first_basic_block()
@@ -508,7 +505,7 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
         if let Some(first_instruction) = block.get_first_instruction() {
             temp_builder.position_before(&first_instruction);
         } else {
-            temp_builder.position_at_end(&block);
+            temp_builder.position_at_end(block);
         }
         temp_builder
     }
@@ -521,13 +518,10 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
             Pat::Bind { name } => {
                 let builder = self.new_alloca_builder();
                 let pat_ty = self.infer[pat].clone();
-                let ty = try_convert_any_to_basic(self.db.type_ir(
-                    pat_ty.clone(),
-                    CodeGenParams {
-                        make_marshallable: false,
-                    },
-                ))
-                .expect("expected basic type");
+                let ty = self
+                    .hir_types
+                    .get_basic_type(&pat_ty)
+                    .expect("expected basic type");
                 let ptr = builder.build_alloca(ty, &name.to_string());
                 self.pat_to_local.insert(pat, ptr);
                 self.pat_to_name.insert(pat, name.to_string());
@@ -548,9 +542,9 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
         path: &Path,
         expr: ExprId,
         resolver: &Resolver,
-    ) -> inkwell::values::BasicValueEnum {
+    ) -> inkwell::values::BasicValueEnum<'ink> {
         let resolution = resolver
-            .resolve_path_without_assoc_items(self.db.upcast(), path)
+            .resolve_path_without_assoc_items(self.db, path)
             .take_values()
             .expect("unknown path");
 
@@ -572,7 +566,11 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
 
     /// Given an expression and its value optionally dereference the value to get to the actual
     /// value. This is useful if we need to do an indirection to get to the actual value.
-    fn opt_deref_value(&mut self, expr: ExprId, value: BasicValueEnum) -> BasicValueEnum {
+    fn opt_deref_value(
+        &mut self,
+        expr: ExprId,
+        value: BasicValueEnum<'ink>,
+    ) -> BasicValueEnum<'ink> {
         let ty = &self.infer[expr];
         if let hir::Ty::Apply(hir::ApplicationTy {
             ctor: hir::TypeCtor::Struct(s),
@@ -592,9 +590,9 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
         path: &Path,
         _expr: ExprId,
         resolver: &Resolver,
-    ) -> inkwell::values::PointerValue {
+    ) -> inkwell::values::PointerValue<'ink> {
         let resolution = resolver
-            .resolve_path_without_assoc_items(self.db.upcast(), path)
+            .resolve_path_without_assoc_items(self.db, path)
             .take_values()
             .expect("unknown path");
 
@@ -614,7 +612,7 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
         lhs: ExprId,
         rhs: ExprId,
         op: BinaryOp,
-    ) -> Option<BasicValueEnum> {
+    ) -> Option<BasicValueEnum<'ink>> {
         let lhs_type = self.infer[lhs].clone();
         match lhs_type.as_simple() {
             Some(TypeCtor::Bool) => self.gen_binary_op_bool(lhs, rhs, op),
@@ -631,29 +629,26 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
                 let rhs_type = self.infer[rhs].clone();
                 unimplemented!(
                     "unimplemented operation {0}op{1}",
-                    lhs_type.display(self.db.upcast()),
-                    rhs_type.display(self.db.upcast())
+                    lhs_type.display(self.db),
+                    rhs_type.display(self.db)
                 )
             }
         }
     }
 
     /// Generates IR to calculate a unary operation on an expression.
-    fn gen_unary_op(&mut self, expr: ExprId, op: UnaryOp) -> Option<BasicValueEnum> {
+    fn gen_unary_op(&mut self, expr: ExprId, op: UnaryOp) -> Option<BasicValueEnum<'ink>> {
         let ty = self.infer[expr].clone();
         match ty.as_simple() {
             Some(TypeCtor::Float(_ty)) => self.gen_unary_op_float(expr, op),
             Some(TypeCtor::Int(ty)) => self.gen_unary_op_int(expr, op, ty.signedness),
             Some(TypeCtor::Bool) => self.gen_unary_op_bool(expr, op),
-            _ => unimplemented!(
-                "unimplemented operation op{0}",
-                ty.display(self.db.upcast())
-            ),
+            _ => unimplemented!("unimplemented operation op{0}", ty.display(self.db)),
         }
     }
 
     /// Generates IR to calculate a unary operation on a floating point value.
-    fn gen_unary_op_float(&mut self, expr: ExprId, op: UnaryOp) -> Option<BasicValueEnum> {
+    fn gen_unary_op_float(&mut self, expr: ExprId, op: UnaryOp) -> Option<BasicValueEnum<'ink>> {
         let value: FloatValue = self
             .gen_expr(expr)
             .map(|value| self.opt_deref_value(expr, value))
@@ -671,7 +666,7 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
         expr: ExprId,
         op: UnaryOp,
         signedness: hir::Signedness,
-    ) -> Option<BasicValueEnum> {
+    ) -> Option<BasicValueEnum<'ink>> {
         let value: IntValue = self
             .gen_expr(expr)
             .map(|value| self.opt_deref_value(expr, value))
@@ -691,7 +686,7 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
     }
 
     /// Generates IR to calculate a unary operation on a boolean value.
-    fn gen_unary_op_bool(&mut self, expr: ExprId, op: UnaryOp) -> Option<BasicValueEnum> {
+    fn gen_unary_op_bool(&mut self, expr: ExprId, op: UnaryOp) -> Option<BasicValueEnum<'ink>> {
         let value: IntValue = self
             .gen_expr(expr)
             .map(|value| self.opt_deref_value(expr, value))
@@ -709,7 +704,7 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
         lhs_expr: ExprId,
         rhs_expr: ExprId,
         op: BinaryOp,
-    ) -> Option<BasicValueEnum> {
+    ) -> Option<BasicValueEnum<'ink>> {
         let lhs: IntValue = self
             .gen_expr(lhs_expr)
             .map(|value| self.opt_deref_value(lhs_expr, value))
@@ -745,7 +740,7 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
         lhs_expr: ExprId,
         rhs_expr: ExprId,
         op: BinaryOp,
-    ) -> Option<BasicValueEnum> {
+    ) -> Option<BasicValueEnum<'ink>> {
         let lhs = self
             .gen_expr(lhs_expr)
             .map(|value| self.opt_deref_value(lhs_expr, value))
@@ -805,7 +800,7 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
         rhs_expr: ExprId,
         op: BinaryOp,
         signedness: hir::Signedness,
-    ) -> Option<BasicValueEnum> {
+    ) -> Option<BasicValueEnum<'ink>> {
         let lhs = self
             .gen_expr(lhs_expr)
             .map(|value| self.opt_deref_value(lhs_expr, value))
@@ -841,7 +836,7 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
         lhs_expr: ExprId,
         rhs_expr: ExprId,
         op: BinaryOp,
-    ) -> Option<BasicValueEnum> {
+    ) -> Option<BasicValueEnum<'ink>> {
         let rhs = self
             .gen_expr(rhs_expr)
             .expect("no rhs value")
@@ -870,7 +865,7 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
         lhs_expr: ExprId,
         rhs_expr: ExprId,
         op: BinaryOp,
-    ) -> Option<BasicValueEnum> {
+    ) -> Option<BasicValueEnum<'ink>> {
         let rhs = self
             .gen_expr(rhs_expr)
             .expect("no rhs value")
@@ -892,7 +887,12 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
         }
     }
 
-    fn gen_arith_bin_op_bool(&mut self, lhs: IntValue, rhs: IntValue, op: ArithOp) -> IntValue {
+    fn gen_arith_bin_op_bool(
+        &mut self,
+        lhs: IntValue<'ink>,
+        rhs: IntValue<'ink>,
+        op: ArithOp,
+    ) -> IntValue<'ink> {
         match op {
             ArithOp::BitAnd => self.builder.build_and(lhs, rhs, "bit_and"),
             ArithOp::BitOr => self.builder.build_or(lhs, rhs, "bit_or"),
@@ -906,11 +906,11 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
 
     fn gen_cmp_bin_op_int(
         &mut self,
-        lhs: IntValue,
-        rhs: IntValue,
+        lhs: IntValue<'ink>,
+        rhs: IntValue<'ink>,
         op: CmpOp,
         signedness: hir::Signedness,
-    ) -> IntValue {
+    ) -> IntValue<'ink> {
         let (name, predicate) = match op {
             CmpOp::Eq { negated: false } => ("eq", IntPredicate::EQ),
             CmpOp::Eq { negated: true } => ("neq", IntPredicate::NE),
@@ -961,11 +961,11 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
 
     fn gen_arith_bin_op_int(
         &mut self,
-        lhs: IntValue,
-        rhs: IntValue,
+        lhs: IntValue<'ink>,
+        rhs: IntValue<'ink>,
         op: ArithOp,
         signedness: hir::Signedness,
-    ) -> IntValue {
+    ) -> IntValue<'ink> {
         match op {
             ArithOp::Add => self.builder.build_int_add(lhs, rhs, "add"),
             ArithOp::Subtract => self.builder.build_int_sub(lhs, rhs, "sub"),
@@ -991,10 +991,10 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
 
     fn gen_arith_bin_op_float(
         &mut self,
-        lhs: FloatValue,
-        rhs: FloatValue,
+        lhs: FloatValue<'ink>,
+        rhs: FloatValue<'ink>,
         op: ArithOp,
-    ) -> FloatValue {
+    ) -> FloatValue<'ink> {
         match op {
             ArithOp::Add => self.builder.build_float_add(lhs, rhs, "add"),
             ArithOp::Subtract => self.builder.build_float_sub(lhs, rhs, "sub"),
@@ -1011,7 +1011,12 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
         }
     }
 
-    fn gen_logic_bin_op(&mut self, lhs: IntValue, rhs: IntValue, op: LogicOp) -> IntValue {
+    fn gen_logic_bin_op(
+        &mut self,
+        lhs: IntValue<'ink>,
+        rhs: IntValue<'ink>,
+        op: LogicOp,
+    ) -> IntValue<'ink> {
         match op {
             LogicOp::And => self.builder.build_and(lhs, rhs, "and"),
             LogicOp::Or => self.builder.build_or(lhs, rhs, "or"),
@@ -1020,11 +1025,11 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
 
     /// Given an expression generate code that results in a memory address that can be used for
     /// other place operations.
-    fn gen_place_expr(&mut self, expr: ExprId) -> PointerValue {
+    fn gen_place_expr(&mut self, expr: ExprId) -> PointerValue<'ink> {
         let body = self.body.clone();
         match &body[expr] {
             Expr::Path(ref p) => {
-                let resolver = hir::resolver_for_expr(self.body.clone(), self.db.upcast(), expr);
+                let resolver = hir::resolver_for_expr(self.body.clone(), self.db, expr);
                 self.gen_path_place_expr(p, expr, &resolver)
             }
             Expr::Field {
@@ -1046,37 +1051,42 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
         }
     }
 
-    fn should_use_dispatch_table(&self) -> bool {
-        // FIXME: When we use the dispatch table, generated wrappers have infinite recursion
-        !self.params.make_marshallable
+    /// Returns true if a call to the specified function should be looked up in the dispatch table;
+    /// if false is returned the function should be called directly.
+    fn should_use_dispatch_table(&self, _function: hir::Function) -> bool {
+        // TODO: add logic to determine when to use the dispatch table (dont use it in release for
+        //  example)
+        true
     }
 
     /// Generates IR for a function call.
-    fn gen_call(&mut self, function: hir::Function, args: &[BasicValueEnum]) -> CallSiteValue {
-        if self.dispatch_table.contains(function) && self.should_use_dispatch_table() {
+    fn gen_call(
+        &mut self,
+        function: hir::Function,
+        args: &[BasicValueEnum<'ink>],
+        allow_dispatch_table: bool,
+    ) -> CallSiteValue<'ink> {
+        if self.dispatch_table.contains(function)
+            && allow_dispatch_table
+            && self.should_use_dispatch_table(function)
+        {
             let ptr_value = self.dispatch_table.gen_function_lookup(
                 self.db,
                 self.external_globals.dispatch_table,
                 &self.builder,
                 function,
             );
-            self.builder.build_call(
-                ptr_value,
-                &args,
-                &function.name(self.db.upcast()).to_string(),
-            )
+            self.builder
+                .build_call(ptr_value, &args, &function.name(self.db).to_string())
         } else {
             let llvm_function = self.function_map.get(&function).unwrap_or_else(|| {
                 panic!(
                     "missing function value for hir function: '{}'",
-                    function.name(self.db.upcast()),
+                    function.name(self.db),
                 )
             });
-            self.builder.build_call(
-                *llvm_function,
-                &args,
-                &function.name(self.db.upcast()).to_string(),
-            )
+            self.builder
+                .build_call(*llvm_function, &args, &function.name(self.db).to_string())
         }
     }
 
@@ -1087,7 +1097,7 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
         condition: ExprId,
         then_branch: ExprId,
         else_branch: Option<ExprId>,
-    ) -> Option<inkwell::values::BasicValueEnum> {
+    ) -> Option<inkwell::values::BasicValueEnum<'ink>> {
         // Generate IR for the condition
         let condition_ir = self
             .gen_expr(condition)
@@ -1095,42 +1105,38 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
             .into_int_value();
 
         // Generate the code blocks to branch to
-        let context = self.db.context();
-        let mut then_block = context.append_basic_block(&self.fn_value, "then");
+        let mut then_block = self.context.append_basic_block(self.fn_value, "then");
         let else_block_and_expr = match &else_branch {
             Some(else_branch) => Some((
-                context.append_basic_block(&self.fn_value, "else"),
+                self.context.append_basic_block(self.fn_value, "else"),
                 else_branch,
             )),
             None => None,
         };
-        let merge_block = context.append_basic_block(&self.fn_value, "if_merge");
+        let merge_block = self.context.append_basic_block(self.fn_value, "if_merge");
 
         // Build the actual branching IR for the if statement
-        let else_block = else_block_and_expr
-            .as_ref()
-            .map(|e| &e.0)
-            .unwrap_or(&merge_block);
+        let else_block = else_block_and_expr.map(|e| e.0).unwrap_or(merge_block);
         self.builder
-            .build_conditional_branch(condition_ir, &then_block, else_block);
+            .build_conditional_branch(condition_ir, then_block, else_block);
 
         // Fill the then block
-        self.builder.position_at_end(&then_block);
+        self.builder.position_at_end(then_block);
         let then_block_ir = self.gen_expr(then_branch);
         if !self.infer[then_branch].is_never() {
-            self.builder.build_unconditional_branch(&merge_block);
+            self.builder.build_unconditional_branch(merge_block);
         }
         then_block = self.builder.get_insert_block().unwrap();
 
         // Fill the else block, if it exists and get the result back
         let else_ir_and_block = if let Some((else_block, else_branch)) = else_block_and_expr {
             else_block
-                .move_after(&then_block)
+                .move_after(then_block)
                 .expect("programmer error, then_block is invalid");
-            self.builder.position_at_end(&else_block);
+            self.builder.position_at_end(else_block);
             let result_ir = self.gen_expr(*else_branch);
             if !self.infer[*else_branch].is_never() {
-                self.builder.build_unconditional_branch(&merge_block);
+                self.builder.build_unconditional_branch(merge_block);
             }
             Some((result_ir, self.builder.get_insert_block().unwrap()))
         } else {
@@ -1139,14 +1145,14 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
 
         // Create merge block
         let current_block = self.builder.get_insert_block().unwrap();
-        merge_block.move_after(&current_block).unwrap();
-        self.builder.position_at_end(&merge_block);
+        merge_block.move_after(current_block).unwrap();
+        self.builder.position_at_end(merge_block);
 
         // Construct phi block if a value was returned
         if let Some(then_block_ir) = then_block_ir {
             if let Some((Some(else_block_ir), else_block)) = else_ir_and_block {
                 let phi = self.builder.build_phi(then_block_ir.get_type(), "iftmp");
-                phi.add_incoming(&[(&then_block_ir, &then_block), (&else_block_ir, &else_block)]);
+                phi.add_incoming(&[(&then_block_ir, then_block), (&else_block_ir, else_block)]);
                 Some(phi.as_basic_value())
             } else {
                 Some(then_block_ir)
@@ -1158,7 +1164,11 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
         }
     }
 
-    fn gen_return(&mut self, _expr: ExprId, ret_expr: Option<ExprId>) -> Option<BasicValueEnum> {
+    fn gen_return(
+        &mut self,
+        _expr: ExprId,
+        ret_expr: Option<ExprId>,
+    ) -> Option<BasicValueEnum<'ink>> {
         let ret_value = ret_expr.and_then(|expr| self.gen_expr(expr));
 
         // Construct a return statement from the returned value of the body
@@ -1171,7 +1181,11 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
         None
     }
 
-    fn gen_break(&mut self, _expr: ExprId, break_expr: Option<ExprId>) -> Option<BasicValueEnum> {
+    fn gen_break(
+        &mut self,
+        _expr: ExprId,
+        break_expr: Option<ExprId>,
+    ) -> Option<BasicValueEnum<'ink>> {
         let break_value = break_expr.and_then(|expr| self.gen_expr(expr));
         let loop_info = self.active_loop.as_mut().unwrap();
         if let Some(break_value) = break_value {
@@ -1180,18 +1194,18 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
                 .push((break_value, self.builder.get_insert_block().unwrap()));
         }
         self.builder
-            .build_unconditional_branch(&loop_info.exit_block);
+            .build_unconditional_branch(loop_info.exit_block);
         None
     }
 
     fn gen_loop_block_expr(
         &mut self,
         block: ExprId,
-        exit_block: BasicBlock,
+        exit_block: BasicBlock<'ink>,
     ) -> (
-        BasicBlock,
-        Vec<(BasicValueEnum, BasicBlock)>,
-        Option<BasicValueEnum>,
+        BasicBlock<'ink>,
+        Vec<(BasicValueEnum<'ink>, BasicBlock<'ink>)>,
+        Option<BasicValueEnum<'ink>>,
     ) {
         // Build a new loop info struct
         let loop_info = LoopInfo {
@@ -1218,25 +1232,25 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
         _expr: ExprId,
         condition_expr: ExprId,
         body_expr: ExprId,
-    ) -> Option<BasicValueEnum> {
-        let context = self.db.context();
-        let cond_block = context.append_basic_block(&self.fn_value, "whilecond");
-        let loop_block = context.append_basic_block(&self.fn_value, "while");
-        let exit_block = context.append_basic_block(&self.fn_value, "afterwhile");
+    ) -> Option<BasicValueEnum<'ink>> {
+        let context = self.context;
+        let cond_block = context.append_basic_block(self.fn_value, "whilecond");
+        let loop_block = context.append_basic_block(self.fn_value, "while");
+        let exit_block = context.append_basic_block(self.fn_value, "afterwhile");
 
         // Insert an explicit fall through from the current block to the condition check
-        self.builder.build_unconditional_branch(&cond_block);
+        self.builder.build_unconditional_branch(cond_block);
 
         // Generate condition block
-        self.builder.position_at_end(&cond_block);
+        self.builder.position_at_end(cond_block);
         let condition_ir = self
             .gen_expr(condition_expr)
             .map(|value| self.opt_deref_value(condition_expr, value));
         if let Some(condition_ir) = condition_ir {
             self.builder.build_conditional_branch(
                 condition_ir.into_int_value(),
-                &loop_block,
-                &exit_block,
+                loop_block,
+                exit_block,
             );
         } else {
             // If the condition doesn't return a value, we also immediately return without a value.
@@ -1245,41 +1259,41 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
         }
 
         // Generate loop block
-        self.builder.position_at_end(&loop_block);
+        self.builder.position_at_end(loop_block);
         let (exit_block, _, value) = self.gen_loop_block_expr(body_expr, exit_block);
         if value.is_some() {
-            self.builder.build_unconditional_branch(&cond_block);
+            self.builder.build_unconditional_branch(cond_block);
         }
 
         // Generate exit block
-        self.builder.position_at_end(&exit_block);
+        self.builder.position_at_end(exit_block);
 
         Some(self.gen_empty())
     }
 
-    fn gen_loop(&mut self, _expr: ExprId, body_expr: ExprId) -> Option<BasicValueEnum> {
-        let context = self.db.context();
-        let loop_block = context.append_basic_block(&self.fn_value, "loop");
-        let exit_block = context.append_basic_block(&self.fn_value, "exit");
+    fn gen_loop(&mut self, _expr: ExprId, body_expr: ExprId) -> Option<BasicValueEnum<'ink>> {
+        let context = self.context;
+        let loop_block = context.append_basic_block(self.fn_value, "loop");
+        let exit_block = context.append_basic_block(self.fn_value, "exit");
 
         // Insert an explicit fall through from the current block to the loop
-        self.builder.build_unconditional_branch(&loop_block);
+        self.builder.build_unconditional_branch(loop_block);
 
         // Generate the body of the loop
-        self.builder.position_at_end(&loop_block);
+        self.builder.position_at_end(loop_block);
         let (exit_block, break_values, value) = self.gen_loop_block_expr(body_expr, exit_block);
         if value.is_some() {
-            self.builder.build_unconditional_branch(&loop_block);
+            self.builder.build_unconditional_branch(loop_block);
         }
 
         // Move the builder to the exit block
-        self.builder.position_at_end(&exit_block);
+        self.builder.position_at_end(exit_block);
 
         if !break_values.is_empty() {
             let (value, _) = break_values.first().unwrap();
             let phi = self.builder.build_phi(value.get_type(), "exit");
-            for (ref value, ref block) in break_values {
-                phi.add_incoming(&[(value, block)])
+            for (value, block) in break_values {
+                phi.add_incoming(&[(&value, block)])
             }
             Some(phi.as_basic_value())
         } else {
@@ -1292,7 +1306,7 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
         _expr: ExprId,
         receiver_expr: ExprId,
         name: &Name,
-    ) -> Option<BasicValueEnum> {
+    ) -> Option<BasicValueEnum<'ink>> {
         let hir_struct = self.infer[receiver_expr]
             .as_struct()
             .expect("expected a struct");
@@ -1300,7 +1314,7 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
         let hir_struct_name = hir_struct.name(self.db.upcast());
 
         let field_idx = hir_struct
-            .field(self.db.upcast(), name)
+            .field(self.db, name)
             .expect("expected a struct field")
             .id()
             .into_raw()
@@ -1312,14 +1326,14 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
             let receiver_ptr = self
                 .opt_deref_value(receiver_expr, receiver_ptr.into())
                 .into_pointer_value();
-            unsafe {
-                let field_ptr = self.builder.build_struct_gep(
+            let field_ptr = unsafe {
+                self.builder.build_struct_gep(
                     receiver_ptr,
                     field_idx,
                     &format!("{}.{}_ptr", hir_struct_name, name),
-                );
-                Some(self.builder.build_load(field_ptr, &field_ir_name))
-            }
+                )
+            };
+            Some(self.builder.build_load(field_ptr, &field_ir_name))
         } else {
             let receiver_value = self.gen_expr(receiver_expr)?;
             let receiver_value = self.opt_deref_value(receiver_expr, receiver_value);
@@ -1343,7 +1357,7 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
         _expr: ExprId,
         receiver_expr: ExprId,
         name: &Name,
-    ) -> PointerValue {
+    ) -> PointerValue<'ink> {
         let hir_struct = self.infer[receiver_expr]
             .as_struct()
             .expect("expected a struct");
@@ -1351,7 +1365,7 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
         let hir_struct_name = hir_struct.name(self.db.upcast());
 
         let field_idx = hir_struct
-            .field(self.db.upcast(), name)
+            .field(self.db, name)
             .expect("expected a struct field")
             .id()
             .into_raw()
@@ -1373,7 +1387,10 @@ impl<'a, 'b> BodyIrGenerator<'a, 'b> {
 
 /// Derefs a heap-allocated value. As we introduce a layer of indirection for hot
 /// reloading, we need to first load the pointer that points to the memory block.
-fn deref_heap_value(builder: &Builder, value: BasicValueEnum) -> BasicValueEnum {
+fn deref_heap_value<'ink>(
+    builder: &Builder<'ink>,
+    value: BasicValueEnum<'ink>,
+) -> BasicValueEnum<'ink> {
     let mem_ptr = builder
         .build_load(value.into_pointer_value(), "mem_ptr")
         .into_pointer_value();
diff --git a/crates/mun_codegen/src/ir/dispatch_table.rs b/crates/mun_codegen/src/ir/dispatch_table.rs
index 2052ddb89..521f7195d 100644
--- a/crates/mun_codegen/src/ir/dispatch_table.rs
+++ b/crates/mun_codegen/src/ir/dispatch_table.rs
@@ -1,8 +1,5 @@
-use crate::intrinsics::Intrinsic;
-use crate::ir::function;
-use crate::type_info::TypeInfo;
-use crate::{CodeGenParams, IrDatabase};
-use hir::{Body, Expr, ExprId, InferenceResult};
+use crate::{intrinsics::Intrinsic, ir::function, ir::ty::HirTypeCache, type_info::TypeInfo};
+use hir::{Body, Expr, ExprId, HirDatabase, InferenceResult};
 use inkwell::{
     context::Context,
     module::Module,
@@ -10,8 +7,10 @@ use inkwell::{
     types::{BasicTypeEnum, FunctionType},
     values::{BasicValueEnum, PointerValue},
 };
-use std::collections::{BTreeMap, HashMap};
-use std::sync::Arc;
+use std::{
+    collections::{BTreeMap, HashMap},
+    sync::Arc,
+};
 
 /// A dispatch table in IR is a struct that contains pointers to all functions that are called from
 /// code. In C terms it looks something like this:
@@ -25,12 +24,12 @@ use std::sync::Arc;
 /// The dispatch table is used to add a patchable indirection when calling a function from IR. The
 /// DispatchTable is exposed to the Runtime which fills the structure with valid pointers to
 /// functions. This basically enables all hot reloading within Mun.
-#[derive(Debug, Clone, Eq, PartialEq)]
-pub struct DispatchTable {
+#[derive(Debug, Eq, PartialEq)]
+pub struct DispatchTable<'ink> {
     // The LLVM context in which all LLVM types live
-    context: Arc<Context>,
+    context: &'ink Context,
     // The target for which to create the dispatch table
-    target: Arc<TargetData>,
+    target: TargetData,
     // This contains the function that map to the DispatchTable struct fields
     function_to_idx: HashMap<hir::Function, usize>,
     // Prototype to function index
@@ -38,9 +37,9 @@ pub struct DispatchTable {
     // This contains an ordered list of all the function in the dispatch table
     entries: Vec<DispatchableFunction>,
     // Contains a reference to the global value containing the DispatchTable
-    table_ref: Option<inkwell::values::GlobalValue>,
+    table_ref: Option<inkwell::values::GlobalValue<'ink>>,
     //
-    table_type: Option<inkwell::types::StructType>,
+    table_type: Option<inkwell::types::StructType<'ink>>,
 }
 
 /// A `FunctionPrototype` defines a unique signature that can be added to the dispatch table.
@@ -59,7 +58,7 @@ pub struct DispatchableFunction {
     pub hir: Option<hir::Function>,
 }
 
-impl DispatchTable {
+impl<'ink> DispatchTable<'ink> {
     /// Returns whether the `DispatchTable` contains the specified `function`.
     pub fn contains(&self, function: hir::Function) -> bool {
         self.function_to_idx.contains_key(&function)
@@ -75,12 +74,12 @@ impl DispatchTable {
     /// struct
     pub fn gen_function_lookup(
         &self,
-        db: &dyn IrDatabase,
-        table_ref: Option<inkwell::values::GlobalValue>,
-        builder: &inkwell::builder::Builder,
+        db: &dyn HirDatabase,
+        table_ref: Option<inkwell::values::GlobalValue<'ink>>,
+        builder: &inkwell::builder::Builder<'ink>,
         function: hir::Function,
-    ) -> PointerValue {
-        let function_name = function.name(db.upcast()).to_string();
+    ) -> PointerValue<'ink> {
+        let function_name = function.name(db).to_string();
 
         // Get the index of the function
         let index = *self
@@ -96,11 +95,11 @@ impl DispatchTable {
     /// struct
     pub fn gen_intrinsic_lookup(
         &self,
-        table_ref: Option<inkwell::values::GlobalValue>,
-        builder: &inkwell::builder::Builder,
+        table_ref: Option<inkwell::values::GlobalValue<'ink>>,
+        builder: &inkwell::builder::Builder<'ink>,
         intrinsic: &impl Intrinsic,
-    ) -> PointerValue {
-        let prototype = intrinsic.prototype(self.context.as_ref(), self.target.as_ref());
+    ) -> PointerValue<'ink> {
+        let prototype = intrinsic.prototype(self.context, &self.target);
 
         // Get the index of the intrinsic
         let index = *self
@@ -115,11 +114,11 @@ impl DispatchTable {
     /// lines of: `dispatchTable[i]`, where i is the index and `dispatchTable` is a struct
     fn gen_function_lookup_by_index(
         &self,
-        table_ref: Option<inkwell::values::GlobalValue>,
-        builder: &inkwell::builder::Builder,
+        table_ref: Option<inkwell::values::GlobalValue<'ink>>,
+        builder: &inkwell::builder::Builder<'ink>,
         function_name: &str,
         index: usize,
-    ) -> PointerValue {
+    ) -> PointerValue<'ink> {
         // Get the internal table reference
         let table_ref = table_ref.expect("no dispatch table defined");
 
@@ -131,6 +130,7 @@ impl DispatchTable {
                 &format!("{0}_ptr_ptr", function_name),
             )
         };
+
         builder
             .build_load(ptr_to_function_ptr, &format!("{0}_ptr", function_name))
             .into_pointer_value()
@@ -138,59 +138,65 @@ impl DispatchTable {
 
     /// Returns the value that represents the dispatch table in IR or `None` if no table was
     /// generated.
-    pub fn global_value(&self) -> Option<&inkwell::values::GlobalValue> {
+    pub fn global_value(&self) -> Option<&inkwell::values::GlobalValue<'ink>> {
         self.table_ref.as_ref()
     }
 
     /// Returns the IR type of the dispatch table's global value, if it exists.
-    pub fn ty(&self) -> Option<inkwell::types::StructType> {
+    pub fn ty(&self) -> Option<inkwell::types::StructType<'ink>> {
         self.table_type
     }
 }
 
 /// A struct that can be used to build the dispatch table from HIR.
-pub(crate) struct DispatchTableBuilder<'a> {
-    db: &'a dyn IrDatabase,
+pub(crate) struct DispatchTableBuilder<'db, 'ink, 't> {
+    db: &'db dyn HirDatabase,
     // The LLVM context in which all LLVM types live
-    context: Arc<Context>,
+    context: &'ink Context,
     // The module in which all values live
-    module: &'a Module,
+    module: &'t Module<'ink>,
     // The target for which to create the dispatch table
-    target: Arc<TargetData>,
+    target_data: TargetData,
+    // Converts HIR ty's to inkwell types
+    hir_types: &'t HirTypeCache<'db, 'ink>,
     // This contains the functions that map to the DispatchTable struct fields
     function_to_idx: HashMap<hir::Function, usize>,
     // Prototype to function index
     prototype_to_idx: HashMap<FunctionPrototype, usize>,
     // These are *all* called functions in the modules
-    entries: Vec<TypedDispatchableFunction>,
+    entries: Vec<TypedDispatchableFunction<'ink>>,
     // Contains a reference to the global value containing the DispatchTable
-    table_ref: Option<inkwell::values::GlobalValue>,
+    table_ref: Option<inkwell::values::GlobalValue<'ink>>,
     // This is the actual DispatchTable type
-    table_type: inkwell::types::StructType,
+    table_type: inkwell::types::StructType<'ink>,
 }
 
-struct TypedDispatchableFunction {
+struct TypedDispatchableFunction<'ink> {
     function: DispatchableFunction,
-    ir_type: FunctionType,
+    ir_type: FunctionType<'ink>,
 }
 
-impl<'a> DispatchTableBuilder<'a> {
+impl<'db, 'ink, 't> DispatchTableBuilder<'db, 'ink, 't> {
     /// Creates a new builder that can generate a dispatch function.
     pub fn new(
-        db: &'a dyn IrDatabase,
-        module: &'a Module,
-        intrinsics: &BTreeMap<FunctionPrototype, FunctionType>,
+        context: &'ink Context,
+        target_data: TargetData,
+        db: &'db dyn HirDatabase,
+        module: &'t Module<'ink>,
+        intrinsics: &BTreeMap<FunctionPrototype, FunctionType<'ink>>,
+        hir_types: &'t HirTypeCache<'db, 'ink>,
     ) -> Self {
-        let mut table = DispatchTableBuilder {
+        let mut table = Self {
             db,
-            context: db.context(),
+            context,
             module,
-            target: db.target_data(),
+            target_data,
             function_to_idx: Default::default(),
             prototype_to_idx: Default::default(),
             entries: Default::default(),
             table_ref: None,
-            table_type: module.get_context().opaque_struct_type("DispatchTable"),
+            table_type: context.opaque_struct_type("DispatchTable"),
+            hir_types,
         };
 
         if !intrinsics.is_empty() {
@@ -247,25 +253,17 @@ impl<'a> DispatchTableBuilder<'a> {
 
         // If the function is not yet contained in the table, add it
         if !self.function_to_idx.contains_key(&function) {
-            let name = function.name(self.db.upcast()).to_string();
-            let hir_type = function.ty(self.db.upcast());
-            let sig = hir_type.callable_sig(self.db.upcast()).unwrap();
-            let ir_type = self
-                .db
-                .type_ir(
-                    hir_type,
-                    CodeGenParams {
-                        make_marshallable: false,
-                    },
-                )
-                .into_function_type();
+            let name = function.name(self.db).to_string();
+            let hir_type = function.ty(self.db);
+            let sig = hir_type.callable_sig(self.db).unwrap();
+            let ir_type = self.hir_types.get_function_type(function);
             let arg_types = sig
                 .params()
                 .iter()
-                .map(|arg| self.db.type_info(arg.clone()))
+                .map(|arg| self.hir_types.type_info(arg))
                 .collect();
             let ret_type = if !sig.ret().is_empty() {
-                Some(self.db.type_info(sig.ret().clone()))
+                Some(self.hir_types.type_info(sig.ret()))
             } else {
                 None
             };
@@ -287,11 +285,11 @@ impl<'a> DispatchTableBuilder<'a> {
             self.function_to_idx.insert(function, index);
 
             // Recurse further
-            let fn_body = function.body(self.db.upcast());
+            let fn_body = function.body(self.db);
             self.collect_expr(
                 fn_body.body_expr(),
                 &fn_body,
-                function.infer(self.db.upcast()).as_ref(),
+                function.infer(self.db).as_ref(),
             );
         }
     }
@@ -305,7 +303,7 @@ impl<'a> DispatchTableBuilder<'a> {
     /// Builds the final DispatchTable with all *called* functions from within the module
     /// # Parameters
     /// * **functions**: Mapping of *defined* Mun functions to their respective IR values.
-    pub fn build(self) -> DispatchTable {
+    pub fn build(self) -> DispatchTable<'ink> {
         // Construct the table body from all the entries in the dispatch table
         let table_body: Vec<BasicTypeEnum> = self
             .entries
@@ -329,18 +327,11 @@ impl<'a> DispatchTableBuilder<'a> {
                     match entry.function.hir {
                         // Case external function: Convert to typed null for the given function
                         None => function_type.const_null(),
-                        Some(f) if f.is_extern(self.db.upcast()) => function_type.const_null(),
+                        Some(f) if f.is_extern(self.db) => function_type.const_null(),
                         // Case mun function: Get the function location as the initializer
-                        Some(f) => function::gen_signature(
-                            self.db,
-                            f,
-                            self.module,
-                            CodeGenParams {
-                                make_marshallable: false,
-                            },
-                        )
-                        .as_global_value()
-                        .as_pointer_value(),
+                        Some(f) => function::gen_prototype(self.db, self.hir_types, f, self.module)
+                            .as_global_value()
+                            .as_pointer_value(),
                     }
                     .into()
                 })
@@ -353,7 +344,7 @@ impl<'a> DispatchTableBuilder<'a> {
 
         DispatchTable {
             context: self.context,
-            target: self.target,
+            target: self.target_data,
             function_to_idx: self.function_to_idx,
             prototype_to_idx: self.prototype_to_idx,
             table_ref: self.table_ref,
diff --git a/crates/mun_codegen/src/ir/file.rs b/crates/mun_codegen/src/ir/file.rs
index d1db08c92..4dac33542 100644
--- a/crates/mun_codegen/src/ir/file.rs
+++ b/crates/mun_codegen/src/ir/file.rs
@@ -1,59 +1,55 @@
 use super::body::ExternalGlobals;
+use crate::code_gen::CodeGenContext;
+use crate::ir::body::BodyIrGenerator;
+use crate::ir::file_group::FileGroupIR;
 use crate::ir::{function, type_table::TypeTable};
 use crate::value::Global;
-use crate::{CodeGenParams, IrDatabase};
 use hir::{FileId, ModuleDef};
 use inkwell::module::Module;
 use std::collections::{BTreeMap, HashMap, HashSet};
-use std::sync::Arc;
 
 /// The IR generated for a single source file.
 #[derive(Debug, Clone, Eq, PartialEq)]
-pub struct FileIR {
+pub struct FileIR<'ink> {
     /// The original source file
     pub file_id: FileId,
     /// The LLVM module that contains the IR
-    pub llvm_module: Module,
+    pub llvm_module: Module<'ink>,
     /// The `hir::Function`s that constitute the file's API.
     pub api: HashSet<hir::Function>,
 }
 
 /// Generates IR for the specified file.
-pub(crate) fn ir_query(db: &dyn IrDatabase, file_id: FileId) -> Arc<FileIR> {
-    let llvm_module = db
-        .context()
-        .create_module(db.file_relative_path(file_id).as_str());
+pub(crate) fn gen_file_ir<'db, 'ink>(
+    code_gen: &CodeGenContext<'db, 'ink>,
+    group_ir: &FileGroupIR<'ink>,
+    file_id: FileId,
+) -> FileIR<'ink> {
+    let llvm_module = code_gen
+        .context
+        .create_module(code_gen.db.file_relative_path(file_id).as_str());
 
-    let group_ir = db.group_ir(file_id);
+    let hir_types = &code_gen.hir_types;
 
     // Generate all exposed function and wrapper function signatures.
     // Use a `BTreeMap` to guarantee deterministically ordered output.ures
     let mut functions = HashMap::new();
     let mut wrapper_functions = BTreeMap::new();
-    for def in db.module_data(file_id).definitions() {
+    for def in code_gen.db.module_data(file_id).definitions() {
         if let ModuleDef::Function(f) = def {
-            if !f.is_extern(db.upcast()) {
-                let fun = function::gen_signature(
-                    db,
-                    *f,
-                    &llvm_module,
-                    CodeGenParams {
-                        make_marshallable: false,
-                    },
-                );
+            if !f.is_extern(code_gen.db) {
+                let fun = function::gen_prototype(code_gen.db, hir_types, *f, &llvm_module);
                 functions.insert(*f, fun);
 
-                let fn_sig = f.ty(db.upcast()).callable_sig(db.upcast()).unwrap();
-                if !f.data(db.upcast()).visibility().is_private()
-                    && !fn_sig.marshallable(db.upcast())
+                let fn_sig = f.ty(code_gen.db).callable_sig(code_gen.db).unwrap();
+                if !f.data(code_gen.db).visibility().is_private()
+                    && !fn_sig.marshallable(code_gen.db)
                 {
-                    let wrapper_fun = function::gen_signature(
-                        db,
+                    let wrapper_fun = function::gen_public_prototype(
+                        code_gen.db,
+                        &code_gen.hir_types,
                         *f,
                         &llvm_module,
-                        CodeGenParams {
-                            make_marshallable: true,
-                        },
                     );
                     wrapper_functions.insert(*f, wrapper_fun);
                 }
@@ -82,43 +78,51 @@ pub(crate) fn ir_query(db: &dyn IrDatabase, file_id: FileId) -> Arc<FileIR> {
     };
 
     // Construct requirements for generating the bodies
-    let fn_pass_manager = function::create_pass_manager(&llvm_module, db.optimization_lvl());
+    let fn_pass_manager = function::create_pass_manager(&llvm_module, code_gen.optimization_level);
 
     // Generate the function bodies
     for (hir_function, llvm_function) in functions.iter() {
-        function::gen_body(
-            db,
+        let mut code_gen = BodyIrGenerator::new(
+            code_gen.context,
+            code_gen.db,
             (*hir_function, *llvm_function),
             &functions,
             &group_ir.dispatch_table,
             &group_ir.type_table,
             external_globals.clone(),
+            &code_gen.hir_types,
         );
+
+        code_gen.gen_fn_body();
         fn_pass_manager.run_on(llvm_function);
     }
 
     for (hir_function, llvm_function) in wrapper_functions.iter() {
-        function::gen_wrapper_body(
-            db,
+        let mut code_gen = BodyIrGenerator::new(
+            code_gen.context,
+            code_gen.db,
             (*hir_function, *llvm_function),
             &functions,
             &group_ir.dispatch_table,
             &group_ir.type_table,
             external_globals.clone(),
+            &code_gen.hir_types,
         );
+
+        code_gen.gen_fn_wrapper();
         fn_pass_manager.run_on(llvm_function);
     }
 
     // Filter private methods
     let api: HashSet<hir::Function> = functions
         .keys()
-        .filter(|f| f.visibility(db.upcast()) != hir::Visibility::Private)
+        .filter(|f| f.visibility(code_gen.db) != hir::Visibility::Private)
         .cloned()
         .collect();
 
-    Arc::new(FileIR {
+    FileIR {
         file_id,
         llvm_module,
         api,
-    })
+    }
 }
diff --git a/crates/mun_codegen/src/ir/file_group.rs b/crates/mun_codegen/src/ir/file_group.rs
index ef64af8ee..0443e9dc7 100644
--- a/crates/mun_codegen/src/ir/file_group.rs
+++ b/crates/mun_codegen/src/ir/file_group.rs
@@ -1,73 +1,87 @@
 use super::{
-    adt,
     dispatch_table::{DispatchTable, DispatchTableBuilder},
     intrinsics,
     type_table::{TypeTable, TypeTableBuilder},
 };
+use crate::code_gen::CodeGenContext;
 use crate::value::{IrTypeContext, IrValueContext};
-use crate::IrDatabase;
 use hir::ModuleDef;
 use inkwell::{module::Module, types::PointerType, values::UnnamedAddress, AddressSpace};
-use std::{collections::BTreeMap, sync::Arc};
+use std::collections::BTreeMap;
 
 /// The IR generated for a group of files. It is used to generate IR for all of the group's files
 /// and the resulting `Assembly`'s symbols.
 #[derive(Debug, PartialEq, Eq)]
-pub struct FileGroupIR {
+pub struct FileGroupIR<'ink> {
     /// The LLVM module that contains the IR
-    pub(crate) llvm_module: Module,
+    pub(crate) llvm_module: Module<'ink>,
     /// The dispatch table
-    pub(crate) dispatch_table: DispatchTable,
+    pub(crate) dispatch_table: DispatchTable<'ink>,
     /// The type table
-    pub(crate) type_table: TypeTable,
+    pub(crate) type_table: TypeTable<'ink>,
     /// The allocator handle, if it exists
-    pub(crate) allocator_handle_type: Option<PointerType>,
+    pub(crate) allocator_handle_type: Option<PointerType<'ink>>,
 }
 
 /// Generates IR that is shared among the group's files.
 /// TODO: Currently, a group always consists of a single file. Need to add support for multiple
-/// files using something like `FileGroupId`.
-pub(crate) fn ir_query(db: &dyn IrDatabase, file_id: hir::FileId) -> Arc<FileGroupIR> {
-    let llvm_module = db.context().create_module("group_name");
+///  files using something like `FileGroupId`.
+pub(crate) fn gen_file_group_ir<'db, 'ink>(
+    code_gen: &CodeGenContext<'db, 'ink>,
+    file_id: hir::FileId,
+) -> FileGroupIR<'ink> {
+    let llvm_module = code_gen.context.create_module("group_name");
 
     // Use a `BTreeMap` to guarantee deterministically ordered output.
     let mut intrinsics_map = BTreeMap::new();
     let mut needs_alloc = false;
 
     // Collect all intrinsic functions, wrapper function, and generate struct declarations.
-    for def in db.module_data(file_id).definitions() {
+    for def in code_gen.db.module_data(file_id).definitions() {
         match def {
-            ModuleDef::Function(f) if !f.is_extern(db.upcast()) => {
+            ModuleDef::Function(f) if !f.is_extern(code_gen.db) => {
                 intrinsics::collect_fn_body(
-                    db,
+                    &code_gen.context,
+                    code_gen.target_machine.get_target_data(),
+                    code_gen.db,
                     &mut intrinsics_map,
                     &mut needs_alloc,
-                    &f.body(db.upcast()),
-                    &f.infer(db.upcast()),
+                    &f.body(code_gen.db),
+                    &f.infer(code_gen.db),
                 );
 
-                let fn_sig = f.ty(db.upcast()).callable_sig(db.upcast()).unwrap();
-                if !f.data(db.upcast()).visibility().is_private()
-                    && !fn_sig.marshallable(db.upcast())
+                let fn_sig = f.ty(code_gen.db).callable_sig(code_gen.db).unwrap();
+                if !f.data(code_gen.db).visibility().is_private()
+                    && !fn_sig.marshallable(code_gen.db)
                 {
-                    intrinsics::collect_wrapper_body(db, &mut intrinsics_map, &mut needs_alloc);
+                    intrinsics::collect_wrapper_body(
+                        &code_gen.context,
+                        code_gen.target_machine.get_target_data(),
+                        &mut intrinsics_map,
+                        &mut needs_alloc,
+                    );
                 }
             }
             ModuleDef::Function(_) => (), // TODO: Extern types?
-            ModuleDef::Struct(s) => {
-                adt::gen_struct_decl(db, *s);
-            }
+            ModuleDef::Struct(_) => (),
             ModuleDef::BuiltinType(_) => (),
         }
     }
 
     // Collect all exposed functions' bodies.
-    let mut dispatch_table_builder = DispatchTableBuilder::new(db, &llvm_module, &intrinsics_map);
-    for def in db.module_data(file_id).definitions() {
+    let mut dispatch_table_builder = DispatchTableBuilder::new(
+        code_gen.context,
+        code_gen.target_machine.get_target_data(),
+        code_gen.db,
+        &llvm_module,
+        &intrinsics_map,
+        &code_gen.hir_types,
+    );
+    for def in code_gen.db.module_data(file_id).definitions() {
         if let ModuleDef::Function(f) = def {
-            if !f.data(db.upcast()).visibility().is_private() && !f.is_extern(db.upcast()) {
-                let body = f.body(db.upcast());
-                let infer = f.infer(db.upcast());
+            if !f.data(code_gen.db).visibility().is_private() && !f.is_extern(code_gen.db) {
+                let body = f.body(code_gen.db);
+                let infer = f.infer(code_gen.db);
                 dispatch_table_builder.collect_body(&body, &infer);
             }
         }
@@ -75,23 +89,28 @@ pub(crate) fn ir_query(db: &dyn IrDatabase, file_id: hir::FileId) -> Arc<FileGro
 
     let dispatch_table = dispatch_table_builder.build();
 
-    let struct_types = db.type_to_struct_mapping();
-
+    let target_data = code_gen.target_machine.get_target_data();
     let type_context = IrTypeContext {
-        context: &db.context(),
-        target_data: &db.target_data(),
-        struct_types: struct_types.as_ref(),
+        context: &code_gen.context,
+        target_data: &target_data,
+        struct_types: &code_gen.rust_types,
     };
     let value_context = IrValueContext {
         type_context: &type_context,
-        context: &db.context(),
+        context: &code_gen.context,
         module: &llvm_module,
     };
-    let mut type_table_builder =
-        TypeTableBuilder::new(db, &value_context, intrinsics_map.keys(), &dispatch_table);
+    let mut type_table_builder = TypeTableBuilder::new(
+        code_gen.db,
+        code_gen.target_machine.get_target_data(),
+        &value_context,
+        intrinsics_map.keys(),
+        &dispatch_table,
+        &code_gen.hir_types,
+    );
 
     // Collect all used types
-    for def in db.module_data(file_id).definitions() {
+    for def in code_gen.db.module_data(file_id).definitions() {
         match def {
             ModuleDef::Struct(s) => {
                 type_table_builder.collect_struct(*s);
@@ -107,7 +126,7 @@ pub(crate) fn ir_query(db: &dyn IrDatabase, file_id: hir::FileId) -> Arc<FileGro
 
     // Create the allocator handle global value
     let allocator_handle_type = if needs_alloc {
-        let allocator_handle_type = db.context().i8_type().ptr_type(AddressSpace::Generic);
+        let allocator_handle_type = code_gen.context.i8_type().ptr_type(AddressSpace::Generic);
         let global = llvm_module.add_global(allocator_handle_type, None, "allocatorHandle");
         global.set_initializer(&allocator_handle_type.const_null());
         global.set_unnamed_address(UnnamedAddress::Global);
@@ -116,10 +135,10 @@ pub(crate) fn ir_query(db: &dyn IrDatabase, file_id: hir::FileId) -> Arc<FileGro
         None
     };
 
-    Arc::new(FileGroupIR {
+    FileGroupIR {
         llvm_module,
         dispatch_table,
         type_table,
         allocator_handle_type,
-    })
+    }
 }
diff --git a/crates/mun_codegen/src/ir/function.rs b/crates/mun_codegen/src/ir/function.rs
index cf7860cad..ea03ceca1 100644
--- a/crates/mun_codegen/src/ir/function.rs
+++ b/crates/mun_codegen/src/ir/function.rs
@@ -1,17 +1,16 @@
-use crate::ir::{body::BodyIrGenerator, dispatch_table::DispatchTable, type_table::TypeTable};
-use crate::{CodeGenParams, IrDatabase, Module, OptimizationLevel};
-use inkwell::passes::{PassManager, PassManagerBuilder};
-use inkwell::types::AnyTypeEnum;
-use inkwell::values::FunctionValue;
+use crate::{ir::ty::HirTypeCache, Module, OptimizationLevel};
+use inkwell::{
+    passes::{PassManager, PassManagerBuilder},
+    values::FunctionValue,
+};
 
-use super::body::ExternalGlobals;
-use std::collections::HashMap;
+use hir::HirDatabase;
 
 /// Constructs a PassManager to optimize functions for the given optimization level.
-pub(crate) fn create_pass_manager(
-    module: &Module,
+pub(crate) fn create_pass_manager<'ink>(
+    module: &Module<'ink>,
     optimization_lvl: OptimizationLevel,
-) -> PassManager<FunctionValue> {
+) -> PassManager<FunctionValue<'ink>> {
     let pass_builder = PassManagerBuilder::create();
     pass_builder.set_optimization_level(optimization_lvl);
 
@@ -26,73 +25,29 @@ pub(crate) fn create_pass_manager(
 /// the `hir::Function`. That task is left to the `gen_body` function. The reason this is split
 /// between two functions is that first all signatures are generated and then all bodies. This
 /// allows bodies to reference `FunctionValue` wherever they are declared in the file.
-pub(crate) fn gen_signature(
-    db: &dyn IrDatabase,
-    f: hir::Function,
-    module: &Module,
-    params: CodeGenParams,
-) -> FunctionValue {
-    let name = {
-        let name = f.name(db.upcast()).to_string();
-        if params.make_marshallable {
-            format!("{}_wrapper", name)
-        } else {
-            name
-        }
-    };
-
-    if let AnyTypeEnum::FunctionType(ty) = db.type_ir(f.ty(db.upcast()), params) {
-        module.add_function(&name, ty, None)
-    } else {
-        panic!("not a function type")
-    }
+pub(crate) fn gen_prototype<'db, 'ink>(
+    db: &'db dyn HirDatabase,
+    types: &HirTypeCache<'db, 'ink>,
+    func: hir::Function,
+    module: &Module<'ink>,
+) -> FunctionValue<'ink> {
+    let name = func.name(db).to_string();
+    let ir_ty = types.get_function_type(func);
+    module.add_function(&name, ir_ty, None)
 }
 
-/// Generates the body of a `hir::Function` for an associated `FunctionValue`.
-pub(crate) fn gen_body<'a, 'b>(
-    db: &'a dyn IrDatabase,
-    function: (hir::Function, FunctionValue),
-    llvm_functions: &'a HashMap<hir::Function, FunctionValue>,
-    dispatch_table: &'b DispatchTable,
-    type_table: &'b TypeTable,
-    external_globals: ExternalGlobals,
-) {
-    let mut code_gen = BodyIrGenerator::new(
-        db,
-        function,
-        llvm_functions,
-        dispatch_table,
-        type_table,
-        CodeGenParams {
-            make_marshallable: false,
-        },
-        external_globals,
-    );
-
-    code_gen.gen_fn_body();
-}
-
-/// Generates the body of a wrapper around `hir::Function` for its associated
-/// `FunctionValue`
-pub(crate) fn gen_wrapper_body<'a, 'b>(
-    db: &'a dyn IrDatabase,
-    function: (hir::Function, FunctionValue),
-    llvm_functions: &'a HashMap<hir::Function, FunctionValue>,
-    dispatch_table: &'b DispatchTable,
-    type_table: &'b TypeTable,
-    external_globals: ExternalGlobals,
-) {
-    let mut code_gen = BodyIrGenerator::new(
-        db,
-        function,
-        llvm_functions,
-        dispatch_table,
-        type_table,
-        CodeGenParams {
-            make_marshallable: true,
-        },
-        external_globals,
-    );
-
-    code_gen.gen_fn_wrapper();
+/// Generates a `FunctionValue` for a `hir::Function` that is usable from the public API. This
+/// function does not generate a body for the `hir::Function`. That task is left to the `gen_body`
+/// function. The reason this is split between two functions is that first all signatures are
+/// generated and then all bodies. This allows bodies to reference `FunctionValue` wherever they
+/// are declared in the file.
+pub(crate) fn gen_public_prototype<'db, 'ink>(
+    db: &'db dyn HirDatabase,
+    types: &HirTypeCache<'db, 'ink>,
+    func: hir::Function,
+    module: &Module<'ink>,
+) -> FunctionValue<'ink> {
+    let name = format!("{}_wrapper", func.name(db));
+    let ir_ty = types.get_public_function_type(func);
+    module.add_function(&name, ir_ty, None)
 }
diff --git a/crates/mun_codegen/src/ir/intrinsics.rs b/crates/mun_codegen/src/ir/intrinsics.rs
index 2223bd92f..0861401e0 100644
--- a/crates/mun_codegen/src/ir/intrinsics.rs
+++ b/crates/mun_codegen/src/ir/intrinsics.rs
@@ -1,26 +1,34 @@
-use crate::intrinsics::{self, Intrinsic};
-use crate::ir::dispatch_table::FunctionPrototype;
-use crate::IrDatabase;
-use hir::{Body, Expr, ExprId, InferenceResult};
-use inkwell::types::FunctionType;
-use std::collections::BTreeMap;
-use std::sync::Arc;
+use crate::{
+    intrinsics::{self, Intrinsic},
+    ir::dispatch_table::FunctionPrototype,
+};
+use hir::{Body, Expr, ExprId, HirDatabase, InferenceResult};
+use inkwell::{context::Context, targets::TargetData, types::FunctionType};
+use std::{collections::BTreeMap, sync::Arc};
 
 // Use a `BTreeMap` to guarantee deterministically ordered output
-pub type IntrinsicsMap = BTreeMap<FunctionPrototype, FunctionType>;
+pub type IntrinsicsMap<'ink> = BTreeMap<FunctionPrototype, FunctionType<'ink>>;
 
-fn collect_intrinsic(d: &dyn IrDatabase, entries: &mut IntrinsicsMap, intrinsic: &impl Intrinsic) {
-    let context = d.context();
-    let target = d.target_data();
-    let prototype = intrinsic.prototype(context.as_ref(), target.as_ref());
+/// Stores the type information from the `intrinsic` in `entries`
+fn collect_intrinsic<'ink>(
+    context: &'ink Context,
+    target: &TargetData,
+    intrinsic: &impl Intrinsic,
+    entries: &mut IntrinsicsMap<'ink>,
+) {
+    let prototype = intrinsic.prototype(context, target);
     entries
         .entry(prototype)
-        .or_insert_with(|| intrinsic.ir_type(context.as_ref(), target.as_ref()));
+        .or_insert_with(|| intrinsic.ir_type(context, target));
 }
 
-fn collect_expr(
-    db: &dyn IrDatabase,
-    entries: &mut IntrinsicsMap,
+/// Iterates over all expressions and stores information on which intrinsics they use in `entries`.
+#[allow(clippy::too_many_arguments)]
+fn collect_expr<'db, 'ink>(
+    context: &'ink Context,
+    target: &TargetData,
+    db: &'db dyn HirDatabase,
+    intrinsics: &mut IntrinsicsMap<'ink>,
     needs_alloc: &mut bool,
     expr_id: ExprId,
     body: &Arc<Body>,
@@ -32,7 +40,7 @@ fn collect_expr(
     if let Expr::Call { callee, .. } = expr {
         match infer[*callee].as_callable_def() {
             Some(hir::CallableDef::Struct(_)) => {
-                collect_intrinsic(db, entries, &intrinsics::new);
+                collect_intrinsic(context, &target, &intrinsics::new, intrinsics);
                 // self.collect_intrinsic(module, entries, &intrinsics::drop);
                 *needs_alloc = true;
             }
@@ -42,45 +50,70 @@ fn collect_expr(
     }
 
     if let Expr::RecordLit { .. } = expr {
-        collect_intrinsic(db, entries, &intrinsics::new);
+        collect_intrinsic(context, &target, &intrinsics::new, intrinsics);
         // self.collect_intrinsic(module, entries, &intrinsics::drop);
         *needs_alloc = true;
     }
 
     if let Expr::Path(path) = expr {
-        let resolver = hir::resolver_for_expr(body.clone(), db.upcast(), expr_id);
+        let resolver = hir::resolver_for_expr(body.clone(), db, expr_id);
         let resolution = resolver
-            .resolve_path_without_assoc_items(db.upcast(), path)
+            .resolve_path_without_assoc_items(db, path)
             .take_values()
             .expect("unknown path");
 
         if let hir::Resolution::Def(hir::ModuleDef::Struct(_)) = resolution {
-            collect_intrinsic(db, entries, &intrinsics::new);
+            collect_intrinsic(context, &target, &intrinsics::new, intrinsics);
             // self.collect_intrinsic( module, entries, &intrinsics::drop);
             *needs_alloc = true;
         }
     }
 
     // Recurse further
-    expr.walk_child_exprs(|expr_id| collect_expr(db, entries, needs_alloc, expr_id, body, infer))
+    expr.walk_child_exprs(|expr_id| {
+        collect_expr(
+            context,
+            target,
+            db,
+            intrinsics,
+            needs_alloc,
+            expr_id,
+            body,
+            infer,
+        )
+    })
 }
 
-pub fn collect_fn_body(
-    db: &dyn IrDatabase,
-    entries: &mut IntrinsicsMap,
+/// Collects all intrinsics from the specified `body`.
+pub fn collect_fn_body<'db, 'ink>(
+    context: &'ink Context,
+    target: TargetData,
+    db: &'db dyn HirDatabase,
+    intrinsics: &mut IntrinsicsMap<'ink>,
     needs_alloc: &mut bool,
     body: &Arc<Body>,
     infer: &InferenceResult,
 ) {
-    collect_expr(db, entries, needs_alloc, body.body_expr(), body, infer);
+    collect_expr(
+        context,
+        &target,
+        db,
+        intrinsics,
+        needs_alloc,
+        body.body_expr(),
+        body,
+        infer,
+    );
 }
 
-pub fn collect_wrapper_body(
-    db: &dyn IrDatabase,
-    entries: &mut IntrinsicsMap,
+/// Collects all intrinsics from a function wrapper body.
+pub fn collect_wrapper_body<'ink>(
+    context: &'ink Context,
+    target: TargetData,
+    intrinsics: &mut IntrinsicsMap<'ink>,
     needs_alloc: &mut bool,
 ) {
-    collect_intrinsic(db, entries, &intrinsics::new);
+    collect_intrinsic(context, &target, &intrinsics::new, intrinsics);
     // self.collect_intrinsic(entries, &intrinsics::drop, module);
     *needs_alloc = true;
 }
diff --git a/crates/mun_codegen/src/ir/ty.rs b/crates/mun_codegen/src/ir/ty.rs
index 1c25100e3..a0d1e223b 100644
--- a/crates/mun_codegen/src/ir/ty.rs
+++ b/crates/mun_codegen/src/ir/ty.rs
@@ -1,133 +1,261 @@
-use super::try_convert_any_to_basic;
 use crate::{
+    ir::IsIrType,
     type_info::{TypeInfo, TypeSize},
-    CodeGenParams, IrDatabase,
-};
-use hir::{
-    ApplicationTy, CallableDef, FloatBitness, FloatTy, IntBitness, IntTy, ResolveBitness, Ty,
-    TypeCtor,
 };
+use hir::{ty_app, FloatBitness, HirDatabase, IntBitness, ResolveBitness, Ty, TypeCtor};
 use inkwell::{
+    context::Context,
+    targets::TargetData,
+    types::FunctionType,
     types::{AnyTypeEnum, BasicType, BasicTypeEnum, FloatType, IntType, StructType},
     AddressSpace,
 };
+use std::{cell::RefCell, collections::HashMap};
+
+/// An object to cache and convert HIR types to Inkwell types.
+pub struct HirTypeCache<'db, 'ink> {
+    context: &'ink Context,
+    db: &'db dyn HirDatabase,
+    target_data: TargetData,
+    types: RefCell<HashMap<hir::Ty, StructType<'ink>>>,
+}
+
+impl<'db, 'ink> HirTypeCache<'db, 'ink> {
+    /// Constructs a new `HirTypeContext`
+    pub fn new(context: &'ink Context, db: &'db dyn HirDatabase, target_data: TargetData) -> Self {
+        Self {
+            context,
+            db,
+            target_data,
+            types: RefCell::new(HashMap::default()),
+        }
+    }
+
+    /// Returns the type of the specified floating-point type
+    pub fn get_float_type(&self, ty: hir::FloatTy) -> FloatType<'ink> {
+        match ty.bitness {
+            FloatBitness::X64 => self.context.f64_type(),
+            FloatBitness::X32 => self.context.f32_type(),
+        }
+    }
+
+    /// Returns the type of the specified integer type
+    pub fn get_int_type(&self, ty: hir::IntTy) -> IntType<'ink> {
+        match ty.bitness {
+            IntBitness::X128 => self.context.i128_type(),
+            IntBitness::X64 => self.context.i64_type(),
+            IntBitness::X32 => self.context.i32_type(),
+            IntBitness::X16 => self.context.i16_type(),
+            IntBitness::X8 => self.context.i8_type(),
+            IntBitness::Xsize => usize::ir_type(self.context, &self.target_data),
+        }
+    }
+
+    /// Returns the type for booleans
+    pub fn get_bool_type(&self) -> IntType<'ink> {
+        self.context.bool_type()
+    }
 
-/// Given a mun type, construct an LLVM IR type
-#[rustfmt::skip]
-pub(crate) fn ir_query(db: &dyn IrDatabase, ty: Ty, params: CodeGenParams) -> AnyTypeEnum {
-    let context = db.context();
-    match ty {
-        Ty::Empty => AnyTypeEnum::StructType(context.struct_type(&[], false)),
-        Ty::Apply(ApplicationTy { ctor, .. }) => match ctor {
-            TypeCtor::Float(fty) => float_ty_query(db, fty).into(),
-            TypeCtor::Int(ity) => int_ty_query(db, ity).into(),
-            TypeCtor::Bool => AnyTypeEnum::IntType(context.bool_type()),
-
-            TypeCtor::FnDef(def @ CallableDef::Function(_)) => {
-                let ty = db.callable_sig(def);
-                let param_tys: Vec<BasicTypeEnum> = ty
-                    .params()
-                    .iter()
-                    .map(|p| {
-                        try_convert_any_to_basic(db.type_ir(p.clone(), params.clone())).unwrap()
-                    })
-                    .collect();
-
-                let fn_type = match ty.ret() {
-                    Ty::Empty => context.void_type().fn_type(&param_tys, false),
-                    ty => try_convert_any_to_basic(db.type_ir(ty.clone(), params))
-                        .expect("could not convert return value")
-                        .fn_type(&param_tys, false),
-                };
-
-                AnyTypeEnum::FunctionType(fn_type)
+    /// Returns the type of the specified integer type
+    pub fn get_struct_type(&self, struct_ty: hir::Struct) -> StructType<'ink> {
+        // TODO: This assumes the contents of the hir::Struct does not change. It definitely does
+        //  between compilations. We have to have a way to uniquely identify the `hir::Struct` and
+        //  its contents.
+
+        let ty = Ty::simple(TypeCtor::Struct(struct_ty));
+
+        // Get the type from the cache
+        if let Some(ir_ty) = self.types.borrow().get(&ty) {
+            return *ir_ty;
+        };
+
+        // Opaquely construct the struct type and store it in the cache
+        let ir_ty = self
+            .context
+            .opaque_struct_type(&struct_ty.name(self.db.upcast()).to_string());
+        self.types.borrow_mut().insert(ty, ir_ty);
+
+        // Fill the struct members
+        let field_types: Vec<_> = struct_ty
+            .fields(self.db)
+            .into_iter()
+            .map(|field| field.ty(self.db))
+            .map(|ty| {
+                self.get_basic_type(&ty)
+                    .expect("could not convert struct field to basic type")
+            })
+            .collect();
+        ir_ty.set_body(&field_types, false);
+
+        ir_ty
+    }
+
+    /// Returns the type of the struct that should be used for variables.
+    pub fn get_struct_reference_type(&self, struct_ty: hir::Struct) -> BasicTypeEnum<'ink> {
+        let ir_ty = self.get_struct_type(struct_ty);
+        match struct_ty.data(self.db.upcast()).memory_kind {
+            hir::StructMemoryKind::GC => {
+                // GC values are pointers to pointers
+                // struct Foo {}
+                // Foo**
+                ir_ty
+                    .ptr_type(AddressSpace::Generic)
+                    .ptr_type(AddressSpace::Generic)
+                    .into()
             }
-            TypeCtor::Struct(s) => {
-                let struct_ty = db.struct_ty(s);
-                match s.data(db.upcast()).memory_kind {
-                    hir::StructMemoryKind::GC => struct_ty.ptr_type(AddressSpace::Generic).ptr_type(AddressSpace::Generic).into(),
-                    hir::StructMemoryKind::Value if params.make_marshallable =>
-                            struct_ty.ptr_type(AddressSpace::Generic).ptr_type(AddressSpace::Generic).into(),
-                    hir::StructMemoryKind::Value => struct_ty.into(),
-                }
+            hir::StructMemoryKind::Value => {
+                // Value structs are passed as values
+                ir_ty.into()
             }
-            _ => unreachable!(),
-        },
-        _ => unreachable!("unknown type can not be converted"),
+        }
     }
-}
 
-/// Returns the LLVM IR type of the specified float type
-fn float_ty_query(db: &dyn IrDatabase, fty: FloatTy) -> FloatType {
-    let context = db.context();
-    match fty.bitness.resolve(&db.target_data_layout()) {
-        FloatBitness::X64 => context.f64_type(),
-        FloatBitness::X32 => context.f32_type(),
-    }
-}
+    /// Returns the type of the struct that should be used in the public API. In the public API we
+    /// don't deal with value types, only with pointers.
+    pub fn get_public_struct_reference_type(&self, struct_ty: hir::Struct) -> BasicTypeEnum<'ink> {
+        let ir_ty = self.get_struct_type(struct_ty);
 
-/// Returns the LLVM IR type of the specified int type
-fn int_ty_query(db: &dyn IrDatabase, ity: IntTy) -> IntType {
-    let context = db.context();
-    match ity.bitness.resolve(&db.target_data_layout()) {
-        IntBitness::X128 => context.i128_type(),
-        IntBitness::X64 => context.i64_type(),
-        IntBitness::X32 => context.i32_type(),
-        IntBitness::X16 => context.i16_type(),
-        IntBitness::X8 => context.i8_type(),
-        _ => unreachable!(),
+        // GC values are pointers to pointers
+        // struct Foo {}
+        // Foo**
+        //
+        // Value structs are converted to GC types in the public API.
+        ir_ty
+            .ptr_type(AddressSpace::Generic)
+            .ptr_type(AddressSpace::Generic)
+            .into()
     }
-}
 
-/// Returns the LLVM IR type of the specified struct
-pub fn struct_ty_query(db: &dyn IrDatabase, s: hir::Struct) -> StructType {
-    let name = s.name(db.upcast()).to_string();
-    for field in s.fields(db.upcast()).iter() {
-        // Ensure that salsa's cached value incorporates the struct fields
-        let _field_type_ir = db.type_ir(
-            field.ty(db.upcast()),
-            CodeGenParams {
-                make_marshallable: false,
-            },
-        );
+    /// Returns the type of the specified function definition
+    pub fn get_function_type(&self, ty: hir::Function) -> FunctionType<'ink> {
+        let ty = self.db.callable_sig(ty.into());
+        let param_tys: Vec<_> = ty
+            .params()
+            .iter()
+            .map(|p| {
+                self.get_basic_type(p)
+                    .expect("could not convert function argument to basic type")
+            })
+            .collect();
+
+        match ty.ret() {
+            Ty::Empty => self.context.void_type().fn_type(&param_tys, false),
+            ty => self
+                .get_basic_type(&ty)
+                .expect("could not convert return value")
+                .fn_type(&param_tys, false),
+        }
     }
 
-    db.context().opaque_struct_type(&name)
-}
+    /// Returns the type of a specified function definition that is callable from the outside of the
+    /// Mun code. This function should be C ABI compatible.
+    pub fn get_public_function_type(&self, ty: hir::Function) -> FunctionType<'ink> {
+        let ty = self.db.callable_sig(ty.into());
+        let param_tys: Vec<_> = ty
+            .params()
+            .iter()
+            .map(|p| {
+                self.get_public_basic_type(p)
+                    .expect("could not convert function argument to public basic type")
+            })
+            .collect();
 
-/// Constructs the `TypeInfo` for the specified HIR type
-pub fn type_info_query(db: &dyn IrDatabase, ty: Ty) -> TypeInfo {
-    let target = db.target_data();
-    match ty {
-        Ty::Apply(ctor) => match ctor.ctor {
-            TypeCtor::Float(ty) => {
-                let ir_ty = float_ty_query(db, ty);
-                let type_size = TypeSize::from_ir_type(&ir_ty, target.as_ref());
-                TypeInfo::new_fundamental(
-                    format!("core::{}", ty.resolve(&db.target_data_layout())),
-                    type_size,
-                )
+        match ty.ret() {
+            Ty::Empty => self.context.void_type().fn_type(&param_tys, false),
+            ty => self
+                .get_public_basic_type(&ty)
+                .expect("could not convert return value")
+                .fn_type(&param_tys, false),
+        }
+    }
+
+    /// Returns the inkwell type of the specified HIR type as a basic value. If the type cannot be
+    /// represented as a basic type enum, `None` is returned.
+    pub fn get_basic_type(&self, ty: &hir::Ty) -> Option<BasicTypeEnum<'ink>> {
+        match ty {
+            Ty::Empty => Some(self.get_empty_type().into()),
+            ty_app!(hir::TypeCtor::Float(float_ty)) => Some(self.get_float_type(*float_ty).into()),
+            ty_app!(hir::TypeCtor::Int(int_ty)) => Some(self.get_int_type(*int_ty).into()),
+            ty_app!(hir::TypeCtor::Struct(struct_ty)) => {
+                Some(self.get_struct_reference_type(*struct_ty))
             }
-            TypeCtor::Int(ty) => {
-                let ir_ty = int_ty_query(db, ty);
-                let type_size = TypeSize::from_ir_type(&ir_ty, target.as_ref());
-                TypeInfo::new_fundamental(
-                    format!("core::{}", ty.resolve(&db.target_data_layout())),
-                    type_size,
-                )
+            ty_app!(hir::TypeCtor::Bool) => Some(self.get_bool_type().into()),
+            _ => None,
+        }
+    }
+
+    /// Returns the inkwell type of the specified HIR type as a basic value that is usable from the
+    /// public API. Internally this means that struct types are always pointers. If the type cannot
+    /// be represented as a basic type enum, `None` is returned.
+    pub fn get_public_basic_type(&self, ty: &hir::Ty) -> Option<BasicTypeEnum<'ink>> {
+        match ty {
+            Ty::Empty => Some(self.get_empty_type().into()),
+            ty_app!(hir::TypeCtor::Float(float_ty)) => Some(self.get_float_type(*float_ty).into()),
+            ty_app!(hir::TypeCtor::Int(int_ty)) => Some(self.get_int_type(*int_ty).into()),
+            ty_app!(hir::TypeCtor::Struct(struct_ty)) => {
+                Some(self.get_public_struct_reference_type(*struct_ty))
             }
-            TypeCtor::Bool => {
-                let ir_ty = db.context().bool_type();
-                let type_size = TypeSize::from_ir_type(&ir_ty, target.as_ref());
-                TypeInfo::new_fundamental("core::bool", type_size)
+            ty_app!(hir::TypeCtor::Bool) => Some(self.get_bool_type().into()),
+            _ => None,
+        }
+    }
+
+    /// Returns the inkwell type of the specified HIR type. If the type cannot be represented as an
+    /// inkwell type, `None` is returned.
+    pub fn get_any_type(&self, ty: &hir::Ty) -> Option<AnyTypeEnum<'ink>> {
+        match ty {
+            Ty::Empty => Some(self.get_empty_type().into()),
+            ty_app!(hir::TypeCtor::Float(float_ty)) => Some(self.get_float_type(*float_ty).into()),
+            ty_app!(hir::TypeCtor::Int(int_ty)) => Some(self.get_int_type(*int_ty).into()),
+            ty_app!(hir::TypeCtor::Struct(struct_ty)) => {
+                Some(self.get_struct_type(*struct_ty).into())
             }
-            TypeCtor::Struct(s) => {
-                let ir_ty = db.struct_ty(s);
-                let type_size = TypeSize::from_ir_type(&ir_ty, target.as_ref());
-                TypeInfo::new_struct(db, s, type_size)
+            ty_app!(hir::TypeCtor::Bool) => Some(self.context.bool_type().into()),
+            ty_app!(hir::TypeCtor::FnDef(hir::CallableDef::Function(fn_ty))) => {
+                Some(self.get_function_type(*fn_ty).into())
             }
-            _ => unreachable!("{:?} unhandled", ctor),
-        },
-        _ => unreachable!("{:?} unhandled", ty),
+            _ => None,
+        }
+    }
+
+    /// Returns the empty type
+    pub fn get_empty_type(&self) -> StructType<'ink> {
+        self.context.struct_type(&[], false)
+    }
+
+    /// Returns a `TypeInfo` for the specified `ty`
+    pub fn type_info(&self, ty: &Ty) -> TypeInfo {
+        match ty {
+            Ty::Apply(ctor) => match ctor.ctor {
+                TypeCtor::Float(ty) => {
+                    let ir_ty = self.get_float_type(ty);
+                    let type_size = TypeSize::from_ir_type(&ir_ty, &self.target_data);
+                    TypeInfo::new_fundamental(
+                        format!("core::{}", ty.resolve(&self.db.target_data_layout())),
+                        type_size,
+                    )
+                }
+                TypeCtor::Int(ty) => {
+                    let ir_ty = self.get_int_type(ty);
+                    let type_size = TypeSize::from_ir_type(&ir_ty, &self.target_data);
+                    TypeInfo::new_fundamental(
+                        format!("core::{}", ty.resolve(&self.db.target_data_layout())),
+                        type_size,
+                    )
+                }
+                TypeCtor::Bool => {
+                    let ir_ty = self.get_bool_type();
+                    let type_size = TypeSize::from_ir_type(&ir_ty, &self.target_data);
+                    TypeInfo::new_fundamental("core::bool", type_size)
+                }
+                TypeCtor::Struct(s) => {
+                    let ir_ty = self.get_struct_type(s);
+                    let type_size = TypeSize::from_ir_type(&ir_ty, &self.target_data);
+                    TypeInfo::new_struct(self.db, s, type_size)
+                }
+                _ => unreachable!("{:?} unhandled", ctor),
+            },
+            _ => unreachable!("{:?} unhandled", ty),
+        }
     }
 }
diff --git a/crates/mun_codegen/src/ir/type_table.rs b/crates/mun_codegen/src/ir/type_table.rs
index 984d1e830..1c530fb27 100644
--- a/crates/mun_codegen/src/ir/type_table.rs
+++ b/crates/mun_codegen/src/ir/type_table.rs
@@ -1,31 +1,38 @@
 use super::types as ir;
-use crate::ir::dispatch_table::{DispatchTable, FunctionPrototype};
-use crate::type_info::{TypeGroup, TypeInfo};
-use crate::value::{AsValue, CanInternalize, Global, IrValueContext, IterAsIrValue, Value};
-use crate::IrDatabase;
-use hir::{Body, ExprId, InferenceResult};
-use inkwell::module::Linkage;
-use inkwell::{module::Module, targets::TargetData, types::ArrayType, values::PointerValue};
-use std::collections::{BTreeSet, HashMap};
-use std::convert::TryInto;
-use std::ffi::CString;
-use std::{mem, sync::Arc};
+use crate::{
+    ir::dispatch_table::{DispatchTable, FunctionPrototype},
+    ir::ty::HirTypeCache,
+    type_info::{TypeGroup, TypeInfo},
+    value::{AsValue, CanInternalize, Global, IrValueContext, IterAsIrValue, Value},
+};
+use hir::{Body, ExprId, HirDatabase, InferenceResult};
+use inkwell::{
+    context::Context, module::Linkage, module::Module, targets::TargetData, types::ArrayType,
+    values::PointerValue,
+};
+use std::{
+    collections::{BTreeSet, HashMap},
+    convert::TryInto,
+    ffi::CString,
+    mem,
+    sync::Arc,
+};
 
 /// A type table in IR is a list of pointers to unique type information that are used to generate
 /// function and struct information.
 #[derive(Debug, Clone, Eq, PartialEq)]
-pub struct TypeTable {
+pub struct TypeTable<'ink> {
     type_info_to_index: HashMap<TypeInfo, usize>,
-    table_type: ArrayType,
+    table_type: ArrayType<'ink>,
 }
 
-impl TypeTable {
+impl<'ink> TypeTable<'ink> {
     /// The name of the TypeTable's LLVM `GlobalValue`.
     pub(crate) const NAME: &'static str = "global_type_table";
 
     /// Looks for a global symbol with the name of the TypeTable global in the specified `module`.
     /// Returns the global value if it could be found, `None` otherwise.
-    pub fn find_global(module: &Module) -> Option<Global<[*const ir::TypeInfo]>> {
+    pub fn find_global(module: &Module<'ink>) -> Option<Global<'ink, [*const ir::TypeInfo<'ink>]>> {
         module
             .get_global(Self::NAME)
             .map(|g| unsafe { Global::from_raw(g) })
@@ -36,24 +43,31 @@ impl TypeTable {
     /// of `TypeInfo` pointers.
     pub fn gen_type_info_lookup(
         &self,
-        builder: &inkwell::builder::Builder,
+        context: &'ink Context,
+        builder: &inkwell::builder::Builder<'ink>,
         type_info: &TypeInfo,
-        table_ref: Option<Global<[*const ir::TypeInfo]>>,
-    ) -> PointerValue {
+        table_ref: Option<Global<'ink, [*const ir::TypeInfo<'ink>]>>,
+    ) -> PointerValue<'ink> {
         let table_ref = table_ref.expect("no type table defined");
 
-        let index = *self
+        let index: u64 = (*self
             .type_info_to_index
             .get(type_info)
-            .expect("unknown type");
+            .expect("unknown type"))
+        .try_into()
+        .expect("too many types");
+
+        let global_index = context.i64_type().const_zero();
+        let array_index = context.i64_type().const_int(index as u64, false);
 
         let ptr_to_type_info_ptr = unsafe {
-            builder.build_struct_gep(
+            builder.build_gep(
                 table_ref.into(),
-                index as u32,
+                &[global_index, array_index],
                 &format!("{}_ptr_ptr", type_info.name),
             )
         };
+
         builder
             .build_load(ptr_to_type_info_ptr, &format!("{}_ptr", type_info.name))
             .into_pointer_value()
@@ -61,10 +75,10 @@ impl TypeTable {
 
     /// Retrieves the global `TypeInfo` IR value corresponding to `type_info`, if it exists.
     pub fn get(
-        module: &Module,
+        module: &Module<'ink>,
         type_info: &TypeInfo,
-        context: &IrValueContext,
-    ) -> Option<Value<*const ir::TypeInfo>> {
+        context: &IrValueContext<'ink, '_, '_>,
+    ) -> Option<Value<'ink, *const ir::TypeInfo<'ink>>> {
         module
             .get_global(&type_info_global_name(type_info))
             .map(|g| Value::<*const ir::TypeInfo>::with_cast(g.as_pointer_value(), context))
@@ -81,33 +95,37 @@ impl TypeTable {
     }
 
     /// Returns the IR type of the type table's global value, if it exists.
-    pub fn ty(&self) -> ArrayType {
+    pub fn ty(&self) -> ArrayType<'ink> {
         self.table_type
     }
 }
 
 /// Used to build a `TypeTable` from HIR.
-pub(crate) struct TypeTableBuilder<'a, 'ctx, 'm> {
-    db: &'a dyn IrDatabase,
-    target_data: Arc<TargetData>,
-    value_context: &'a IrValueContext<'a, 'ctx, 'm>,
-    dispatch_table: &'a DispatchTable,
+pub(crate) struct TypeTableBuilder<'db, 'ink, 't> {
+    db: &'db dyn HirDatabase,
+    target_data: TargetData,
+    value_context: &'t IrValueContext<'ink, 't, 't>,
+    dispatch_table: &'t DispatchTable<'ink>,
+    hir_types: &'t HirTypeCache<'db, 'ink>,
     entries: BTreeSet<TypeInfo>, // Use a `BTreeSet` to guarantee deterministically ordered output
 }
 
-impl<'a, 'ctx, 'm> TypeTableBuilder<'a, 'ctx, 'm> {
+impl<'db, 'ink, 't> TypeTableBuilder<'db, 'ink, 't> {
     /// Creates a new `TypeTableBuilder`.
     pub(crate) fn new<'f>(
-        db: &'a dyn IrDatabase,
-        value_context: &'a IrValueContext<'a, 'ctx, 'm>,
+        db: &'db dyn HirDatabase,
+        target_data: TargetData,
+        value_context: &'t IrValueContext<'ink, '_, '_>,
         intrinsics: impl Iterator<Item = &'f FunctionPrototype>,
-        dispatch_table: &'a DispatchTable,
+        dispatch_table: &'t DispatchTable<'ink>,
+        hir_types: &'t HirTypeCache<'db, 'ink>,
     ) -> Self {
         let mut builder = Self {
             db,
-            target_data: db.target_data(),
+            target_data,
             value_context,
             dispatch_table,
+            hir_types,
             entries: BTreeSet::new(),
         };
 
@@ -145,48 +163,43 @@ impl<'a, 'ctx, 'm> TypeTableBuilder<'a, 'ctx, 'm> {
     /// Collects unique `TypeInfo` from the specified function signature and body.
     pub fn collect_fn(&mut self, hir_fn: hir::Function) {
         // Collect type info for exposed function
-        if !hir_fn.data(self.db.upcast()).visibility().is_private()
-            || self.dispatch_table.contains(hir_fn)
-        {
-            let fn_sig = hir_fn
-                .ty(self.db.upcast())
-                .callable_sig(self.db.upcast())
-                .unwrap();
+        if !hir_fn.data(self.db).visibility().is_private() || self.dispatch_table.contains(hir_fn) {
+            let fn_sig = hir_fn.ty(self.db).callable_sig(self.db).unwrap();
 
             // Collect argument types
             for ty in fn_sig.params().iter() {
-                self.collect_type(self.db.type_info(ty.clone()));
+                self.collect_type(self.hir_types.type_info(ty));
             }
 
             // Collect return type
             let ret_ty = fn_sig.ret();
             if !ret_ty.is_empty() {
-                self.collect_type(self.db.type_info(ret_ty.clone()));
+                self.collect_type(self.hir_types.type_info(ret_ty));
             }
         }
 
         // Collect used types from body
-        let body = hir_fn.body(self.db.upcast());
-        let infer = hir_fn.infer(self.db.upcast());
+        let body = hir_fn.body(self.db);
+        let infer = hir_fn.infer(self.db);
         self.collect_expr(body.body_expr(), &body, &infer);
     }
 
     /// Collects unique `TypeInfo` from the specified struct type.
     pub fn collect_struct(&mut self, hir_struct: hir::Struct) {
-        let type_info = self.db.type_info(hir_struct.ty(self.db.upcast()));
+        let type_info = self.hir_types.type_info(&hir_struct.ty(self.db));
         self.entries.insert(type_info);
 
-        let fields = hir_struct.fields(self.db.upcast());
+        let fields = hir_struct.fields(self.db);
         for field in fields.into_iter() {
-            self.collect_type(self.db.type_info(field.ty(self.db.upcast())));
+            self.collect_type(self.hir_types.type_info(&field.ty(self.db)));
         }
     }
 
     fn gen_type_info(
         &self,
-        type_info_to_ir: &mut HashMap<TypeInfo, Value<*const ir::TypeInfo>>,
+        type_info_to_ir: &mut HashMap<TypeInfo, Value<'ink, *const ir::TypeInfo<'ink>>>,
         type_info: &TypeInfo,
-    ) -> Value<*const ir::TypeInfo> {
+    ) -> Value<'ink, *const ir::TypeInfo<'ink>> {
         // If there is already an entry, return that.
         if let Some(value) = type_info_to_ir.get(type_info) {
             return *value;
@@ -244,19 +257,19 @@ impl<'a, 'ctx, 'm> TypeTableBuilder<'a, 'ctx, 'm> {
 
     fn gen_struct_info(
         &self,
-        type_info_to_ir: &mut HashMap<TypeInfo, Value<*const ir::TypeInfo>>,
+        type_info_to_ir: &mut HashMap<TypeInfo, Value<'ink, *const ir::TypeInfo<'ink>>>,
         hir_struct: hir::Struct,
-    ) -> Value<ir::StructInfo> {
-        let struct_ir = self.db.struct_ty(hir_struct);
+    ) -> Value<'ink, ir::StructInfo<'ink>> {
+        let struct_ir = self.hir_types.get_struct_type(hir_struct);
         let name = hir_struct.name(self.db.upcast()).to_string();
-        let fields = hir_struct.fields(self.db.upcast());
+        let fields = hir_struct.fields(self.db);
 
         // Construct an array of field names (or null if there are no fields)
         let field_names = fields
             .iter()
             .enumerate()
             .map(|(idx, field)| {
-                CString::new(field.name(self.db.upcast()).to_string())
+                CString::new(field.name(self.db).to_string())
                     .expect("field name is not a valid CString")
                     .intern(
                         format!("struct_info::<{}>::field_names.{}", name, idx),
@@ -273,7 +286,7 @@ impl<'a, 'ctx, 'm> TypeTableBuilder<'a, 'ctx, 'm> {
         let field_types = fields
             .iter()
             .map(|field| {
-                let field_type_info = self.db.type_info(field.ty(self.db.upcast()));
+                let field_type_info = self.hir_types.type_info(&field.ty(self.db));
                 self.gen_type_info(type_info_to_ir, &field_type_info)
             })
             .into_const_private_pointer_or_null(
@@ -309,7 +322,7 @@ impl<'a, 'ctx, 'm> TypeTableBuilder<'a, 'ctx, 'm> {
     }
 
     /// Constructs a `TypeTable` from all *used* types.
-    pub fn build(mut self) -> TypeTable {
+    pub fn build(mut self) -> TypeTable<'ink> {
         let mut entries = BTreeSet::new();
         mem::swap(&mut entries, &mut self.entries);
 
@@ -317,7 +330,7 @@ impl<'a, 'ctx, 'm> TypeTableBuilder<'a, 'ctx, 'm> {
         let mut type_info_to_index = HashMap::with_capacity(entries.len());
 
         // Construct a list of all `ir::TypeInfo`s
-        let type_info_ptrs: Value<[*const ir::TypeInfo]> = entries
+        let type_info_ptrs: Value<'ink, [*const ir::TypeInfo]> = entries
             .into_iter()
             .enumerate()
             .map(|(index, type_info)| {
@@ -331,7 +344,7 @@ impl<'a, 'ctx, 'm> TypeTableBuilder<'a, 'ctx, 'm> {
 
         // If there are types, introduce a special global that contains all the TypeInfos
         if !type_info_ptrs.is_empty() {
-            let _: Global<[*const ir::TypeInfo]> = type_info_ptrs.into_global(
+            let _: Global<'ink, [*const ir::TypeInfo]> = type_info_ptrs.into_global(
                 TypeTable::NAME,
                 self.value_context,
                 true,
diff --git a/crates/mun_codegen/src/ir/types.rs b/crates/mun_codegen/src/ir/types.rs
index 47d22ed19..829cf23ad 100644
--- a/crates/mun_codegen/src/ir/types.rs
+++ b/crates/mun_codegen/src/ir/types.rs
@@ -1,103 +1,95 @@
 use crate::value::{AsValue, IrValueContext, SizedValueType, TransparentValue, Value};
 use mun_codegen_macros::AsValue;
 
-impl TransparentValue for abi::Guid {
+impl<'ink> TransparentValue<'ink> for abi::Guid {
     type Target = [u8; 16];
 
-    fn as_target_value(&self, context: &IrValueContext) -> Value<Self::Target> {
+    fn as_target_value(&self, context: &IrValueContext<'ink, '_, '_>) -> Value<'ink, Self::Target> {
         self.0.as_value(context)
     }
 }
 
-impl TransparentValue for abi::Privacy {
+impl<'ink> TransparentValue<'ink> for abi::Privacy {
     type Target = u8;
 
-    fn as_target_value(&self, context: &IrValueContext) -> Value<Self::Target> {
+    fn as_target_value(&self, context: &IrValueContext<'ink, '_, '_>) -> Value<'ink, Self::Target> {
         (*self as u8).as_value(context)
     }
 }
 
-impl TransparentValue for abi::TypeGroup {
+impl<'ink> TransparentValue<'ink> for abi::TypeGroup {
     type Target = u8;
 
-    fn as_target_value(&self, context: &IrValueContext) -> Value<Self::Target> {
+    fn as_target_value(&self, context: &IrValueContext<'ink, '_, '_>) -> Value<'ink, Self::Target> {
         (*self as u8).as_value(context)
     }
 }
 
-impl TransparentValue for abi::StructMemoryKind {
+impl<'ink> TransparentValue<'ink> for abi::StructMemoryKind {
     type Target = u8;
 
-    fn as_target_value(&self, context: &IrValueContext) -> Value<Self::Target> {
+    fn as_target_value(&self, context: &IrValueContext<'ink, '_, '_>) -> Value<'ink, Self::Target> {
         (self.clone() as u8).as_value(context)
     }
 }
 
 #[derive(AsValue)]
-#[ir_name = "struct.MunTypeInfo"]
-pub struct TypeInfo {
+pub struct TypeInfo<'ink> {
     pub guid: abi::Guid,
-    pub name: Value<*const u8>,
+    pub name: Value<'ink, *const u8>,
     pub size_in_bits: u32,
     pub alignment: u8,
     pub group: abi::TypeGroup,
 }
 
 #[derive(AsValue)]
-#[ir_name = "struct.MunFunctionSignature"]
-pub struct FunctionSignature {
-    pub arg_types: Value<*const *const TypeInfo>,
-    pub return_type: Value<*const TypeInfo>,
+pub struct FunctionSignature<'ink> {
+    pub arg_types: Value<'ink, *const *const TypeInfo<'ink>>,
+    pub return_type: Value<'ink, *const TypeInfo<'ink>>,
     pub num_arg_types: u16,
 }
 
 #[derive(AsValue)]
-#[ir_name = "struct.MunFunctionPrototype"]
-pub struct FunctionPrototype {
-    pub name: Value<*const u8>,
-    pub signature: FunctionSignature,
+pub struct FunctionPrototype<'ink> {
+    pub name: Value<'ink, *const u8>,
+    pub signature: FunctionSignature<'ink>,
 }
 
 #[derive(AsValue)]
-#[ir_name = "struct.MunFunctionDefinition"]
-pub struct FunctionDefinition {
-    pub prototype: FunctionPrototype,
-    pub fn_ptr: Value<*const fn()>,
+pub struct FunctionDefinition<'ink> {
+    pub prototype: FunctionPrototype<'ink>,
+    pub fn_ptr: Value<'ink, *const fn()>,
 }
 
 #[derive(AsValue)]
-#[ir_name = "struct.MunStructInfo"]
-pub struct StructInfo {
-    pub field_names: Value<*const *const u8>,
-    pub field_types: Value<*const *const TypeInfo>,
-    pub field_offsets: Value<*const u16>,
+pub struct StructInfo<'ink> {
+    pub field_names: Value<'ink, *const *const u8>,
+    pub field_types: Value<'ink, *const *const TypeInfo<'ink>>,
+    pub field_offsets: Value<'ink, *const u16>,
     pub num_fields: u16,
     pub memory_kind: abi::StructMemoryKind,
 }
 
 #[derive(AsValue)]
-#[ir_name = "struct.MunModuleInfo"]
-pub struct ModuleInfo {
-    pub path: Value<*const u8>,
-    pub functions: Value<*const FunctionDefinition>,
-    pub types: Value<*const *const TypeInfo>,
+pub struct ModuleInfo<'ink> {
+    pub path: Value<'ink, *const u8>,
+    pub functions: Value<'ink, *const FunctionDefinition<'ink>>,
+    pub types: Value<'ink, *const *const TypeInfo<'ink>>,
     pub num_functions: u32,
     pub num_types: u32,
 }
 
 #[derive(AsValue)]
-#[ir_name = "struct.MunDispatchTable"]
-pub struct DispatchTable {
-    pub prototypes: Value<*const FunctionPrototype>,
-    pub fn_ptrs: Value<*mut *const fn()>,
+pub struct DispatchTable<'ink> {
+    pub prototypes: Value<'ink, *const FunctionPrototype<'ink>>,
+    pub fn_ptrs: Value<'ink, *mut *const fn()>,
     pub num_entries: u32,
 }
 
 #[derive(AsValue)]
-#[ir_name = "struct.MunAssemblyInfo"]
-pub struct AssemblyInfo {
-    pub symbols: ModuleInfo,
-    pub dispatch_table: DispatchTable,
-    pub dependencies: Value<*const *const u8>,
+pub struct AssemblyInfo<'ink> {
+    pub symbols: ModuleInfo<'ink>,
+    pub dispatch_table: DispatchTable<'ink>,
+    pub dependencies: Value<'ink, *const *const u8>,
     pub num_dependencies: u32,
 }
diff --git a/crates/mun_codegen/src/lib.rs b/crates/mun_codegen/src/lib.rs
index f9e1c8720..3b57a3ae2 100644
--- a/crates/mun_codegen/src/lib.rs
+++ b/crates/mun_codegen/src/lib.rs
@@ -13,6 +13,7 @@ mod test;
 pub mod value;
 
 pub(crate) mod intrinsics;
+mod linker;
 pub(crate) mod type_info;
 
 pub use inkwell::{builder::Builder, context::Context, module::Module, OptimizationLevel};
@@ -20,12 +21,5 @@ pub use inkwell::{builder::Builder, context::Context, module::Module, Optimizati
 pub use crate::{
     assembly::Assembly,
     code_gen::ModuleBuilder,
-    db::{IrDatabase, IrDatabaseStorage},
+    db::{CodeGenDatabase, CodeGenDatabaseStorage},
 };
-
-#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
-pub struct CodeGenParams {
-    /// Whether generated code should support extern function calls.
-    /// This allows function parameters with `struct(value)` types to be marshalled.
-    make_marshallable: bool,
-}
diff --git a/crates/mun_codegen/src/code_gen/linker.rs b/crates/mun_codegen/src/linker.rs
similarity index 100%
rename from crates/mun_codegen/src/code_gen/linker.rs
rename to crates/mun_codegen/src/linker.rs
diff --git a/crates/mun_codegen/src/mock.rs b/crates/mun_codegen/src/mock.rs
index 85affa3b9..17e992f44 100644
--- a/crates/mun_codegen/src/mock.rs
+++ b/crates/mun_codegen/src/mock.rs
@@ -1,4 +1,7 @@
-use crate::{IrDatabase, OptimizationLevel};
+use crate::{
+    db::{CodeGenDatabase, CodeGenDatabaseStorage},
+    OptimizationLevel,
+};
 use hir::{FileId, RelativePathBuf, SourceDatabase, SourceRoot, SourceRootId};
 use parking_lot::Mutex;
 use std::sync::Arc;
@@ -8,7 +11,7 @@ use std::sync::Arc;
     hir::SourceDatabaseStorage,
     hir::DefDatabaseStorage,
     hir::HirDatabaseStorage,
-    crate::IrDatabaseStorage
+    CodeGenDatabaseStorage
 )]
 #[derive(Default)]
 pub(crate) struct MockDatabase {
@@ -43,6 +46,12 @@ impl hir::Upcast<dyn hir::HirDatabase> for MockDatabase {
     }
 }
 
+impl hir::Upcast<dyn CodeGenDatabase> for MockDatabase {
+    fn upcast(&self) -> &dyn CodeGenDatabase {
+        &*self
+    }
+}
+
 impl MockDatabase {
     /// Creates a database from the given text.
     pub fn with_single_file(text: &str) -> (MockDatabase, FileId) {
@@ -60,10 +69,8 @@ impl MockDatabase {
         source_root.insert_file(file_id);
 
         db.set_source_root(source_root_id, Arc::new(source_root));
-        db.set_optimization_lvl(OptimizationLevel::None);
+        db.set_optimization_level(OptimizationLevel::None);
 
-        let context = crate::Context::create();
-        db.set_context(Arc::new(context));
         (db, file_id)
     }
 
diff --git a/crates/mun_codegen/src/type_info.rs b/crates/mun_codegen/src/type_info.rs
index 3f2c69f21..cc4cf30da 100644
--- a/crates/mun_codegen/src/type_info.rs
+++ b/crates/mun_codegen/src/type_info.rs
@@ -1,6 +1,6 @@
 use super::ir::IsIrType;
-use crate::IrDatabase;
 use abi::Guid;
+use hir::HirDatabase;
 use inkwell::context::Context;
 use inkwell::targets::TargetData;
 use inkwell::types::AnyType;
@@ -46,7 +46,7 @@ pub struct TypeSize {
 }
 
 impl TypeSize {
-    pub fn from_ir_type(ty: &impl AnyType, target: &TargetData) -> Self {
+    pub fn from_ir_type<'ink>(ty: &impl AnyType<'ink>, target: &TargetData) -> Self {
         Self {
             bit_size: target.get_bit_size(ty),
             store_size: target.get_store_size(ty),
@@ -98,18 +98,18 @@ impl TypeInfo {
         }
     }
 
-    pub fn new_struct(db: &dyn IrDatabase, s: hir::Struct, type_size: TypeSize) -> TypeInfo {
+    pub fn new_struct(db: &dyn HirDatabase, s: hir::Struct, type_size: TypeSize) -> TypeInfo {
         let name = s.name(db.upcast()).to_string();
         let guid_string = {
             let fields: Vec<String> = s
-                .fields(db.upcast())
+                .fields(db)
                 .into_iter()
                 .map(|f| {
                     let ty_string = f
-                        .ty(db.upcast())
-                        .guid_string(db.upcast())
+                        .ty(db)
+                        .guid_string(db)
                         .expect("type should be convertible to a string");
-                    format!("{}: {}", f.name(db.upcast()).to_string(), ty_string)
+                    format!("{}: {}", f.name(db).to_string(), ty_string)
                 })
                 .collect();
 
@@ -180,7 +180,7 @@ impl_fundamental_static_type_info!(
 
 impl<T: HasStaticTypeName> HasStaticTypeInfo for *mut T {
     fn type_info(context: &Context, target: &TargetData) -> TypeInfo {
-        let ty = target.ptr_sized_int_type(None);
+        let ty = target.ptr_sized_int_type_in_context(context, None);
         TypeInfo::new_fundamental(
             format!("*mut {}", T::type_name(context, target)),
             TypeSize::from_ir_type(&ty, target),
@@ -193,7 +193,7 @@ impl<T: HasStaticTypeName> HasStaticTypeInfo for *const T {
         context: &inkwell::context::Context,
         target: &inkwell::targets::TargetData,
     ) -> TypeInfo {
-        let ty = target.ptr_sized_int_type(None);
+        let ty = target.ptr_sized_int_type_in_context(context, None);
         TypeInfo::new_fundamental(
             format!("*const {}", T::type_name(context, target)),
             TypeSize::from_ir_type(&ty, target),
diff --git a/crates/mun_codegen/src/value/array_value.rs b/crates/mun_codegen/src/value/array_value.rs
index be91ba643..3f4efca4d 100644
--- a/crates/mun_codegen/src/value/array_value.rs
+++ b/crates/mun_codegen/src/value/array_value.rs
@@ -6,18 +6,24 @@ use inkwell::types::{BasicType, PointerType};
 use inkwell::values::PointerValue;
 use inkwell::AddressSpace;
 
-impl<T: ConcreteValueType> ConcreteValueType for [T] {
-    type Value = inkwell::values::ArrayValue;
+impl<'ink, T: ConcreteValueType<'ink>> ConcreteValueType<'ink> for [T] {
+    type Value = inkwell::values::ArrayValue<'ink>;
 }
 
-impl<T: PointerValueType> PointerValueType for [T] {
-    fn get_ptr_type(context: &IrTypeContext, address_space: Option<AddressSpace>) -> PointerType {
+impl<'ink, T: PointerValueType<'ink>> PointerValueType<'ink> for [T] {
+    fn get_ptr_type(
+        context: &IrTypeContext<'ink, '_>,
+        address_space: Option<AddressSpace>,
+    ) -> PointerType<'ink> {
         T::get_ptr_type(context, address_space)
     }
 }
 
-impl<I, T: AddressableType<I>> AddressableType<I> for [T] {
-    fn ptr_cast(value: PointerValue, _context: &IrValueContext) -> PointerValue {
+impl<'ink, I, T: AddressableType<'ink, I>> AddressableType<'ink, I> for [T] {
+    fn ptr_cast(
+        value: PointerValue<'ink>,
+        _context: &IrValueContext<'ink, '_, '_>,
+    ) -> PointerValue<'ink> {
         let ptr_type = value
             .get_type()
             .get_element_type()
@@ -31,49 +37,49 @@ impl<I, T: AddressableType<I>> AddressableType<I> for [T] {
 macro_rules! impl_array(
     ($($size:expr),+) => {
         $(
-            impl<T: SizedValueType> ConcreteValueType for [T; $size] {
-                type Value = inkwell::values::ArrayValue;
+            impl<'ink, T: SizedValueType<'ink>> ConcreteValueType<'ink> for [T; $size] {
+                type Value = inkwell::values::ArrayValue<'ink>;
             }
 
-            impl<T: SizedValueType> SizedValueType for [T; $size]
+            impl<'ink, T: SizedValueType<'ink>> SizedValueType<'ink> for [T; $size]
             where
-                <<T as ConcreteValueType>::Value as ValueType>::Type: ConstArrayType,
+                <<T as ConcreteValueType<'ink>>::Value as ValueType<'ink>>::Type: ConstArrayType<'ink>,
             {
-                fn get_ir_type(context: &IrTypeContext) -> inkwell::types::ArrayType {
+                fn get_ir_type(context: &IrTypeContext<'ink, '_>) -> inkwell::types::ArrayType<'ink> {
                     T::get_ir_type(context).array_type($size)
                 }
             }
 
-            impl<T: PointerValueType + SizedValueType> PointerValueType for [T; $size] {
-                fn get_ptr_type(context: &IrTypeContext, address_space: Option<AddressSpace>) -> PointerType {
+            impl<'ink, T: PointerValueType<'ink> + SizedValueType<'ink>> PointerValueType<'ink> for [T; $size] {
+                fn get_ptr_type(context: &IrTypeContext<'ink, '_>, address_space: Option<AddressSpace>) -> PointerType<'ink> {
                     T::get_ptr_type(context, address_space)
                 }
             }
 
-            impl<E: SizedValueType, T: AsValue<E>> AsValue<[E; $size]> for [T; $size]
+            impl<'ink, E: SizedValueType<'ink>, T: AsValue<'ink, E>> AsValue<'ink, [E; $size]> for [T; $size]
             where
-                E::Value: ConstArrayValue
+                E::Value: ConstArrayValue<'ink>
             {
-                fn as_value(&self, context: &IrValueContext) -> Value<[E; $size]> {
+                fn as_value(&self, context: &IrValueContext<'ink, '_, '_>) -> Value<'ink, [E; $size]> {
                     let element_ir_type = E::get_ir_type(context.type_context);
                     let values: [E::Value; $size] =
                         array_init::from_iter(self.iter().map(|e| e.as_value(context).value))
                             .expect("unable to construct sized array");
-                    let value = E::Value::const_array(values.as_ref(), &element_ir_type);
+                    let value = E::Value::const_array(values.as_ref(), element_ir_type);
                     Value::from_raw(value)
                 }
             }
 
-            impl<E: SizedValueType, T: AsValue<E>> AsValue<[E]> for [T; $size]
+            impl<'ink, E: SizedValueType<'ink>, T: AsValue<'ink, E>> AsValue<'ink, [E]> for [T; $size]
                 where
-                    E::Value: ConstArrayValue
+                    E::Value: ConstArrayValue<'ink>
             {
-                fn as_value(&self, context: &IrValueContext) -> Value<[E]> {
+                fn as_value(&self, context: &IrValueContext<'ink, '_, '_>) -> Value<'ink, [E]> {
                     let element_ir_type = E::get_ir_type(context.type_context);
                     let values: [E::Value; $size] =
                         array_init::from_iter(self.iter().map(|e| e.as_value(context).value))
                             .expect("unable to construct sized array");
-                    let value = E::Value::const_array(values.as_ref(), &element_ir_type);
+                    let value = E::Value::const_array(values.as_ref(), element_ir_type);
                     Value::from_raw(value)
                 }
             }
@@ -85,32 +91,34 @@ impl_array!(
     0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20, 24, 32, 36, 0x40, 0x80, 0x100
 );
 
-impl<E: SizedValueType, T: AsValue<E>> AsValue<[E]> for &[T]
+impl<'ink, E: SizedValueType<'ink>, T: AsValue<'ink, E>> AsValue<'ink, [E]> for &[T]
 where
-    E::Value: ConstArrayValue,
+    E::Value: ConstArrayValue<'ink>,
 {
-    fn as_value(&self, context: &IrValueContext) -> Value<[E]> {
+    fn as_value(&self, context: &IrValueContext<'ink, '_, '_>) -> Value<'ink, [E]> {
         let element_type = E::get_ir_type(context.type_context);
         let elements: Vec<E::Value> = self.iter().map(|v| v.as_value(context).value).collect();
-        let value = ConstArrayValue::const_array(&elements, &element_type);
+        let value = ConstArrayValue::const_array(&elements, element_type);
         Value::from_raw(value)
     }
 }
 
-pub trait IterAsIrValue<E: SizedValueType, T: AsValue<E>>: IntoIterator<Item = T> {
+pub trait IterAsIrValue<'ink, E: SizedValueType<'ink>, T: AsValue<'ink, E>>:
+    IntoIterator<Item = T>
+{
     /// Returns a `Value<[E]>` that contains all values converted to `Value<E>`.
-    fn as_value(self, context: &IrValueContext) -> Value<[E]>;
+    fn as_value(self, context: &IrValueContext<'ink, '_, '_>) -> Value<'ink, [E]>;
 
     /// Constructs a const private global and returns a pointer to it.
     fn into_const_private_pointer<S: AsRef<str>>(
         self,
         name: S,
-        context: &IrValueContext,
-    ) -> Value<*const E>
+        context: &IrValueContext<'ink, '_, '_>,
+    ) -> Value<'ink, *const E>
     where
-        *const E: ConcreteValueType<Value = inkwell::values::PointerValue>,
-        E: AddressableType<E>,
-        E: PointerValueType,
+        *const E: ConcreteValueType<'ink, Value = inkwell::values::PointerValue<'ink>>,
+        E: AddressableType<'ink, E>,
+        E: PointerValueType<'ink>,
         Self: Sized,
     {
         self.as_value(context)
@@ -123,14 +131,14 @@ pub trait IterAsIrValue<E: SizedValueType, T: AsValue<E>>: IntoIterator<Item = T
     fn into_const_private_pointer_or_null<S: AsRef<str>>(
         self,
         name: S,
-        context: &IrValueContext,
-    ) -> Value<*const E>
+        context: &IrValueContext<'ink, '_, '_>,
+    ) -> Value<'ink, *const E>
     where
-        *const E: SizedValueType<Value = inkwell::values::PointerValue>,
-        E: AddressableType<E>,
-        E: PointerValueType,
+        *const E: SizedValueType<'ink, Value = inkwell::values::PointerValue<'ink>>,
+        E: AddressableType<'ink, E>,
+        E: PointerValueType<'ink>,
         Self: Sized,
-        E::Value: ConstArrayValue,
+        E::Value: ConstArrayValue<'ink>,
     {
         let mut iter = self.into_iter().peekable();
         if iter.peek().is_some() {
@@ -143,11 +151,12 @@ pub trait IterAsIrValue<E: SizedValueType, T: AsValue<E>>: IntoIterator<Item = T
     }
 }
 
-impl<E: SizedValueType, T: AsValue<E>, I: IntoIterator<Item = T>> IterAsIrValue<E, T> for I
+impl<'ink, E: SizedValueType<'ink>, T: AsValue<'ink, E>, I: IntoIterator<Item = T>>
+    IterAsIrValue<'ink, E, T> for I
 where
-    E::Value: ConstArrayValue,
+    E::Value: ConstArrayValue<'ink>,
 {
-    fn as_value(self, context: &IrValueContext) -> Value<[E]> {
+    fn as_value(self, context: &IrValueContext<'ink, '_, '_>) -> Value<'ink, [E]> {
         let element_type = E::get_ir_type(context.type_context);
         // eprintln!("constructing array of type {:?}", element_type);
         let elements: Vec<E::Value> = self
@@ -155,23 +164,28 @@ where
             .map(|v| v.as_value(context).value)
             // .map(|v| { eprintln!("- type {:?}", v.get_type()); v})
             .collect();
-        let value = ConstArrayValue::const_array(&elements, &element_type);
+        let value = ConstArrayValue::const_array(&elements, element_type);
         // eprintln!("done, {} elements", elements.len());
         Value::from_raw(value)
     }
 }
 
 /// A helper trait that enables the creation of a const LLVM array for a type.
-pub trait ConstArrayType: BasicType + Sized + TypeValue {
-    fn const_array(&self, values: &[<Self as TypeValue>::Value]) -> inkwell::values::ArrayValue;
+pub trait ConstArrayType<'ink>: BasicType<'ink> + Sized + TypeValue<'ink> {
+    fn const_array(
+        self,
+        values: &[<Self as TypeValue<'ink>>::Value],
+    ) -> inkwell::values::ArrayValue<'ink>;
 }
 
 /// A helper trait that enables the creation of a const LLVM array for a type.
-pub trait ConstArrayValue: ValueType {
-    fn const_array(values: &[Self], ir_type: &Self::Type) -> inkwell::values::ArrayValue;
+pub trait ConstArrayValue<'ink>: ValueType<'ink> {
+    fn const_array(values: &[Self], ir_type: Self::Type) -> inkwell::values::ArrayValue<'ink>;
 }
 
-impl<T: ConcreteValueType<Value = inkwell::values::ArrayValue> + ?Sized> Value<T> {
+impl<'ink, T: ConcreteValueType<'ink, Value = inkwell::values::ArrayValue<'ink>> + ?Sized>
+    Value<'ink, T>
+{
     /// Returns the number of elements in the array.
     pub fn len(self) -> usize {
         self.value.get_type().len() as usize
@@ -186,15 +200,15 @@ impl<T: ConcreteValueType<Value = inkwell::values::ArrayValue> + ?Sized> Value<T
 macro_rules! impl_array_type {
     ($($inkwell_type:ty => $inkwell_value:ty),+) => {
         $(
-            impl ConstArrayType for $inkwell_type {
-                fn const_array(&self, values: &[<Self as TypeValue>::Value]) -> inkwell::values::ArrayValue {
-                    <$inkwell_type>::const_array(self, values)
+            impl<'ink> ConstArrayType<'ink> for $inkwell_type {
+                fn const_array(self, values: &[<Self as TypeValue<'ink>>::Value]) -> inkwell::values::ArrayValue<'ink> {
+                    <$inkwell_type>::const_array(&self, values)
                 }
             }
 
-            impl ConstArrayValue for $inkwell_value {
-               fn const_array(values: &[Self], ir_type: &Self::Type) -> inkwell::values::ArrayValue {
-                    Self::Type::const_array(ir_type, values)
+            impl<'ink> ConstArrayValue<'ink> for $inkwell_value {
+               fn const_array(values: &[Self], ir_type: Self::Type) -> inkwell::values::ArrayValue<'ink> {
+                    Self::Type::const_array(&ir_type, values)
                }
             }
         )*
@@ -202,10 +216,10 @@ macro_rules! impl_array_type {
 }
 
 impl_array_type!(
-    inkwell::types::IntType => inkwell::values::IntValue,
-    inkwell::types::FloatType => inkwell::values::FloatValue,
-    inkwell::types::ArrayType => inkwell::values::ArrayValue,
-    inkwell::types::VectorType => inkwell::values::VectorValue,
-    inkwell::types::StructType => inkwell::values::StructValue,
-    inkwell::types::PointerType => inkwell::values::PointerValue
+    inkwell::types::IntType<'ink> => inkwell::values::IntValue<'ink>,
+    inkwell::types::FloatType<'ink> => inkwell::values::FloatValue<'ink>,
+    inkwell::types::ArrayType<'ink> => inkwell::values::ArrayValue<'ink>,
+    inkwell::types::VectorType<'ink> => inkwell::values::VectorValue<'ink>,
+    inkwell::types::StructType<'ink> => inkwell::values::StructValue<'ink>,
+    inkwell::types::PointerType<'ink> => inkwell::values::PointerValue<'ink>
 );
diff --git a/crates/mun_codegen/src/value/float_value.rs b/crates/mun_codegen/src/value/float_value.rs
index 320d542cc..48e2f8125 100644
--- a/crates/mun_codegen/src/value/float_value.rs
+++ b/crates/mun_codegen/src/value/float_value.rs
@@ -5,48 +5,54 @@ use super::{
 use crate::value::AddressableType;
 use inkwell::{types::PointerType, AddressSpace};
 
-impl ConcreteValueType for f32 {
-    type Value = inkwell::values::FloatValue;
+impl<'ink> ConcreteValueType<'ink> for f32 {
+    type Value = inkwell::values::FloatValue<'ink>;
 }
 
-impl ConcreteValueType for f64 {
-    type Value = inkwell::values::FloatValue;
+impl<'ink> ConcreteValueType<'ink> for f64 {
+    type Value = inkwell::values::FloatValue<'ink>;
 }
 
-impl SizedValueType for f32 {
-    fn get_ir_type(context: &IrTypeContext) -> inkwell::types::FloatType {
+impl<'ink> SizedValueType<'ink> for f32 {
+    fn get_ir_type(context: &IrTypeContext<'ink, '_>) -> inkwell::types::FloatType<'ink> {
         context.context.f32_type()
     }
 }
-impl SizedValueType for f64 {
-    fn get_ir_type(context: &IrTypeContext) -> inkwell::types::FloatType {
+impl<'ink> SizedValueType<'ink> for f64 {
+    fn get_ir_type(context: &IrTypeContext<'ink, '_>) -> inkwell::types::FloatType<'ink> {
         context.context.f64_type()
     }
 }
 
-impl PointerValueType for f32 {
-    fn get_ptr_type(context: &IrTypeContext, address_space: Option<AddressSpace>) -> PointerType {
+impl<'ink> PointerValueType<'ink> for f32 {
+    fn get_ptr_type(
+        context: &IrTypeContext<'ink, '_>,
+        address_space: Option<AddressSpace>,
+    ) -> PointerType<'ink> {
         Self::get_ir_type(context).ptr_type(address_space.unwrap_or(AddressSpace::Generic))
     }
 }
-impl PointerValueType for f64 {
-    fn get_ptr_type(context: &IrTypeContext, address_space: Option<AddressSpace>) -> PointerType {
+impl<'ink> PointerValueType<'ink> for f64 {
+    fn get_ptr_type(
+        context: &IrTypeContext<'ink, '_>,
+        address_space: Option<AddressSpace>,
+    ) -> PointerType<'ink> {
         Self::get_ir_type(context).ptr_type(address_space.unwrap_or(AddressSpace::Generic))
     }
 }
 
-impl AddressableType<f32> for f32 {}
-impl AddressableType<f64> for f64 {}
+impl<'ink> AddressableType<'ink, f32> for f32 {}
+impl<'ink> AddressableType<'ink, f64> for f64 {}
 
-impl AsValue<f32> for f32 {
-    fn as_value(&self, context: &IrValueContext) -> Value<f32> {
+impl<'ink> AsValue<'ink, f32> for f32 {
+    fn as_value(&self, context: &IrValueContext<'ink, '_, '_>) -> Value<'ink, f32> {
         Value::from_raw(
             <Self as SizedValueType>::get_ir_type(context.type_context).const_float(*self as f64),
         )
     }
 }
-impl AsValue<f64> for f64 {
-    fn as_value(&self, context: &IrValueContext) -> Value<f64> {
+impl<'ink> AsValue<'ink, f64> for f64 {
+    fn as_value(&self, context: &IrValueContext<'ink, '_, '_>) -> Value<'ink, f64> {
         Value::from_raw(
             <Self as SizedValueType>::get_ir_type(context.type_context).const_float(*self),
         )
diff --git a/crates/mun_codegen/src/value/function_value.rs b/crates/mun_codegen/src/value/function_value.rs
index c619b58c3..b201a6b57 100644
--- a/crates/mun_codegen/src/value/function_value.rs
+++ b/crates/mun_codegen/src/value/function_value.rs
@@ -6,20 +6,20 @@ macro_rules! into_function_info_impl {
         fn($($T:ident),*) -> $R:ident;
     )+) => {
         $(
-            impl<$R:ConcreteValueType, $($T:ConcreteValueType,)*> ConcreteValueType for fn($($T,)*) -> $R {
-                type Value = inkwell::values::FunctionValue;
+            impl<'ink, $R:ConcreteValueType<'ink>, $($T:ConcreteValueType<'ink>,)*> ConcreteValueType<'ink> for fn($($T,)*) -> $R {
+                type Value = inkwell::values::FunctionValue<'ink>;
             }
-            impl<$R:SizedValueType + 'static, $($T:SizedValueType,)*> SizedValueType for fn($($T,)*) -> $R
+            impl<'ink, $R:SizedValueType<'ink> + 'ink, $($T:SizedValueType<'ink>,)*> SizedValueType<'ink> for fn($($T,)*) -> $R
             where
-                <<R as ConcreteValueType>::Value as ValueType>::Type: inkwell::types::BasicType,
+                <<R as ConcreteValueType<'ink>>::Value as ValueType<'ink>>::Type: inkwell::types::BasicType<'ink>,
                 $(
-                    <$T::Value as ValueType>::Type: inkwell::types::BasicType
+                    <$T::Value as ValueType<'ink>>::Type: inkwell::types::BasicType<'ink>
                 ),*
             {
-                fn get_ir_type(context: &IrTypeContext) -> inkwell::types::FunctionType {
+                fn get_ir_type(context: &IrTypeContext<'ink, '_>) -> inkwell::types::FunctionType<'ink> {
                     // This is a bit of a dirty hack. The problem is that in this specific case we
                     // want the type () to be represented as void in LLVM.
-                    if std::any::TypeId::of::<$R>() == std::any::TypeId::of::<()>() {
+                    if std::any::type_name::<$R>() == std::any::type_name::<()>() {
                         context.context.void_type().fn_type(
                             &[
                                 $(
@@ -41,17 +41,17 @@ macro_rules! into_function_info_impl {
                 }
             }
 
-            impl<$R:SizedValueType + 'static, $($T:SizedValueType,)*> PointerValueType for fn($($T,)*) -> $R
+            impl<'ink, $R:SizedValueType<'ink> + 'ink, $($T:SizedValueType<'ink>,)*> PointerValueType<'ink> for fn($($T,)*) -> $R
             where
-                <<R as ConcreteValueType>::Value as ValueType>::Type: inkwell::types::BasicType,
+                <<R as ConcreteValueType<'ink>>::Value as ValueType<'ink>>::Type: inkwell::types::BasicType<'ink>,
                 $(
-                    <$T::Value as ValueType>::Type: inkwell::types::BasicType
+                    <$T::Value as ValueType<'ink>>::Type: inkwell::types::BasicType<'ink>
                 ),*
             {
                 fn get_ptr_type(
-                    context: &IrTypeContext,
+                    context: &IrTypeContext<'ink, '_>,
                     address_space: Option<inkwell::AddressSpace>,
-                ) -> inkwell::types::PointerType
+                ) -> inkwell::types::PointerType<'ink>
                 {
                     debug_assert!(
                         address_space.is_none() || address_space == Some(inkwell::AddressSpace::Generic),
diff --git a/crates/mun_codegen/src/value/global.rs b/crates/mun_codegen/src/value/global.rs
index e59565c82..ee25adff4 100644
--- a/crates/mun_codegen/src/value/global.rs
+++ b/crates/mun_codegen/src/value/global.rs
@@ -17,12 +17,12 @@ use std::marker::PhantomData;
 /// Globals can be used to store data inside an inkwell context which can be referenced from code.
 ///
 /// Like `Value<T>` a `Global<T>` is typed on the type of data that it stores.
-pub struct Global<T: ?Sized> {
-    pub value: inkwell::values::GlobalValue,
+pub struct Global<'ink, T: ?Sized> {
+    pub value: inkwell::values::GlobalValue<'ink>,
     data: PhantomData<T>,
 }
 
-impl<T: ?Sized> Clone for Global<T> {
+impl<'ink, T: ?Sized> Clone for Global<'ink, T> {
     fn clone(&self) -> Self {
         Global {
             value: self.value,
@@ -31,9 +31,9 @@ impl<T: ?Sized> Clone for Global<T> {
     }
 }
 
-impl<T: ?Sized> Copy for Global<T> {}
+impl<'ink, T: ?Sized> Copy for Global<'ink, T> {}
 
-impl<T: ?Sized> Global<T> {
+impl<'ink, T: ?Sized> Global<'ink, T> {
     /// Creates a `Global<T>` from an underlying value.
     ///
     /// # Safety
@@ -41,7 +41,7 @@ impl<T: ?Sized> Global<T> {
     /// There is no guarantee that the passed value actually represents the type `T`. Sometimes this
     /// can however be very useful. This method is marked as unsafe since there is also no way to
     /// check the correctness.
-    pub unsafe fn from_raw(value: inkwell::values::GlobalValue) -> Self {
+    pub unsafe fn from_raw(value: inkwell::values::GlobalValue<'ink>) -> Self {
         Global {
             value,
             data: Default::default(),
@@ -49,18 +49,21 @@ impl<T: ?Sized> Global<T> {
     }
 }
 
-impl<T: ?Sized> ConcreteValueType for *const Global<T> {
-    type Value = inkwell::values::PointerValue;
+impl<'ink, T: ?Sized> ConcreteValueType<'ink> for *const Global<'ink, T> {
+    type Value = inkwell::values::PointerValue<'ink>;
 }
 
-impl<T: PointerValueType + ?Sized> SizedValueType for *const Global<T> {
-    fn get_ir_type(context: &IrTypeContext) -> <Self::Value as ValueType>::Type {
+impl<'ink, T: PointerValueType<'ink> + ?Sized> SizedValueType<'ink> for *const Global<'ink, T> {
+    fn get_ir_type(context: &IrTypeContext<'ink, '_>) -> <Self::Value as ValueType<'ink>>::Type {
         T::get_ptr_type(context, None)
     }
 }
 
-impl<T: PointerValueType + ?Sized> PointerValueType for *const Global<T> {
-    fn get_ptr_type(context: &IrTypeContext, address_space: Option<AddressSpace>) -> PointerType {
+impl<'ink, T: PointerValueType<'ink> + ?Sized> PointerValueType<'ink> for *const Global<'ink, T> {
+    fn get_ptr_type(
+        context: &IrTypeContext<'ink, '_>,
+        address_space: Option<AddressSpace>,
+    ) -> PointerType<'ink> {
         debug_assert!(
             address_space.is_none() || address_space == Some(AddressSpace::Generic),
             "Globals can only live in generic address space"
@@ -69,28 +72,28 @@ impl<T: PointerValueType + ?Sized> PointerValueType for *const Global<T> {
     }
 }
 
-impl<T: ?Sized, I> AsValue<*const I> for Global<T>
+impl<'ink, T: ?Sized, I> AsValue<'ink, *const I> for Global<'ink, T>
 where
-    *const I: ConcreteValueType<Value = inkwell::values::PointerValue>,
-    T: AddressableType<I>,
+    *const I: ConcreteValueType<'ink, Value = inkwell::values::PointerValue<'ink>>,
+    T: AddressableType<'ink, I>,
 {
-    fn as_value(&self, context: &IrValueContext) -> Value<*const I> {
+    fn as_value(&self, context: &IrValueContext<'ink, '_, '_>) -> Value<'ink, *const I> {
         Value::from_raw(T::ptr_cast(self.value.as_pointer_value(), context))
     }
 }
 
-impl<T: ConcreteValueType + ?Sized> Value<T>
+impl<'ink, T: ConcreteValueType<'ink> + ?Sized> Value<'ink, T>
 where
-    T::Value: Into<BasicValueEnum>,
+    T::Value: Into<BasicValueEnum<'ink>>,
 {
     pub fn into_global<S: AsRef<str>>(
         self,
         name: S,
-        context: &IrValueContext,
+        context: &IrValueContext<'ink, '_, '_>,
         is_const: bool,
         linkage: Linkage,
         unnamed_addr: Option<UnnamedAddress>,
-    ) -> Global<T> {
+    ) -> Global<'ink, T> {
         // NOTE: No support for address spaces
         let address_space = None;
 
@@ -130,8 +133,8 @@ where
     pub fn into_const_private_global<S: AsRef<str>>(
         self,
         name: S,
-        context: &IrValueContext,
-    ) -> Global<T> {
+        context: &IrValueContext<'ink, '_, '_>,
+    ) -> Global<'ink, T> {
         self.into_global(
             name,
             context,
@@ -142,8 +145,8 @@ where
     }
 }
 
-impl<T: ?Sized> Into<inkwell::values::PointerValue> for Global<T> {
-    fn into(self) -> PointerValue {
+impl<'ink, T: ?Sized> Into<inkwell::values::PointerValue<'ink>> for Global<'ink, T> {
+    fn into(self) -> PointerValue<'ink> {
         self.value.as_pointer_value()
     }
 }
diff --git a/crates/mun_codegen/src/value/int_value.rs b/crates/mun_codegen/src/value/int_value.rs
index 9527f34cc..9d93e9fee 100644
--- a/crates/mun_codegen/src/value/int_value.rs
+++ b/crates/mun_codegen/src/value/int_value.rs
@@ -7,23 +7,23 @@ use inkwell::AddressSpace;
 macro_rules! impl_as_int_ir_value {
     ($($ty:ty => $context_fun:ident()),*) => {
         $(
-            impl ConcreteValueType for $ty {
-                type Value = inkwell::values::IntValue;
+            impl<'ink> ConcreteValueType<'ink> for $ty {
+                type Value = inkwell::values::IntValue<'ink>;
             }
 
-            impl SizedValueType for $ty {
-                fn get_ir_type(context: &IrTypeContext) -> inkwell::types::IntType {
+            impl<'ink> SizedValueType<'ink> for $ty {
+                fn get_ir_type(context: &IrTypeContext<'ink, '_>) -> inkwell::types::IntType<'ink> {
                     context.context.$context_fun()
                 }
             }
 
-            impl PointerValueType for $ty {
-                fn get_ptr_type(context: &IrTypeContext, address_space: Option<AddressSpace>) -> inkwell::types::PointerType {
+            impl<'ink> PointerValueType<'ink> for $ty {
+                fn get_ptr_type(context: &IrTypeContext<'ink, '_>, address_space: Option<AddressSpace>) -> inkwell::types::PointerType<'ink> {
                     Self::get_ir_type(context).ptr_type(address_space.unwrap_or(AddressSpace::Generic))
                 }
             }
 
-            impl AddressableType<$ty> for $ty {}
+            impl<'ink> AddressableType<'ink, $ty> for $ty {}
         )*
     }
 }
@@ -39,8 +39,8 @@ impl_as_int_ir_value!(
     u64 => i64_type()
 );
 
-impl AsValue<u8> for u8 {
-    fn as_value(&self, context: &IrValueContext) -> Value<u8> {
+impl<'ink> AsValue<'ink, u8> for u8 {
+    fn as_value(&self, context: &IrValueContext<'ink, '_, '_>) -> Value<'ink, u8> {
         Value::from_raw(
             <Self as SizedValueType>::get_ir_type(context.type_context)
                 .const_int(*self as u64, false),
@@ -48,8 +48,8 @@ impl AsValue<u8> for u8 {
     }
 }
 
-impl AsValue<u16> for u16 {
-    fn as_value(&self, context: &IrValueContext) -> Value<u16> {
+impl<'ink> AsValue<'ink, u16> for u16 {
+    fn as_value(&self, context: &IrValueContext<'ink, '_, '_>) -> Value<'ink, u16> {
         Value::from_raw(
             <Self as SizedValueType>::get_ir_type(context.type_context)
                 .const_int(*self as u64, false),
@@ -57,8 +57,8 @@ impl AsValue<u16> for u16 {
     }
 }
 
-impl AsValue<u32> for u32 {
-    fn as_value(&self, context: &IrValueContext) -> Value<u32> {
+impl<'ink> AsValue<'ink, u32> for u32 {
+    fn as_value(&self, context: &IrValueContext<'ink, '_, '_>) -> Value<'ink, u32> {
         Value::from_raw(
             <Self as SizedValueType>::get_ir_type(context.type_context)
                 .const_int(*self as u64, false),
@@ -66,8 +66,8 @@ impl AsValue<u32> for u32 {
     }
 }
 
-impl AsValue<u64> for u64 {
-    fn as_value(&self, context: &IrValueContext) -> Value<u64> {
+impl<'ink> AsValue<'ink, u64> for u64 {
+    fn as_value(&self, context: &IrValueContext<'ink, '_, '_>) -> Value<'ink, u64> {
         Value::from_raw(
             <Self as SizedValueType>::get_ir_type(context.type_context)
                 .const_int(*self as u64, false),
@@ -75,8 +75,8 @@ impl AsValue<u64> for u64 {
     }
 }
 
-impl AsValue<i8> for i8 {
-    fn as_value(&self, context: &IrValueContext) -> Value<i8> {
+impl<'ink> AsValue<'ink, i8> for i8 {
+    fn as_value(&self, context: &IrValueContext<'ink, '_, '_>) -> Value<'ink, i8> {
         Value::from_raw(
             <Self as SizedValueType>::get_ir_type(context.type_context)
                 .const_int(*self as u64, true),
@@ -84,8 +84,8 @@ impl AsValue<i8> for i8 {
     }
 }
 
-impl AsValue<i16> for i16 {
-    fn as_value(&self, context: &IrValueContext) -> Value<i16> {
+impl<'ink> AsValue<'ink, i16> for i16 {
+    fn as_value(&self, context: &IrValueContext<'ink, '_, '_>) -> Value<'ink, i16> {
         Value::from_raw(
             <Self as SizedValueType>::get_ir_type(context.type_context)
                 .const_int(*self as u64, true),
@@ -93,8 +93,8 @@ impl AsValue<i16> for i16 {
     }
 }
 
-impl AsValue<i32> for i32 {
-    fn as_value(&self, context: &IrValueContext) -> Value<i32> {
+impl<'ink> AsValue<'ink, i32> for i32 {
+    fn as_value(&self, context: &IrValueContext<'ink, '_, '_>) -> Value<'ink, i32> {
         Value::from_raw(
             <Self as SizedValueType>::get_ir_type(context.type_context)
                 .const_int(*self as u64, true),
@@ -102,8 +102,8 @@ impl AsValue<i32> for i32 {
     }
 }
 
-impl AsValue<i64> for i64 {
-    fn as_value(&self, context: &IrValueContext) -> Value<i64> {
+impl<'ink> AsValue<'ink, i64> for i64 {
+    fn as_value(&self, context: &IrValueContext<'ink, '_, '_>) -> Value<'ink, i64> {
         Value::from_raw(
             <Self as SizedValueType>::get_ir_type(context.type_context)
                 .const_int(*self as u64, true),
diff --git a/crates/mun_codegen/src/value/mod.rs b/crates/mun_codegen/src/value/mod.rs
index 06ab4b7a9..0ff0ca748 100644
--- a/crates/mun_codegen/src/value/mod.rs
+++ b/crates/mun_codegen/src/value/mod.rs
@@ -19,8 +19,7 @@ pub use string::CanInternalize;
 use inkwell::types::{BasicTypeEnum, PointerType, StructType};
 use inkwell::values::{BasicValueEnum, PointerValue};
 use inkwell::AddressSpace;
-use parking_lot::RwLock;
-use std::any::TypeId;
+use std::cell::RefCell;
 use std::collections::HashMap;
 use std::fmt::Debug;
 use std::hash::Hash;
@@ -44,10 +43,10 @@ use std::hash::Hash;
 ///      bar: f32,
 ///   }
 ///
-///   impl TransparentValue for Foo {
+///   impl<'ink> TransparentValue<'ink> for Foo {
 ///       type Target = (u32, f32);
 ///
-///       fn as_target_value(&self, context: &IrValueContext) -> Value<Self::Target> {
+///       fn as_target_value(&self, context: &IrValueContext<'ink, '_, '_>) -> Value<'ink, Self::Target> {
 ///           (self.value, self.bar).as_value(context)
 ///       }
 ///   }
@@ -60,7 +59,6 @@ use std::hash::Hash;
 ///   #[macro_use] extern crate mun_codegen_macros;
 ///
 ///   #[derive(AsValue)]
-///   #[ir_name = "Foo"]
 ///   struct Foo {
 ///       value: u32,
 ///       bar: f32
@@ -72,102 +70,105 @@ use std::hash::Hash;
 /// - You can also add completely custom support by implementing the [`ConcreteValueType`] and
 ///   [`AsValue`] traits. Optionally you might also want to implement the [`SizedValueType`] and
 ///   [`PointerValueType`] traits.
-pub struct Value<T: ConcreteValueType + ?Sized> {
+pub struct Value<'ink, T: ConcreteValueType<'ink> + ?Sized> {
     pub value: T::Value,
 }
 
 /// When implemented enables the conversion from a value to a `Value<T>`.
-pub trait AsValue<T: ConcreteValueType + ?Sized> {
+pub trait AsValue<'ink, T: ConcreteValueType<'ink> + ?Sized> {
     /// Creates a `Value<T>` from an instance.
-    fn as_value(&self, context: &IrValueContext) -> Value<T>;
+    fn as_value(&self, context: &IrValueContext<'ink, '_, '_>) -> Value<'ink, T>;
 }
 
 /// A `TransparentValue` is something that can be represented as a `Value<T>` but which is actually
 /// a rewritten version of another type.
-pub trait TransparentValue {
-    type Target: ConcreteValueType + ?Sized;
+pub trait TransparentValue<'ink> {
+    type Target: ConcreteValueType<'ink> + ?Sized;
 
     /// Converts the instance to the target value
-    fn as_target_value(&self, context: &IrValueContext) -> Value<Self::Target>;
+    fn as_target_value(&self, context: &IrValueContext<'ink, '_, '_>) -> Value<'ink, Self::Target>;
 }
 
 /// The context in which an `IrType` operates.
-pub struct IrTypeContext<'ctx> {
-    pub context: &'ctx Context,
-    pub target_data: &'ctx TargetData,
-    pub struct_types: &'ctx RwLock<HashMap<(&'static str, TypeId), StructType>>,
+pub struct IrTypeContext<'ink, 'a> {
+    pub context: &'ink Context,
+    pub target_data: &'a TargetData,
+    pub struct_types: &'a RefCell<HashMap<&'static str, StructType<'ink>>>,
 }
 
 /// The context in which an `IrValue` exists.
-pub struct IrValueContext<'t, 'ctx, 'm> {
-    pub type_context: &'t IrTypeContext<'ctx>,
-    pub context: &'ctx Context,
-    pub module: &'m Module,
+pub struct IrValueContext<'ink, 'a, 'b> {
+    pub context: &'ink Context,
+    pub type_context: &'b IrTypeContext<'ink, 'a>,
+    pub module: &'b Module<'ink>,
 }
 
 /// A trait that represents that a concrete type can be used as `Value<T>` type generic. A type must
 /// implement this trait to be able to be represented as a `Value<Self>`.
-pub trait ConcreteValueType {
-    type Value: ValueType;
+pub trait ConcreteValueType<'ink> {
+    type Value: ValueType<'ink>;
 }
 
 /// If we take a pointer of value T, which type can it return? This trait dictates that.
-pub trait AddressableType<T: ?Sized> {
+pub trait AddressableType<'ink, T: ?Sized> {
     /// Cast the pointer if required
-    fn ptr_cast(value: PointerValue, _context: &IrValueContext) -> PointerValue {
+    fn ptr_cast(
+        value: PointerValue<'ink>,
+        _context: &IrValueContext<'ink, '_, '_>,
+    ) -> PointerValue<'ink> {
         value
     }
 }
 
 /// A trait implemented for types that can determine the IR type of a value without an instance.
-pub trait SizedValueType: ConcreteValueType + Sized {
+pub trait SizedValueType<'ink>: ConcreteValueType<'ink> + Sized {
     /// Returns the IR type of a value of this type.
     fn get_ir_type(
-        context: &IrTypeContext,
-    ) -> <<Self as ConcreteValueType>::Value as ValueType>::Type;
+        context: &IrTypeContext<'ink, '_>,
+    ) -> <<Self as ConcreteValueType<'ink>>::Value as ValueType<'ink>>::Type;
 }
 
 /// A trait that returns the pointer type of the specified value.
-pub trait PointerValueType: ConcreteValueType {
+pub trait PointerValueType<'ink>: ConcreteValueType<'ink> {
     /// Returns the pointer type of the value
     fn get_ptr_type(
-        context: &IrTypeContext,
+        context: &IrTypeContext<'ink, '_>,
         address_space: Option<AddressSpace>,
-    ) -> inkwell::types::PointerType;
+    ) -> inkwell::types::PointerType<'ink>;
 }
 
 /// A trait that enables the conversion from an inkwell type to a corresponding value type. (e.g.
 /// IntType -> IntValue)
-pub trait TypeValue {
-    type Value: inkwell::values::AnyValue;
+pub trait TypeValue<'ink> {
+    type Value: inkwell::values::AnyValue<'ink>;
 }
 
 /// A trait that enables the conversion from an inkwell value to a corresponding type. (e.g.
 /// IntValue -> IntType)
-pub trait ValueType: Clone + Debug + Copy + Eq + PartialEq + Hash {
-    type Type: inkwell::types::AnyType;
+pub trait ValueType<'ink>: Clone + Debug + Copy + Eq + PartialEq + Hash {
+    type Type: inkwell::types::AnyType<'ink>;
 
     /// Returns the type of the value
     fn get_type(&self) -> Self::Type;
 }
 
 /// A trait that is implemented for types that can also be represented as a pointer.
-pub trait AddressableTypeValue: TypeValue {
-    fn ptr_type(&self, address_space: AddressSpace) -> inkwell::types::PointerType;
+pub trait AddressableTypeValue<'ink>: TypeValue<'ink> {
+    fn ptr_type(&self, address_space: AddressSpace) -> inkwell::types::PointerType<'ink>;
 }
 
 /// A macro that implements basic traits for inkwell types.
 macro_rules! impl_value_type_value {
     ($($ty:ty => $val:ty),+) => {
         $(
-            impl TypeValue for $ty {
+            impl<'ink> TypeValue<'ink> for $ty {
                 type Value = $val;
             }
-            impl ValueType for $val {
+            impl<'ink> ValueType<'ink> for $val {
                 type Type = $ty;
 
                 fn get_type(&self) -> Self::Type {
-                    self.get_type()
+                    Self::get_type(self)
                 }
             }
         )*
@@ -175,21 +176,21 @@ macro_rules! impl_value_type_value {
 }
 
 impl_value_type_value! (
-    inkwell::types::IntType => inkwell::values::IntValue,
-    inkwell::types::FloatType => inkwell::values::FloatValue,
-    inkwell::types::ArrayType => inkwell::values::ArrayValue,
-    inkwell::types::VectorType => inkwell::values::VectorValue,
-    inkwell::types::StructType => inkwell::values::StructValue,
-    inkwell::types::PointerType => inkwell::values::PointerValue,
-    inkwell::types::FunctionType => inkwell::values::FunctionValue
+    inkwell::types::IntType<'ink> => inkwell::values::IntValue<'ink>,
+    inkwell::types::FloatType<'ink> => inkwell::values::FloatValue<'ink>,
+    inkwell::types::ArrayType<'ink> => inkwell::values::ArrayValue<'ink>,
+    inkwell::types::VectorType<'ink> => inkwell::values::VectorValue<'ink>,
+    inkwell::types::StructType<'ink> => inkwell::values::StructValue<'ink>,
+    inkwell::types::PointerType<'ink> => inkwell::values::PointerValue<'ink>,
+    inkwell::types::FunctionType<'ink> => inkwell::values::FunctionValue<'ink>
 );
 
 macro_rules! impl_addressable_type_values {
     ($($ty:ty),+) => {
         $(
-            impl AddressableTypeValue for $ty {
-                fn ptr_type(&self, address_space: AddressSpace) -> inkwell::types::PointerType {
-                    self.ptr_type(address_space)
+            impl<'ink> AddressableTypeValue<'ink> for $ty {
+                fn ptr_type(&self, address_space: AddressSpace) -> inkwell::types::PointerType<'ink> {
+                    Self::ptr_type(self, address_space)
                 }
             }
         )*
@@ -197,17 +198,17 @@ macro_rules! impl_addressable_type_values {
 }
 
 impl_addressable_type_values!(
-    inkwell::types::IntType,
-    inkwell::types::FloatType,
-    inkwell::types::ArrayType,
-    inkwell::types::VectorType,
-    inkwell::types::StructType,
-    inkwell::types::PointerType,
-    inkwell::types::FunctionType
+    inkwell::types::IntType<'ink>,
+    inkwell::types::FloatType<'ink>,
+    inkwell::types::ArrayType<'ink>,
+    inkwell::types::VectorType<'ink>,
+    inkwell::types::StructType<'ink>,
+    inkwell::types::PointerType<'ink>,
+    inkwell::types::FunctionType<'ink>
 );
 
-impl AddressableTypeValue for inkwell::types::BasicTypeEnum {
-    fn ptr_type(&self, address_space: AddressSpace) -> PointerType {
+impl<'ink> AddressableTypeValue<'ink> for inkwell::types::BasicTypeEnum<'ink> {
+    fn ptr_type(&self, address_space: AddressSpace) -> PointerType<'ink> {
         match self {
             BasicTypeEnum::ArrayType(ty) => ty.ptr_type(address_space),
             BasicTypeEnum::FloatType(ty) => ty.ptr_type(address_space),
@@ -219,29 +220,29 @@ impl AddressableTypeValue for inkwell::types::BasicTypeEnum {
     }
 }
 
-impl TypeValue for inkwell::types::BasicTypeEnum {
-    type Value = inkwell::values::BasicValueEnum;
+impl<'ink> TypeValue<'ink> for inkwell::types::BasicTypeEnum<'ink> {
+    type Value = inkwell::values::BasicValueEnum<'ink>;
 }
 
-impl<T: ConcreteValueType + ?Sized> Value<T> {
+impl<'ink, T: ConcreteValueType<'ink> + ?Sized> Value<'ink, T> {
     /// Returns the type of the value.
-    pub fn get_type(&self) -> <T::Value as ValueType>::Type {
+    pub fn get_type(&self) -> <T::Value as ValueType<'ink>>::Type {
         <T::Value as ValueType>::get_type(&self.value)
     }
 
     /// Constructs a `Value<T>` from an inkwell value.
-    pub(super) fn from_raw(value: T::Value) -> Value<T> {
+    pub(super) fn from_raw(value: T::Value) -> Value<'ink, T> {
         Value { value }
     }
 }
 
-impl<T: ConcreteValueType + ?Sized> Value<*const T>
+impl<'ink, T: ConcreteValueType<'ink> + ?Sized> Value<'ink, *const T>
 where
-    *const T: SizedValueType<Value = PointerValue>,
-    <*const T as ConcreteValueType>::Value: ValueType<Type = PointerType>,
+    *const T: SizedValueType<'ink, Value = PointerValue<'ink>>,
+    <*const T as ConcreteValueType<'ink>>::Value: ValueType<'ink, Type = PointerType<'ink>>,
 {
     /// Constructs a value by casting the specified pointer value to this value
-    pub fn with_cast(value: PointerValue, context: &IrValueContext) -> Self {
+    pub fn with_cast(value: PointerValue<'ink>, context: &IrValueContext<'ink, '_, '_>) -> Self {
         let target_type = <*const T>::get_ir_type(context.type_context);
         Value {
             value: if value.get_type() == target_type {
@@ -253,13 +254,13 @@ where
     }
 }
 
-impl<T: ConcreteValueType + ?Sized> Value<*mut T>
+impl<'ink, T: ConcreteValueType<'ink> + ?Sized> Value<'ink, *mut T>
 where
-    *mut T: SizedValueType<Value = PointerValue>,
-    <*mut T as ConcreteValueType>::Value: ValueType<Type = PointerType>,
+    *mut T: SizedValueType<'ink, Value = PointerValue<'ink>>,
+    <*mut T as ConcreteValueType<'ink>>::Value: ValueType<'ink, Type = PointerType<'ink>>,
 {
     /// Constructs a value by casting the specified pointer value to this value
-    pub fn with_cast(value: PointerValue, context: &IrValueContext) -> Self {
+    pub fn with_cast(value: PointerValue<'ink>, context: &IrValueContext<'ink, '_, '_>) -> Self {
         let target_type = <*mut T>::get_ir_type(context.type_context);
         Value {
             value: if value.get_type() == target_type {
@@ -271,121 +272,129 @@ where
     }
 }
 
-impl<T: SizedValueType + ?Sized> Value<T> {
+impl<'ink, T: SizedValueType<'ink> + ?Sized> Value<'ink, T> {
     /// Returns the inkwell type of this `Value`.
-    pub fn get_ir_type(context: &IrTypeContext) -> <T::Value as ValueType>::Type {
+    pub fn get_ir_type(context: &IrTypeContext<'ink, '_>) -> <T::Value as ValueType<'ink>>::Type {
         T::get_ir_type(context)
     }
 }
 
-impl<T: ConcreteValueType + ?Sized> AsValue<T> for Value<T> {
-    fn as_value(&self, _context: &IrValueContext) -> Value<T> {
+impl<'ink, T: ConcreteValueType<'ink> + ?Sized> AsValue<'ink, T> for Value<'ink, T> {
+    fn as_value(&self, _context: &IrValueContext<'ink, '_, '_>) -> Value<'ink, T> {
         *self
     }
 }
 
-impl<T: ConcreteValueType + ?Sized> Clone for Value<T> {
+impl<'ink, T: ConcreteValueType<'ink> + ?Sized> Clone for Value<'ink, T> {
     fn clone(&self) -> Self {
         Value { value: self.value }
     }
 }
 
-impl<T: ConcreteValueType + ?Sized> Copy for Value<T> {}
+impl<'ink, T: ConcreteValueType<'ink> + ?Sized> Copy for Value<'ink, T> {}
 
-impl<T: ConcreteValueType + ?Sized> PartialEq for Value<T> {
+impl<'ink, T: ConcreteValueType<'ink> + ?Sized> PartialEq for Value<'ink, T> {
     fn eq(&self, other: &Self) -> bool {
         self.value == other.value
     }
 }
 
-impl<T: ConcreteValueType + ?Sized> Eq for Value<T> {}
+impl<'ink, T: ConcreteValueType<'ink> + ?Sized> Eq for Value<'ink, T> {}
 
-impl<T: ConcreteValueType + ?Sized> Hash for Value<T> {
+impl<'ink, T: ConcreteValueType<'ink> + ?Sized> Hash for Value<'ink, T> {
     fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
         self.value.hash(state)
     }
 }
 
-impl<T: ConcreteValueType + ?Sized> std::fmt::Debug for Value<T> {
+impl<'ink, T: ConcreteValueType<'ink> + ?Sized> std::fmt::Debug for Value<'ink, T> {
     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
         write!(f, "{:?}", self.value)
     }
 }
 
-impl<T: ConcreteValueType + ?Sized> Into<inkwell::values::BasicValueEnum> for Value<T>
+impl<'ink, T: ConcreteValueType<'ink> + ?Sized> Into<inkwell::values::BasicValueEnum<'ink>>
+    for Value<'ink, T>
 where
-    T::Value: Into<inkwell::values::BasicValueEnum>,
+    T::Value: Into<inkwell::values::BasicValueEnum<'ink>>,
 {
-    fn into(self) -> BasicValueEnum {
+    fn into(self) -> BasicValueEnum<'ink> {
         self.value.into()
     }
 }
 
-pub trait AsValueInto<T> {
-    fn as_value_into(&self, context: &IrValueContext) -> T;
+pub trait AsValueInto<'ink, T> {
+    fn as_value_into(&self, context: &IrValueContext<'ink, '_, '_>) -> T;
 }
 
-impl<I, T: ConcreteValueType> AsValueInto<I> for Value<T>
+impl<'ink, I, T: ConcreteValueType<'ink>> AsValueInto<'ink, I> for Value<'ink, T>
 where
     T::Value: Into<I>,
 {
-    fn as_value_into(&self, _context: &IrValueContext) -> I {
+    fn as_value_into(&self, _context: &IrValueContext<'ink, '_, '_>) -> I {
         self.value.into()
     }
 }
 
-impl<I, T: ConcreteValueType + AsValue<T>> AsValueInto<I> for T
+impl<'ink, I, T: ConcreteValueType<'ink> + AsValue<'ink, T>> AsValueInto<'ink, I> for T
 where
     T::Value: Into<I>,
 {
-    fn as_value_into(&self, context: &IrValueContext) -> I {
+    fn as_value_into(&self, context: &IrValueContext<'ink, '_, '_>) -> I {
         self.as_value(context).value.into()
     }
 }
 
 // A `TransparentValue` can also be represented by a `Value<T>`.
-impl<T: TransparentValue> ConcreteValueType for T {
-    type Value = <T::Target as ConcreteValueType>::Value;
+impl<'ink, T: TransparentValue<'ink>> ConcreteValueType<'ink> for T {
+    type Value = <T::Target as ConcreteValueType<'ink>>::Value;
 }
 
 // A `TransparentValue` is sized if the target is also sized.
-impl<T: TransparentValue> SizedValueType for T
+impl<'ink, T: TransparentValue<'ink>> SizedValueType<'ink> for T
 where
-    T::Target: SizedValueType,
+    T::Target: SizedValueType<'ink>,
 {
-    fn get_ir_type(context: &IrTypeContext) -> <Self::Value as ValueType>::Type {
+    fn get_ir_type(context: &IrTypeContext<'ink, '_>) -> <Self::Value as ValueType<'ink>>::Type {
         T::Target::get_ir_type(context)
     }
 }
 
 // If the target of the transparent value can statically return a pointer type, so can we.
-impl<T: TransparentValue> PointerValueType for T
+impl<'ink, T: TransparentValue<'ink>> PointerValueType<'ink> for T
 where
-    T::Target: PointerValueType,
+    T::Target: PointerValueType<'ink>,
 {
-    fn get_ptr_type(context: &IrTypeContext, address_space: Option<AddressSpace>) -> PointerType {
+    fn get_ptr_type(
+        context: &IrTypeContext<'ink, '_>,
+        address_space: Option<AddressSpace>,
+    ) -> PointerType<'ink> {
         T::Target::get_ptr_type(context, address_space)
     }
 }
 
 // If the target is addressable as I, the transparent value is also addressable as I.
 impl<
+        'ink,
         I: ?Sized,
-        U: ConcreteValueType + ?Sized + AddressableType<I>,
-        T: TransparentValue<Target = U>,
-    > AddressableType<I> for T
+        U: ConcreteValueType<'ink> + ?Sized + AddressableType<'ink, I>,
+        T: TransparentValue<'ink, Target = U>,
+    > AddressableType<'ink, I> for T
 {
-    fn ptr_cast(value: PointerValue, context: &IrValueContext) -> PointerValue {
-        <T::Target as AddressableType<I>>::ptr_cast(value, context)
+    fn ptr_cast(
+        value: PointerValue<'ink>,
+        context: &IrValueContext<'ink, '_, '_>,
+    ) -> PointerValue<'ink> {
+        <T::Target as AddressableType<'ink, I>>::ptr_cast(value, context)
     }
 }
 
 // Transparent values can also be represented as `Value<Self>`.
-impl<T> AsValue<T> for T
+impl<'ink, T> AsValue<'ink, T> for T
 where
-    T: TransparentValue,
+    T: TransparentValue<'ink>,
 {
-    fn as_value(&self, context: &IrValueContext) -> Value<T> {
+    fn as_value(&self, context: &IrValueContext<'ink, '_, '_>) -> Value<'ink, T> {
         Value::from_raw(self.as_target_value(context).value)
     }
 }
diff --git a/crates/mun_codegen/src/value/pointer_value.rs b/crates/mun_codegen/src/value/pointer_value.rs
index 299fea889..c42d99a3e 100644
--- a/crates/mun_codegen/src/value/pointer_value.rs
+++ b/crates/mun_codegen/src/value/pointer_value.rs
@@ -5,41 +5,47 @@ use crate::value::{
 use inkwell::types::PointerType;
 use inkwell::AddressSpace;
 
-impl<T: PointerValueType> ConcreteValueType for *const T {
-    type Value = inkwell::values::PointerValue;
+impl<'ink, T: PointerValueType<'ink>> ConcreteValueType<'ink> for *const T {
+    type Value = inkwell::values::PointerValue<'ink>;
 }
-impl<T: PointerValueType> ConcreteValueType for *mut T {
-    type Value = inkwell::values::PointerValue;
+impl<'ink, T: PointerValueType<'ink>> ConcreteValueType<'ink> for *mut T {
+    type Value = inkwell::values::PointerValue<'ink>;
 }
 
-impl<T: PointerValueType> SizedValueType for *const T {
-    fn get_ir_type(context: &IrTypeContext) -> inkwell::types::PointerType {
+impl<'ink, T: PointerValueType<'ink>> SizedValueType<'ink> for *const T {
+    fn get_ir_type(context: &IrTypeContext<'ink, '_>) -> inkwell::types::PointerType<'ink> {
         T::get_ptr_type(context, None)
     }
 }
-impl<T: PointerValueType> SizedValueType for *mut T {
-    fn get_ir_type(context: &IrTypeContext) -> inkwell::types::PointerType {
+impl<'ink, T: PointerValueType<'ink>> SizedValueType<'ink> for *mut T {
+    fn get_ir_type(context: &IrTypeContext<'ink, '_>) -> inkwell::types::PointerType<'ink> {
         T::get_ptr_type(context, None)
     }
 }
-impl<T: PointerValueType> PointerValueType for *mut T {
-    fn get_ptr_type(context: &IrTypeContext, address_space: Option<AddressSpace>) -> PointerType {
+impl<'ink, T: PointerValueType<'ink>> PointerValueType<'ink> for *mut T {
+    fn get_ptr_type(
+        context: &IrTypeContext<'ink, '_>,
+        address_space: Option<AddressSpace>,
+    ) -> PointerType<'ink> {
         Self::get_ir_type(context).ptr_type(address_space.unwrap_or(AddressSpace::Generic))
     }
 }
-impl<T: PointerValueType> PointerValueType for *const T {
-    fn get_ptr_type(context: &IrTypeContext, address_space: Option<AddressSpace>) -> PointerType {
+impl<'ink, T: PointerValueType<'ink>> PointerValueType<'ink> for *const T {
+    fn get_ptr_type(
+        context: &IrTypeContext<'ink, '_>,
+        address_space: Option<AddressSpace>,
+    ) -> PointerType<'ink> {
         Self::get_ir_type(context).ptr_type(address_space.unwrap_or(AddressSpace::Generic))
     }
 }
 
-impl<T: SizedValueType<Value = inkwell::values::PointerValue>> Value<T> {
+impl<'ink, T: SizedValueType<'ink, Value = inkwell::values::PointerValue<'ink>>> Value<'ink, T> {
     /// Constructs a `null` pointer of type `T`
-    pub fn null(context: &IrValueContext) -> Self {
+    pub fn null(context: &IrValueContext<'ink, '_, '_>) -> Self {
         Value::from_raw(T::get_ir_type(context.type_context).const_null())
     }
 }
 
-impl<T> AddressableType<*const T> for *const T where *const T: ConcreteValueType {}
+impl<'ink, T> AddressableType<'ink, *const T> for *const T where *const T: ConcreteValueType<'ink> {}
 
-impl<T> AddressableType<*mut T> for *mut T where *mut T: ConcreteValueType {}
+impl<'ink, T> AddressableType<'ink, *mut T> for *mut T where *mut T: ConcreteValueType<'ink> {}
diff --git a/crates/mun_codegen/src/value/string.rs b/crates/mun_codegen/src/value/string.rs
index cf8a99491..e53086c8d 100644
--- a/crates/mun_codegen/src/value/string.rs
+++ b/crates/mun_codegen/src/value/string.rs
@@ -6,13 +6,21 @@ pub trait CanInternalize {
     type Type;
 
     /// Internalizes the instance into a global value.
-    fn intern<S: AsRef<str>>(&self, name: S, context: &IrValueContext) -> Global<Self::Type>;
+    fn intern<'ink, S: AsRef<str>>(
+        &self,
+        name: S,
+        context: &IrValueContext<'ink, '_, '_>,
+    ) -> Global<'ink, Self::Type>;
 }
 
 impl CanInternalize for str {
     type Type = String;
 
-    fn intern<S: AsRef<str>>(&self, name: S, context: &IrValueContext) -> Global<Self::Type> {
+    fn intern<'ink, S: AsRef<str>>(
+        &self,
+        name: S,
+        context: &IrValueContext<'ink, '_, '_>,
+    ) -> Global<'ink, Self::Type> {
         unsafe {
             Global::from_raw(
                 self.as_bytes()
@@ -27,7 +35,11 @@ impl CanInternalize for str {
 impl CanInternalize for CStr {
     type Type = CString;
 
-    fn intern<S: AsRef<str>>(&self, name: S, context: &IrValueContext) -> Global<Self::Type> {
+    fn intern<'ink, S: AsRef<str>>(
+        &self,
+        name: S,
+        context: &IrValueContext<'ink, '_, '_>,
+    ) -> Global<'ink, Self::Type> {
         unsafe {
             Global::from_raw(
                 self.to_bytes_with_nul()
@@ -39,18 +51,18 @@ impl CanInternalize for CStr {
     }
 }
 
-impl TransparentValue for CString {
+impl<'ink> TransparentValue<'ink> for CString {
     type Target = [u8];
 
-    fn as_target_value(&self, context: &IrValueContext) -> Value<Self::Target> {
+    fn as_target_value(&self, context: &IrValueContext<'ink, '_, '_>) -> Value<'ink, Self::Target> {
         self.as_bytes_with_nul().as_value(context)
     }
 }
 
-impl TransparentValue for String {
+impl<'ink> TransparentValue<'ink> for String {
     type Target = [u8];
 
-    fn as_target_value(&self, context: &IrValueContext) -> Value<Self::Target> {
+    fn as_target_value(&self, context: &IrValueContext<'ink, '_, '_>) -> Value<'ink, Self::Target> {
         self.as_bytes().as_value(context)
     }
 }
diff --git a/crates/mun_codegen/src/value/tuple_value.rs b/crates/mun_codegen/src/value/tuple_value.rs
index a6dd41ab1..99df6f82e 100644
--- a/crates/mun_codegen/src/value/tuple_value.rs
+++ b/crates/mun_codegen/src/value/tuple_value.rs
@@ -7,28 +7,28 @@ macro_rules! tuple_impls {
     ( $( $name:ident )* ) => {
         /// Every tuple that contains values that can be converted to BasicValueEnum can be
         /// represented by a tuple
-        impl<$($name: AsValueInto<inkwell::values::BasicValueEnum>),*> ConcreteValueType for ($($name,)*) {
-            type Value = inkwell::values::StructValue;
+        impl<'ink, $($name: AsValueInto<'ink, inkwell::values::BasicValueEnum<'ink>>),*> ConcreteValueType<'ink> for ($($name,)*) {
+            type Value = inkwell::values::StructValue<'ink>;
         }
 
         /// Every tuple that contains values that can be converted to BasicValueEnum and which are
         /// sized, are also sized.
-        impl<$($name: AsValueInto<inkwell::values::BasicValueEnum> + SizedValueType),*> SizedValueType for ($($name,)*)
+        impl<'ink, $($name: AsValueInto<'ink, inkwell::values::BasicValueEnum<'ink>> + SizedValueType<'ink>),*> SizedValueType<'ink> for ($($name,)*)
         where
             $(
-                <<$name as ConcreteValueType>::Value as ValueType>::Type: Into<inkwell::types::BasicTypeEnum>
+                <<$name as ConcreteValueType<'ink>>::Value as ValueType<'ink>>::Type: Into<inkwell::types::BasicTypeEnum<'ink>>
             ,)*
         {
-            fn get_ir_type(context: &IrTypeContext) -> inkwell::types::StructType {
+            fn get_ir_type(context: &IrTypeContext<'ink, '_>) -> inkwell::types::StructType<'ink> {
                 context.context.struct_type(&[
                     $($name::get_ir_type(context).into(),)*
                 ], false)
             }
         }
 
-        impl<$($name: AsValueInto<inkwell::values::BasicValueEnum>),*> AsValue<($($name,)*)> for ($($name,)*) {
+        impl<'ink, $($name: AsValueInto<'ink, inkwell::values::BasicValueEnum<'ink>>),*> AsValue<'ink, ($($name,)*)> for ($($name,)*) {
             #[allow(unused_variables)]
-            fn as_value(&self, context: &IrValueContext) -> Value<Self> {
+            fn as_value(&self, context: &IrValueContext<'ink, '_, '_>) -> Value<'ink, Self> {
                 #[allow(non_snake_case)]
                 let ($($name,)*) = self;
                 Value::from_raw(
diff --git a/crates/mun_codegen_macros/src/lib.rs b/crates/mun_codegen_macros/src/lib.rs
index d97a187f8..e9f9e8f88 100644
--- a/crates/mun_codegen_macros/src/lib.rs
+++ b/crates/mun_codegen_macros/src/lib.rs
@@ -2,12 +2,12 @@
 
 use proc_macro::TokenStream;
 use quote::quote;
-use syn::{parse_macro_input, Attribute, Data, DeriveInput, Lit, Meta, NestedMeta, Path};
+use syn::{parse_macro_input, Attribute, Data, DeriveInput, Meta, NestedMeta, Path};
 
 /// This procedural macro implements the `AsValue` trait as well as several required other traits.
 /// All of these traits enable creating an `inkwell::values::StructValue` from a generic struct, as
 /// long as all fields of the struct also implement `AsValue`.
-#[proc_macro_derive(AsValue, attributes(ir_name))]
+#[proc_macro_derive(AsValue)]
 pub fn as_value_derive(input: TokenStream) -> TokenStream {
     // Parse Phase
     let derive_input = parse_macro_input!(input as DeriveInput);
@@ -17,44 +17,22 @@ pub fn as_value_derive(input: TokenStream) -> TokenStream {
         Data::Enum(_) => panic!("#[derive(AsValue)] is only defined for structs, not for enums!"),
     };
 
-    // Parse the `[ir_name = ".."]` part
-    let mut ir_name = String::new();
-    for attr in derive_input
-        .attrs
-        .iter()
-        .filter(|a| a.path.get_ident().map(|i| *i == "ir_name").unwrap_or(false))
-        .map(Attribute::parse_meta)
-        .filter_map(|x| x.ok())
-    {
-        if let Meta::NameValue(meta_name_value) = attr {
-            match meta_name_value.lit {
-                Lit::Str(lit_str) => {
-                    ir_name = lit_str.value();
-                }
-                _ => {
-                    panic!("ir_name must be a string");
-                }
-            };
-        }
-    }
-
     // Get the typename of the struct we're working with
-    let ident = &derive_input.ident;
 
-    // Generate a list of all field types
-    let field_types_tuple = struct_data.fields.iter().map(|f| {
-        let ty = &f.ty;
+    let ident = {
+        let ident = derive_input.ident;
+        let generics = derive_input.generics;
         quote! {
-            #ty
+            #ident #generics
         }
-    });
+    };
 
     // Generate a list of where clauses that ensure that we can cast each field to an
     // `inkwell::types::BasicTypeEnum`
     let field_types = struct_data.fields.iter().map(|f| {
         let ty = &f.ty;
         quote! {
-            Into::<inkwell::types::BasicTypeEnum>::into(<#ty>::get_ir_type(context))
+            Into::<inkwell::types::BasicTypeEnum<'ink>>::into(<#ty>::get_ir_type(context))
         }
     });
 
@@ -64,7 +42,7 @@ pub fn as_value_derive(input: TokenStream) -> TokenStream {
         let name = f.ident.as_ref().map(|i| quote! { #i }).unwrap_or_else(|| quote! { #idx });
         quote! {
             {
-                let value = crate::value::AsValueInto::<inkwell::values::BasicValueEnum>::as_value_into(&self. #name, context);
+                let value = crate::value::AsValueInto::<'ink, inkwell::values::BasicValueEnum<'ink>>::as_value_into(&self. #name, context);
                 // eprintln!("- {:?}", value.get_type());
                 value
             }
@@ -73,40 +51,37 @@ pub fn as_value_derive(input: TokenStream) -> TokenStream {
 
     // Generate Phase
     (quote! {
-        impl crate::value::ConcreteValueType for #ident {
-            type Value = inkwell::values::StructValue;
+        impl<'ink> crate::value::ConcreteValueType<'ink> for #ident {
+            type Value = inkwell::values::StructValue<'ink>;
         }
 
-        impl crate::value::SizedValueType for #ident {
-            fn get_ir_type(context: &crate::value::IrTypeContext) -> inkwell::types::StructType {
-                let key = (#ir_name, std::any::TypeId::of::<(#(#field_types_tuple),*)>());
-                let struct_types = context.struct_types.upgradable_read();
-                let value = match struct_types.get(&key) {
+        impl<'ink> crate::value::SizedValueType<'ink> for #ident {
+            fn get_ir_type(context: &crate::value::IrTypeContext<'ink, '_>) -> inkwell::types::StructType<'ink> {
+                let key = std::any::type_name::<#ident>();
+                match context.struct_types.borrow().get(&key) {
                     Some(value) => {
                         return *value;
                     }
-                    None => {
-                        let mut struct_types = parking_lot::RwLockUpgradableReadGuard::upgrade(struct_types);
-                        let struct_ty = context.context.opaque_struct_type(key.0);
-                        struct_types.insert(key, struct_ty);
-                        struct_ty
-                    }
+                    None => (),
                 };
-                value.set_body(&[
+
+                let struct_ty = context.context.opaque_struct_type(key);
+                context.struct_types.borrow_mut().insert(key, struct_ty);
+                struct_ty.set_body(&[
                     #(#field_types),*
                 ], false);
-                value
+                struct_ty
             }
         }
 
-        impl crate::value::PointerValueType for #ident {
-            fn get_ptr_type(context: &crate::value::IrTypeContext, address_space: Option<inkwell::AddressSpace>) -> inkwell::types::PointerType {
+        impl<'ink> crate::value::PointerValueType<'ink> for #ident {
+            fn get_ptr_type(context: &crate::value::IrTypeContext<'ink, '_>, address_space: Option<inkwell::AddressSpace>) -> inkwell::types::PointerType<'ink> {
                 Self::get_ir_type(context).ptr_type(address_space.unwrap_or(inkwell::AddressSpace::Generic))
             }
         }
 
-        impl crate::value::AsValue<#ident> for #ident {
-            fn as_value(&self, context: &crate::value::IrValueContext) -> crate::value::Value<Self> {
+        impl<'ink> crate::value::AsValue<'ink, #ident> for #ident {
+            fn as_value(&self, context: &crate::value::IrValueContext<'ink, '_, '_>) -> crate::value::Value<'ink, Self> {
                 let struct_type = Self::get_ir_type(context.type_context);
                 // eprintln!("Constructing: {:?}", struct_type.print_to_string().to_string());
                 let value = struct_type.const_named_struct(&[
@@ -117,7 +92,7 @@ pub fn as_value_derive(input: TokenStream) -> TokenStream {
             }
         }
 
-        impl crate::value::AddressableType<#ident> for #ident {}
+        impl<'ink> crate::value::AddressableType<'ink, #ident> for #ident {}
     }).into()
 }
 
@@ -177,8 +152,21 @@ pub fn is_abi_compatible_derive(input: TokenStream) -> TokenStream {
         .collect::<Vec<_>>()
         .join("::");
 
+    let struct_generics = {
+        let generics = &derive_input.generics;
+        quote! {
+            #generics
+        }
+    };
+
     // Get the type and name of the struct we're implementing this for
-    let struct_type = &derive_input.ident;
+    let struct_type = {
+        let ident = derive_input.ident;
+        let generics = derive_input.generics;
+        quote! {
+            #ident #generics
+        }
+    };
     let struct_type_name = format!("{}", struct_type);
 
     // Generate code for every field to test its compatibility
@@ -196,7 +184,7 @@ pub fn is_abi_compatible_derive(input: TokenStream) -> TokenStream {
     // Generate Phase
     (quote! {
         #[cfg(test)]
-        impl self::test::TestIsAbiCompatible<#abi_type> for #struct_type {
+        impl #struct_generics self::test::TestIsAbiCompatible<#abi_type> for #struct_type {
             fn test(abi_value: &#abi_type) {
                 use self::test::*;
                 #(#field_types)*
@@ -204,10 +192,10 @@ pub fn is_abi_compatible_derive(input: TokenStream) -> TokenStream {
         }
 
         #[cfg(test)]
-        impl self::test::IsAbiCompatible<#abi_type> for #struct_type {}
+        impl #struct_generics self::test::IsAbiCompatible<#abi_type> for #struct_type {}
 
         #[cfg(test)]
-        impl self::test::IsAbiCompatible<#struct_type> for #struct_type {}
+        impl #struct_generics self::test::IsAbiCompatible<#struct_type> for #struct_type {}
     })
     .into()
 }
diff --git a/crates/mun_compiler/src/db.rs b/crates/mun_compiler/src/db.rs
index d151c0096..7ab3bcd49 100644
--- a/crates/mun_compiler/src/db.rs
+++ b/crates/mun_compiler/src/db.rs
@@ -1,14 +1,13 @@
 use crate::Config;
-use mun_codegen::IrDatabase;
+use mun_codegen::{CodeGenDatabase, CodeGenDatabaseStorage};
 use mun_hir::{salsa, HirDatabase, Upcast};
-use std::sync::Arc;
 
 /// A compiler database is a salsa database that enables increment compilation.
 #[salsa::database(
     mun_hir::SourceDatabaseStorage,
     mun_hir::DefDatabaseStorage,
     mun_hir::HirDatabaseStorage,
-    mun_codegen::IrDatabaseStorage
+    CodeGenDatabaseStorage
 )]
 pub struct CompilerDatabase {
     storage: salsa::Storage<Self>,
@@ -32,8 +31,8 @@ impl Upcast<dyn mun_hir::HirDatabase> for CompilerDatabase {
     }
 }
 
-impl Upcast<dyn mun_codegen::IrDatabase> for CompilerDatabase {
-    fn upcast(&self) -> &dyn mun_codegen::IrDatabase {
+impl Upcast<dyn CodeGenDatabase> for CompilerDatabase {
+    fn upcast(&self) -> &dyn CodeGenDatabase {
         &*self
     }
 }
@@ -46,7 +45,6 @@ impl CompilerDatabase {
         };
 
         // Set the initial configuration
-        db.set_context(Arc::new(mun_codegen::Context::create()));
         db.set_config(config);
 
         db
@@ -55,7 +53,7 @@ impl CompilerDatabase {
     /// Applies the given configuration to the database
     pub fn set_config(&mut self, config: &Config) {
         self.set_target(config.target.clone());
-        self.set_optimization_lvl(config.optimization_lvl);
+        self.set_optimization_level(config.optimization_lvl);
     }
 }
 
diff --git a/crates/mun_compiler/src/driver.rs b/crates/mun_compiler/src/driver.rs
index b5db96a57..519d975d4 100644
--- a/crates/mun_compiler/src/driver.rs
+++ b/crates/mun_compiler/src/driver.rs
@@ -5,7 +5,7 @@ use crate::{
     compute_source_relative_path, db::CompilerDatabase, ensure_package_output_dir, is_source_file,
     PathOrInline, RelativePath,
 };
-use mun_codegen::{Assembly, IrDatabase};
+use mun_codegen::{Assembly, CodeGenDatabase};
 use mun_hir::{DiagnosticSink, FileId, RelativePathBuf, SourceDatabase, SourceRoot, SourceRootId};
 
 use std::{path::PathBuf, sync::Arc};
diff --git a/crates/mun_lld/Cargo.toml b/crates/mun_lld/Cargo.toml
index f43dbe64a..44dad7843 100644
--- a/crates/mun_lld/Cargo.toml
+++ b/crates/mun_lld/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "mun_lld"
-version = "70.2.0"
+version = "80.0.0"
 authors = ["The Mun Team <team@mun-lang.org>"]
 edition = "2018"
 description = "Bindings for LLD used to link Mun binaries"
diff --git a/crates/mun_lld/wrapper/lld-c.cpp b/crates/mun_lld/wrapper/lld-c.cpp
index 2132bdcbf..1b045dcc5 100644
--- a/crates/mun_lld/wrapper/lld-c.cpp
+++ b/crates/mun_lld/wrapper/lld-c.cpp
@@ -45,8 +45,8 @@ void mun_link_free_result(LldInvokeResult* result)
 }
 
 LldInvokeResult mun_lld_link(LldFlavor flavor, int argc, const char *const *argv) {
-  std::string errorString;
-  llvm::raw_string_ostream errorStream(errorString);
+  std::string outputString, errorString;
+  llvm::raw_string_ostream outputStream(outputString);
   std::vector<const char*> args(argv, argv + argc);
   LldInvokeResult result;
   switch(flavor)
@@ -55,33 +55,34 @@ LldInvokeResult mun_lld_link(LldFlavor flavor, int argc, const char *const *argv
     {
       args.insert(args.begin(), "lld");               // Issue #1: The first argument MUST be the executable name..
       std::unique_lock<std::mutex> lock(_elfMutex);   // Issue #2: The ELF driver is not thread safe..
-      result.success = lld::elf::link(args, false, errorStream);
+      result.success = lld::elf::link(args, false, outputStream);
       break;
     }
     case Wasm:
     {
       std::unique_lock<std::mutex> lock(_wasmMutex);
-      result.success = lld::wasm::link(args, false, errorStream);
+      result.success = lld::wasm::link(args, false, outputStream);
       break;
     }
     case MachO:
     {
       std::unique_lock <std::mutex> lock(_machOMutex);
-      result.success = lld::mach_o::link(args, false, errorStream);
+      result.success = lld::mach_o::link(args, false, outputStream);
       break;
     }
     case Coff:
     {
       args.insert(args.begin(), "lld.exe");           // Issue #1: The first argument MUST be the executable name..
       std::unique_lock<std::mutex> lock(_coffMutex);  // Issue #2: The COFF driver is not thread safe..
-      result.success = lld::coff::link(args, false, errorStream);
+      result.success = lld::coff::link(args, false, outputStream);
       break;
     }
     default:
       result.success = false;
       break;
   }
-  result.messages = mun_alloc_str(errorStream.str());
+  std::string resultMessage = outputStream.str();
+  result.messages = mun_alloc_str(resultMessage);
   return result;
 }
 
diff --git a/crates/mun_runtime/benches/util/mod.rs b/crates/mun_runtime/benches/util/mod.rs
index 12a0b67ef..d15d89a82 100644
--- a/crates/mun_runtime/benches/util/mod.rs
+++ b/crates/mun_runtime/benches/util/mod.rs
@@ -1,5 +1,5 @@
+use compiler::{Config, Driver, OptimizationLevel, PathOrInline};
 use mlua::Lua;
-use mun_compiler::{Config, Driver, OptimizationLevel, PathOrInline};
 use mun_runtime::RuntimeBuilder;
 use std::cell::RefCell;
 use std::io::Cursor;
diff --git a/crates/mun_target/src/spec.rs b/crates/mun_target/src/spec.rs
index 640cf2db2..091ef42af 100644
--- a/crates/mun_target/src/spec.rs
+++ b/crates/mun_target/src/spec.rs
@@ -67,6 +67,7 @@ pub struct TargetOptions {
 
     /// Whether the target toolchain is like Windows
     pub is_like_windows: bool,
+    pub is_like_msvc: bool,
 }
 
 impl Default for TargetOptions {
@@ -77,6 +78,7 @@ impl Default for TargetOptions {
             features: "".to_string(),
             dll_prefix: "lib".to_string(),
             is_like_windows: false,
+            is_like_msvc: false,
         }
     }
 }
diff --git a/crates/mun_target/src/spec/windows_msvc_base.rs b/crates/mun_target/src/spec/windows_msvc_base.rs
index a20e2db21..6be332aa5 100644
--- a/crates/mun_target/src/spec/windows_msvc_base.rs
+++ b/crates/mun_target/src/spec/windows_msvc_base.rs
@@ -4,6 +4,7 @@ pub fn opts() -> TargetOptions {
     TargetOptions {
         dll_prefix: "".to_string(),
         is_like_windows: true,
+        is_like_msvc: true,
         ..Default::default()
     }
 }


Test patch:
diff --git a/crates/mun_codegen/src/ir/types/test.rs b/crates/mun_codegen/src/ir/types/test.rs
new file mode 100644
index 000000000..89d266bce
--- /dev/null
+++ b/crates/mun_codegen/src/ir/types/test.rs
@@ -0,0 +1,195 @@
+use crate::value::{ConcreteValueType, Value};
+
+/// A trait implemented by a type to check if its values match its corresponding abi type (T). This
+/// trait can be derived. e.g.:
+///
+/// ```ignore,rust
+/// #[derive(AsValue, TestIsAbiCompatible)]
+/// #[ir_name = "struct.MunTypeInfo"]
+/// #[abi_type(abi::TypeInfo)]
+/// pub struct TypeInfo { }
+/// ```
+///
+/// The procedural macro calls for every field:
+///
+/// ```ignore,rust
+/// self::test::AbiTypeHelper::from_value(&abi_value.<field_name>)
+///     .ir_type::<<field_type>>()
+///     .assert_compatible(<struct_type_name>, <abi_type_name>, <name>);
+/// ```
+pub trait TestIsAbiCompatible<T> {
+    /// Runs a test to see if the implementor is compatible with the specified abi type.
+    fn test(abi_value: &T);
+}
+
+/// A trait that is implemented if a type is ABI compatible with `T`.
+pub trait IsAbiCompatible<T> {}
+
+/// A trait indicating that a type is *not* compatible. This is a helper trait used to determine
+/// if a type is ABI compatible at runtime. By default this trait is implemented for all types.
+pub trait IsNotAbiCompatible {
+    fn assert_compatible(&self, ir_type: &str, abi_type: &str, field_name: &str) {
+        panic!(
+            "the field '{}' on type '{}' is not compatible with the ABI version of the struct ({})",
+            field_name, ir_type, abi_type
+        );
+    }
+}
+impl<T> IsNotAbiCompatible for T {}
+
+/// Helper structs that allows extracting the type of a value.
+pub struct AbiTypeHelper<T>(std::marker::PhantomData<T>);
+pub struct AbiAndIrTypeHelper<T, S>(std::marker::PhantomData<T>, std::marker::PhantomData<S>);
+
+impl<T> AbiTypeHelper<T> {
+    pub fn from_value(_: &T) -> Self {
+        Self(Default::default())
+    }
+
+    pub fn ir_type<S>(&self) -> AbiAndIrTypeHelper<S, T> {
+        AbiAndIrTypeHelper(Default::default(), Default::default())
+    }
+}
+
+impl<S, T: IsAbiCompatible<S>> AbiAndIrTypeHelper<T, S> {
+    pub fn assert_compatible(&self, _ir_type: &str, _abi_type: &str, _field_name: &str) {}
+}
+
+impl IsAbiCompatible<u8> for u8 {}
+impl IsAbiCompatible<u16> for u16 {}
+impl IsAbiCompatible<u32> for u32 {}
+impl IsAbiCompatible<abi::Guid> for abi::Guid {}
+impl IsAbiCompatible<abi::TypeGroup> for abi::TypeGroup {}
+impl IsAbiCompatible<abi::StructMemoryKind> for abi::StructMemoryKind {}
+impl IsAbiCompatible<*const ::std::os::raw::c_char> for *const u8 {}
+impl IsAbiCompatible<*const ::std::os::raw::c_void> for *const fn() {}
+impl<S, T: IsAbiCompatible<S>> IsAbiCompatible<*const S> for *const T {}
+impl<S, T: IsAbiCompatible<S>> IsAbiCompatible<*mut S> for *mut T {}
+impl<'ink, S, T: ConcreteValueType<'ink>> IsAbiCompatible<S> for Value<'ink, T> where
+    T: IsAbiCompatible<S>
+{
+}
+
+#[test]
+#[cfg(test)]
+fn test_type_info_abi_compatible() {
+    let abi_type = abi::TypeInfo {
+        guid: abi::Guid {
+            b: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+        },
+        name: std::ptr::null(),
+        size_in_bits: 0,
+        alignment: 0,
+        group: abi::TypeGroup::FundamentalTypes,
+    };
+
+    super::TypeInfo::test(&abi_type);
+}
+
+#[test]
+#[cfg(test)]
+fn test_function_signature_abi_compatible() {
+    let abi_type = abi::FunctionSignature {
+        arg_types: std::ptr::null(),
+        return_type: std::ptr::null(),
+        num_arg_types: 0,
+    };
+
+    super::FunctionSignature::test(&abi_type);
+}
+
+#[test]
+#[cfg(test)]
+fn test_function_prototype_abi_compatible() {
+    let abi_type = abi::FunctionPrototype {
+        name: std::ptr::null(),
+        signature: abi::FunctionSignature {
+            arg_types: std::ptr::null(),
+            return_type: std::ptr::null(),
+            num_arg_types: 0,
+        },
+    };
+
+    super::FunctionPrototype::test(&abi_type);
+}
+
+#[test]
+#[cfg(test)]
+fn test_function_definition_abi_compatible() {
+    let abi_type = abi::FunctionDefinition {
+        prototype: abi::FunctionPrototype {
+            name: std::ptr::null(),
+            signature: abi::FunctionSignature {
+                arg_types: std::ptr::null(),
+                return_type: std::ptr::null(),
+                num_arg_types: 0,
+            },
+        },
+        fn_ptr: std::ptr::null(),
+    };
+
+    super::FunctionDefinition::test(&abi_type);
+}
+
+#[test]
+#[cfg(test)]
+fn test_struct_info_abi_compatible() {
+    let abi_type = abi::StructInfo {
+        field_names: std::ptr::null(),
+        field_types: std::ptr::null(),
+        field_offsets: std::ptr::null(),
+        num_fields: 0,
+        memory_kind: abi::StructMemoryKind::Value,
+    };
+
+    super::StructInfo::test(&abi_type);
+}
+
+#[test]
+#[cfg(test)]
+fn test_module_info_abi_compatible() {
+    let abi_type = abi::ModuleInfo {
+        path: std::ptr::null(),
+        functions: std::ptr::null(),
+        num_functions: 0,
+        types: std::ptr::null(),
+        num_types: 0,
+    };
+
+    super::ModuleInfo::test(&abi_type);
+}
+
+#[test]
+#[cfg(test)]
+fn test_dispatch_table_abi_compatible() {
+    let abi_type = abi::DispatchTable {
+        prototypes: std::ptr::null(),
+        fn_ptrs: std::ptr::null_mut(),
+        num_entries: 0,
+    };
+
+    super::DispatchTable::test(&abi_type);
+}
+
+#[test]
+#[cfg(test)]
+fn test_assembly_info_abi_compatible() {
+    let abi_type = abi::AssemblyInfo {
+        symbols: abi::ModuleInfo {
+            path: std::ptr::null(),
+            functions: std::ptr::null(),
+            num_functions: 0,
+            types: std::ptr::null(),
+            num_types: 0,
+        },
+        dispatch_table: abi::DispatchTable {
+            prototypes: std::ptr::null(),
+            fn_ptrs: std::ptr::null_mut(),
+            num_entries: 0,
+        },
+        dependencies: std::ptr::null(),
+        num_dependencies: 0,
+    };
+
+    super::AssemblyInfo::test(&abi_type);
+}
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_f32.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_f32.snap
index c4538bcbc..bcfd170b7 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_f32.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_f32.snap
@@ -6,9 +6,9 @@ expression: "pub fn add(a: f32, b: f32) -> f32 { a + b }\npub fn subtract(a: f32
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define float @add(float, float) {
 body:
@@ -45,9 +45,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::f32>::name" = private unnamed_addr constant [10 x i8] c"core::f32\00"
-@"type_info::<core::f32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"P\19b7\A8k\F2\81P\FB\83\F5P\B0\82!", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::f32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::f32>"]
+@"type_info::<core::f32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"P\19b7\A8k\F2\81P\FB\83\F5P\B0\82!", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::f32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::f32>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_f64.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_f64.snap
index 47a2dcda7..25c7e59be 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_f64.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_f64.snap
@@ -6,9 +6,9 @@ expression: "pub fn add(a: f64, b: f64) -> f64 { a + b }\npub fn subtract(a: f64
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define double @add(double, double) {
 body:
@@ -45,9 +45,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::f64>::name" = private unnamed_addr constant [10 x i8] c"core::f64\00"
-@"type_info::<core::f64>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"`\DBF\9C?YJ%G\AD4\9F\D5\92%A", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::f64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::f64>"]
+@"type_info::<core::f64>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"`\DBF\9C?YJ%G\AD4\9F\D5\92%A", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::f64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::f64>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_i128.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_i128.snap
index 28bd7ba2b..1e9ba38cd 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_i128.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_i128.snap
@@ -6,9 +6,9 @@ expression: "pub fn add(a: i128, b: i128) -> i128 { a + b }\npub fn subtract(a:
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i128 @add(i128, i128) {
 body:
@@ -45,9 +45,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i128>::name" = private unnamed_addr constant [11 x i8] c"core::i128\00"
-@"type_info::<core::i128>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\BDkp\09RRM\EBc\02\A0\DB47\A7\E3", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::i128>::name", i32 0, i32 0), i32 128, i8 8, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i128>"]
+@"type_info::<core::i128>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\BDkp\09RRM\EBc\02\A0\DB47\A7\E3", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::i128>::name", i32 0, i32 0), i32 128, i8 8, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i128>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_i16.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_i16.snap
index 497eefa10..7cd070826 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_i16.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_i16.snap
@@ -6,9 +6,9 @@ expression: "pub fn add(a: i16, b: i16) -> i16 { a + b }\npub fn subtract(a: i16
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i16 @add(i16, i16) {
 body:
@@ -45,9 +45,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i16>::name" = private unnamed_addr constant [10 x i8] c"core::i16\00"
-@"type_info::<core::i16>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\05\CD|\F8Bv\D8\B1\E8\8B\8C\D8\8D\B5\89\B0", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i16>::name", i32 0, i32 0), i32 16, i8 2, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i16>"]
+@"type_info::<core::i16>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\05\CD|\F8Bv\D8\B1\E8\8B\8C\D8\8D\B5\89\B0", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i16>::name", i32 0, i32 0), i32 16, i8 2, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i16>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_i32.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_i32.snap
index 26a03b38c..b62aac7d2 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_i32.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_i32.snap
@@ -6,9 +6,9 @@ expression: "pub fn add(a: i32, b: i32) -> i32 { a + b }\npub fn subtract(a: i32
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i32 @add(i32, i32) {
 body:
@@ -45,9 +45,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i32>::name" = private unnamed_addr constant [10 x i8] c"core::i32\00"
-@"type_info::<core::i32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i32>"]
+@"type_info::<core::i32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_i64.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_i64.snap
index 327f2fff6..e5a3e5ae5 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_i64.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_i64.snap
@@ -6,9 +6,9 @@ expression: "pub fn add(a: i64, b: i64) -> i64 { a + b }\npub fn subtract(a: i64
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i64 @add(i64, i64) {
 body:
@@ -45,9 +45,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i64>::name" = private unnamed_addr constant [10 x i8] c"core::i64\00"
-@"type_info::<core::i64>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"G\13;t\97j8\18\D7M\83`\1D\C8\19%", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i64>"]
+@"type_info::<core::i64>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"G\13;t\97j8\18\D7M\83`\1D\C8\19%", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i64>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_i8.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_i8.snap
index 5d8a3319f..e625766a8 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_i8.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_i8.snap
@@ -6,9 +6,9 @@ expression: "pub fn add(a: i8, b: i8) -> i8 { a + b }\npub fn subtract(a: i8, b:
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i8 @add(i8, i8) {
 body:
@@ -45,9 +45,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i8>::name" = private unnamed_addr constant [9 x i8] c"core::i8\00"
-@"type_info::<core::i8>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\EF\C4\B1Z\E7\12\B1\91q\F1\0B\80U\FC\A6\0F", i8* getelementptr inbounds ([9 x i8], [9 x i8]* @"type_info::<core::i8>::name", i32 0, i32 0), i32 8, i8 1, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i8>"]
+@"type_info::<core::i8>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\EF\C4\B1Z\E7\12\B1\91q\F1\0B\80U\FC\A6\0F", i8* getelementptr inbounds ([9 x i8], [9 x i8]* @"type_info::<core::i8>::name", i32 0, i32 0), i32 8, i8 1, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i8>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_u128.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_u128.snap
index 1323e7570..bb5ac2d6c 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_u128.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_u128.snap
@@ -6,9 +6,9 @@ expression: "pub fn add(a: u128, b: u128) -> u128 { a + b }\npub fn subtract(a:
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i128 @add(i128, i128) {
 body:
@@ -45,9 +45,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::u128>::name" = private unnamed_addr constant [11 x i8] c"core::u128\00"
-@"type_info::<core::u128>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\E67\1BU\E9k\95\93d\14}\1C\96S\95\F0", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::u128>::name", i32 0, i32 0), i32 128, i8 8, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::u128>"]
+@"type_info::<core::u128>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\E67\1BU\E9k\95\93d\14}\1C\96S\95\F0", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::u128>::name", i32 0, i32 0), i32 128, i8 8, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::u128>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_u16.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_u16.snap
index ad49b8ff9..c47acf21e 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_u16.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_u16.snap
@@ -6,9 +6,9 @@ expression: "pub fn add(a: u16, b: u16) -> u16 { a + b }\npub fn subtract(a: u16
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i16 @add(i16, i16) {
 body:
@@ -45,9 +45,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::u16>::name" = private unnamed_addr constant [10 x i8] c"core::u16\00"
-@"type_info::<core::u16>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"0\01\BC\BBK\E0\F2\7F&l\01\CD|q\F2\B3", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u16>::name", i32 0, i32 0), i32 16, i8 2, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::u16>"]
+@"type_info::<core::u16>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"0\01\BC\BBK\E0\F2\7F&l\01\CD|q\F2\B3", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u16>::name", i32 0, i32 0), i32 16, i8 2, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::u16>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_u32.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_u32.snap
index fdec67d0d..ab0458f2d 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_u32.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_u32.snap
@@ -6,9 +6,9 @@ expression: "pub fn add(a: u32, b: u32) -> u32 { a + b }\npub fn subtract(a: u32
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i32 @add(i32, i32) {
 body:
@@ -45,9 +45,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::u32>::name" = private unnamed_addr constant [10 x i8] c"core::u32\00"
-@"type_info::<core::u32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"daz5d\A6\BE\88\81=&Y\A1+\C6\1D", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::u32>"]
+@"type_info::<core::u32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"daz5d\A6\BE\88\81=&Y\A1+\C6\1D", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::u32>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_u64.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_u64.snap
index 74e152f2c..ae56d4df6 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_u64.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_u64.snap
@@ -6,9 +6,9 @@ expression: "pub fn add(a: u64, b: u64) -> u64 { a + b }\npub fn subtract(a: u64
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i64 @add(i64, i64) {
 body:
@@ -45,9 +45,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::u64>::name" = private unnamed_addr constant [10 x i8] c"core::u64\00"
-@"type_info::<core::u64>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\A6\E7g \D1\8B\1Aq`\1F\1E\07\BB5@q", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::u64>"]
+@"type_info::<core::u64>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\A6\E7g \D1\8B\1Aq`\1F\1E\07\BB5@q", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::u64>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_u8.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_u8.snap
index f4bf1dfab..2af16b9f2 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_u8.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__arithmetic_op_u8.snap
@@ -6,9 +6,9 @@ expression: "pub fn add(a: u8, b: u8) -> u8 { a + b }\npub fn subtract(a: u8, b:
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i8 @add(i8, i8) {
 body:
@@ -45,9 +45,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::u8>::name" = private unnamed_addr constant [9 x i8] c"core::u8\00"
-@"type_info::<core::u8>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\A0y\A7S\B6(n\F7f&H\E1\F9\AD\04>", i8* getelementptr inbounds ([9 x i8], [9 x i8]* @"type_info::<core::u8>::name", i32 0, i32 0), i32 8, i8 1, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::u8>"]
+@"type_info::<core::u8>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\A0y\A7S\B6(n\F7f&H\E1\F9\AD\04>", i8* getelementptr inbounds ([9 x i8], [9 x i8]* @"type_info::<core::u8>::name", i32 0, i32 0), i32 8, i8 1, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::u8>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_bool.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_bool.snap
index 9c552c983..f605289b6 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_bool.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_bool.snap
@@ -6,9 +6,9 @@ expression: "pub fn assign_bitand(a: bool, b: bool) -> bool {\n    a &= b;\n
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i1 @assign_bitand(i1, i1) {
 body:
@@ -33,9 +33,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::bool>::name" = private unnamed_addr constant [11 x i8] c"core::bool\00"
-@"type_info::<core::bool>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::bool>::name", i32 0, i32 0), i32 1, i8 1, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::bool>"]
+@"type_info::<core::bool>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::bool>::name", i32 0, i32 0), i32 1, i8 1, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::bool>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_i128.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_i128.snap
index 8656a43d1..92c6605c1 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_i128.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_i128.snap
@@ -6,9 +6,9 @@ expression: "pub fn assign_bitand(a: i128, b: i128) -> i128 {\n    a &= b;\n
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i128 @assign_bitand(i128, i128) {
 body:
@@ -33,9 +33,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i128>::name" = private unnamed_addr constant [11 x i8] c"core::i128\00"
-@"type_info::<core::i128>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\BDkp\09RRM\EBc\02\A0\DB47\A7\E3", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::i128>::name", i32 0, i32 0), i32 128, i8 8, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i128>"]
+@"type_info::<core::i128>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\BDkp\09RRM\EBc\02\A0\DB47\A7\E3", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::i128>::name", i32 0, i32 0), i32 128, i8 8, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i128>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_i16.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_i16.snap
index 2b55793c6..ae10666a5 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_i16.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_i16.snap
@@ -6,9 +6,9 @@ expression: "pub fn assign_bitand(a: i16, b: i16) -> i16 {\n    a &= b;\n    a\n
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i16 @assign_bitand(i16, i16) {
 body:
@@ -33,9 +33,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i16>::name" = private unnamed_addr constant [10 x i8] c"core::i16\00"
-@"type_info::<core::i16>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\05\CD|\F8Bv\D8\B1\E8\8B\8C\D8\8D\B5\89\B0", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i16>::name", i32 0, i32 0), i32 16, i8 2, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i16>"]
+@"type_info::<core::i16>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\05\CD|\F8Bv\D8\B1\E8\8B\8C\D8\8D\B5\89\B0", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i16>::name", i32 0, i32 0), i32 16, i8 2, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i16>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_i32.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_i32.snap
index 4f35894c2..015c83305 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_i32.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_i32.snap
@@ -6,9 +6,9 @@ expression: "pub fn assign_bitand(a: i32, b: i32) -> i32 {\n    a &= b;\n    a\n
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i32 @assign_bitand(i32, i32) {
 body:
@@ -33,9 +33,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i32>::name" = private unnamed_addr constant [10 x i8] c"core::i32\00"
-@"type_info::<core::i32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i32>"]
+@"type_info::<core::i32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_i64.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_i64.snap
index c45fbb759..c0d3d0300 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_i64.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_i64.snap
@@ -6,9 +6,9 @@ expression: "pub fn assign_bitand(a: i64, b: i64) -> i64 {\n    a &= b;\n    a\n
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i64 @assign_bitand(i64, i64) {
 body:
@@ -33,9 +33,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i64>::name" = private unnamed_addr constant [10 x i8] c"core::i64\00"
-@"type_info::<core::i64>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"G\13;t\97j8\18\D7M\83`\1D\C8\19%", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i64>"]
+@"type_info::<core::i64>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"G\13;t\97j8\18\D7M\83`\1D\C8\19%", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i64>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_i8.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_i8.snap
index 700f38313..83dfa1cd3 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_i8.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_i8.snap
@@ -6,9 +6,9 @@ expression: "pub fn assign_bitand(a: i8, b: i8) -> i8 {\n    a &= b;\n    a\n}\n
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i8 @assign_bitand(i8, i8) {
 body:
@@ -33,9 +33,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i8>::name" = private unnamed_addr constant [9 x i8] c"core::i8\00"
-@"type_info::<core::i8>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\EF\C4\B1Z\E7\12\B1\91q\F1\0B\80U\FC\A6\0F", i8* getelementptr inbounds ([9 x i8], [9 x i8]* @"type_info::<core::i8>::name", i32 0, i32 0), i32 8, i8 1, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i8>"]
+@"type_info::<core::i8>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\EF\C4\B1Z\E7\12\B1\91q\F1\0B\80U\FC\A6\0F", i8* getelementptr inbounds ([9 x i8], [9 x i8]* @"type_info::<core::i8>::name", i32 0, i32 0), i32 8, i8 1, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i8>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_u128.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_u128.snap
index ac5d8bf00..6d7383fa4 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_u128.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_u128.snap
@@ -6,9 +6,9 @@ expression: "pub fn assign_bitand(a: u128, b: u128) -> u128 {\n    a &= b;\n
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i128 @assign_bitand(i128, i128) {
 body:
@@ -33,9 +33,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::u128>::name" = private unnamed_addr constant [11 x i8] c"core::u128\00"
-@"type_info::<core::u128>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\E67\1BU\E9k\95\93d\14}\1C\96S\95\F0", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::u128>::name", i32 0, i32 0), i32 128, i8 8, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::u128>"]
+@"type_info::<core::u128>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\E67\1BU\E9k\95\93d\14}\1C\96S\95\F0", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::u128>::name", i32 0, i32 0), i32 128, i8 8, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::u128>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_u16.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_u16.snap
index 621ba27d4..59d497ac0 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_u16.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_u16.snap
@@ -6,9 +6,9 @@ expression: "pub fn assign_bitand(a: u16, b: u16) -> u16 {\n    a &= b;\n    a\n
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i16 @assign_bitand(i16, i16) {
 body:
@@ -33,9 +33,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::u16>::name" = private unnamed_addr constant [10 x i8] c"core::u16\00"
-@"type_info::<core::u16>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"0\01\BC\BBK\E0\F2\7F&l\01\CD|q\F2\B3", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u16>::name", i32 0, i32 0), i32 16, i8 2, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::u16>"]
+@"type_info::<core::u16>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"0\01\BC\BBK\E0\F2\7F&l\01\CD|q\F2\B3", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u16>::name", i32 0, i32 0), i32 16, i8 2, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::u16>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_u32.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_u32.snap
index ad4e2d6f1..760d576ca 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_u32.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_u32.snap
@@ -6,9 +6,9 @@ expression: "pub fn assign_bitand(a: u32, b: u32) -> u32 {\n    a &= b;\n    a\n
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i32 @assign_bitand(i32, i32) {
 body:
@@ -33,9 +33,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::u32>::name" = private unnamed_addr constant [10 x i8] c"core::u32\00"
-@"type_info::<core::u32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"daz5d\A6\BE\88\81=&Y\A1+\C6\1D", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::u32>"]
+@"type_info::<core::u32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"daz5d\A6\BE\88\81=&Y\A1+\C6\1D", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::u32>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_u64.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_u64.snap
index 5e140e16f..af992b59b 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_u64.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_u64.snap
@@ -6,9 +6,9 @@ expression: "pub fn assign_bitand(a: u64, b: u64) -> u64 {\n    a &= b;\n    a\n
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i64 @assign_bitand(i64, i64) {
 body:
@@ -33,9 +33,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::u64>::name" = private unnamed_addr constant [10 x i8] c"core::u64\00"
-@"type_info::<core::u64>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\A6\E7g \D1\8B\1Aq`\1F\1E\07\BB5@q", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::u64>"]
+@"type_info::<core::u64>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\A6\E7g \D1\8B\1Aq`\1F\1E\07\BB5@q", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::u64>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_u8.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_u8.snap
index fbf3633cd..a4f794a2c 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_u8.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_bit_op_u8.snap
@@ -6,9 +6,9 @@ expression: "pub fn assign_bitand(a: u8, b: u8) -> u8 {\n    a &= b;\n    a\n}\n
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i8 @assign_bitand(i8, i8) {
 body:
@@ -33,9 +33,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::u8>::name" = private unnamed_addr constant [9 x i8] c"core::u8\00"
-@"type_info::<core::u8>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\A0y\A7S\B6(n\F7f&H\E1\F9\AD\04>", i8* getelementptr inbounds ([9 x i8], [9 x i8]* @"type_info::<core::u8>::name", i32 0, i32 0), i32 8, i8 1, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::u8>"]
+@"type_info::<core::u8>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\A0y\A7S\B6(n\F7f&H\E1\F9\AD\04>", i8* getelementptr inbounds ([9 x i8], [9 x i8]* @"type_info::<core::u8>::name", i32 0, i32 0), i32 8, i8 1, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::u8>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_i128.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_i128.snap
index 703a087cf..b9ed809a2 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_i128.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_i128.snap
@@ -6,9 +6,9 @@ expression: "pub fn assign_leftshift(a: i128, b: i128) -> i128 {\n    a <<= b;\n
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i128 @assign_leftshift(i128, i128) {
 body:
@@ -27,9 +27,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i128>::name" = private unnamed_addr constant [11 x i8] c"core::i128\00"
-@"type_info::<core::i128>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\BDkp\09RRM\EBc\02\A0\DB47\A7\E3", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::i128>::name", i32 0, i32 0), i32 128, i8 8, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i128>"]
+@"type_info::<core::i128>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\BDkp\09RRM\EBc\02\A0\DB47\A7\E3", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::i128>::name", i32 0, i32 0), i32 128, i8 8, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i128>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_i16.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_i16.snap
index 8394d8fd5..cc91345ee 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_i16.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_i16.snap
@@ -6,9 +6,9 @@ expression: "pub fn assign_leftshift(a: i16, b: i16) -> i16 {\n    a <<= b;\n
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i16 @assign_leftshift(i16, i16) {
 body:
@@ -27,9 +27,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i16>::name" = private unnamed_addr constant [10 x i8] c"core::i16\00"
-@"type_info::<core::i16>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\05\CD|\F8Bv\D8\B1\E8\8B\8C\D8\8D\B5\89\B0", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i16>::name", i32 0, i32 0), i32 16, i8 2, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i16>"]
+@"type_info::<core::i16>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\05\CD|\F8Bv\D8\B1\E8\8B\8C\D8\8D\B5\89\B0", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i16>::name", i32 0, i32 0), i32 16, i8 2, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i16>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_i32.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_i32.snap
index e203b9f70..77e9426a6 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_i32.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_i32.snap
@@ -6,9 +6,9 @@ expression: "pub fn assign_leftshift(a: i32, b: i32) -> i32 {\n    a <<= b;\n
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i32 @assign_leftshift(i32, i32) {
 body:
@@ -27,9 +27,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i32>::name" = private unnamed_addr constant [10 x i8] c"core::i32\00"
-@"type_info::<core::i32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i32>"]
+@"type_info::<core::i32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_i64.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_i64.snap
index 3a33cd219..4dc779205 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_i64.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_i64.snap
@@ -6,9 +6,9 @@ expression: "pub fn assign_leftshift(a: i64, b: i64) -> i64 {\n    a <<= b;\n
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i64 @assign_leftshift(i64, i64) {
 body:
@@ -27,9 +27,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i64>::name" = private unnamed_addr constant [10 x i8] c"core::i64\00"
-@"type_info::<core::i64>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"G\13;t\97j8\18\D7M\83`\1D\C8\19%", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i64>"]
+@"type_info::<core::i64>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"G\13;t\97j8\18\D7M\83`\1D\C8\19%", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i64>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_i8.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_i8.snap
index ef32cf1e6..5e6a64195 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_i8.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_i8.snap
@@ -6,9 +6,9 @@ expression: "pub fn assign_leftshift(a: i8, b: i8) -> i8 {\n    a <<= b;\n    a\
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i8 @assign_leftshift(i8, i8) {
 body:
@@ -27,9 +27,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i8>::name" = private unnamed_addr constant [9 x i8] c"core::i8\00"
-@"type_info::<core::i8>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\EF\C4\B1Z\E7\12\B1\91q\F1\0B\80U\FC\A6\0F", i8* getelementptr inbounds ([9 x i8], [9 x i8]* @"type_info::<core::i8>::name", i32 0, i32 0), i32 8, i8 1, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i8>"]
+@"type_info::<core::i8>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\EF\C4\B1Z\E7\12\B1\91q\F1\0B\80U\FC\A6\0F", i8* getelementptr inbounds ([9 x i8], [9 x i8]* @"type_info::<core::i8>::name", i32 0, i32 0), i32 8, i8 1, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i8>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_u128.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_u128.snap
index 725b24ecd..0765fc638 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_u128.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_u128.snap
@@ -6,9 +6,9 @@ expression: "pub fn assign_leftshift(a: u128, b: u128) -> u128 {\n    a <<= b;\n
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i128 @assign_leftshift(i128, i128) {
 body:
@@ -27,9 +27,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::u128>::name" = private unnamed_addr constant [11 x i8] c"core::u128\00"
-@"type_info::<core::u128>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\E67\1BU\E9k\95\93d\14}\1C\96S\95\F0", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::u128>::name", i32 0, i32 0), i32 128, i8 8, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::u128>"]
+@"type_info::<core::u128>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\E67\1BU\E9k\95\93d\14}\1C\96S\95\F0", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::u128>::name", i32 0, i32 0), i32 128, i8 8, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::u128>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_u16.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_u16.snap
index 59966df32..be6c4b2de 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_u16.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_u16.snap
@@ -6,9 +6,9 @@ expression: "pub fn assign_leftshift(a: u16, b: u16) -> u16 {\n    a <<= b;\n
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i16 @assign_leftshift(i16, i16) {
 body:
@@ -27,9 +27,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::u16>::name" = private unnamed_addr constant [10 x i8] c"core::u16\00"
-@"type_info::<core::u16>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"0\01\BC\BBK\E0\F2\7F&l\01\CD|q\F2\B3", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u16>::name", i32 0, i32 0), i32 16, i8 2, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::u16>"]
+@"type_info::<core::u16>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"0\01\BC\BBK\E0\F2\7F&l\01\CD|q\F2\B3", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u16>::name", i32 0, i32 0), i32 16, i8 2, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::u16>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_u32.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_u32.snap
index 92500fa54..414638cbc 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_u32.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_u32.snap
@@ -6,9 +6,9 @@ expression: "pub fn assign_leftshift(a: u32, b: u32) -> u32 {\n    a <<= b;\n
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i32 @assign_leftshift(i32, i32) {
 body:
@@ -27,9 +27,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::u32>::name" = private unnamed_addr constant [10 x i8] c"core::u32\00"
-@"type_info::<core::u32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"daz5d\A6\BE\88\81=&Y\A1+\C6\1D", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::u32>"]
+@"type_info::<core::u32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"daz5d\A6\BE\88\81=&Y\A1+\C6\1D", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::u32>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_u64.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_u64.snap
index 6fcfa777a..63a1e623e 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_u64.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_u64.snap
@@ -6,9 +6,9 @@ expression: "pub fn assign_leftshift(a: u64, b: u64) -> u64 {\n    a <<= b;\n
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i64 @assign_leftshift(i64, i64) {
 body:
@@ -27,9 +27,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::u64>::name" = private unnamed_addr constant [10 x i8] c"core::u64\00"
-@"type_info::<core::u64>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\A6\E7g \D1\8B\1Aq`\1F\1E\07\BB5@q", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::u64>"]
+@"type_info::<core::u64>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\A6\E7g \D1\8B\1Aq`\1F\1E\07\BB5@q", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::u64>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_u8.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_u8.snap
index e477af0a6..f3fd32152 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_u8.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__assign_shift_op_u8.snap
@@ -6,9 +6,9 @@ expression: "pub fn assign_leftshift(a: u8, b: u8) -> u8 {\n    a <<= b;\n    a\
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i8 @assign_leftshift(i8, i8) {
 body:
@@ -27,9 +27,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::u8>::name" = private unnamed_addr constant [9 x i8] c"core::u8\00"
-@"type_info::<core::u8>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\A0y\A7S\B6(n\F7f&H\E1\F9\AD\04>", i8* getelementptr inbounds ([9 x i8], [9 x i8]* @"type_info::<core::u8>::name", i32 0, i32 0), i32 8, i8 1, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::u8>"]
+@"type_info::<core::u8>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\A0y\A7S\B6(n\F7f&H\E1\F9\AD\04>", i8* getelementptr inbounds ([9 x i8], [9 x i8]* @"type_info::<core::u8>::name", i32 0, i32 0), i32 8, i8 1, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::u8>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_bool.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_bool.snap
index b1791cb84..93a0bf7b9 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_bool.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_bool.snap
@@ -6,9 +6,9 @@ expression: "pub fn assign(a: bool, b: bool) -> bool {\n    a = b;\n    a\n}\n//
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i1 @assign(i1, i1) {
 body:
@@ -20,9 +20,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::bool>::name" = private unnamed_addr constant [11 x i8] c"core::bool\00"
-@"type_info::<core::bool>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::bool>::name", i32 0, i32 0), i32 1, i8 1, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::bool>"]
+@"type_info::<core::bool>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::bool>::name", i32 0, i32 0), i32 1, i8 1, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::bool>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_f32.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_f32.snap
index e31671799..934eeaade 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_f32.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_f32.snap
@@ -6,9 +6,9 @@ expression: "pub fn assign(a: f32, b: f32) -> f32 {\n    a = b;\n    a\n}\npub f
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define float @assign(float, float) {
 body:
@@ -50,9 +50,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::f32>::name" = private unnamed_addr constant [10 x i8] c"core::f32\00"
-@"type_info::<core::f32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"P\19b7\A8k\F2\81P\FB\83\F5P\B0\82!", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::f32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::f32>"]
+@"type_info::<core::f32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"P\19b7\A8k\F2\81P\FB\83\F5P\B0\82!", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::f32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::f32>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_f64.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_f64.snap
index 1775b0e93..abc498a44 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_f64.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_f64.snap
@@ -6,9 +6,9 @@ expression: "pub fn assign(a: f64, b: f64) -> f64 {\n    a = b;\n    a\n}\npub f
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define double @assign(double, double) {
 body:
@@ -50,9 +50,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::f64>::name" = private unnamed_addr constant [10 x i8] c"core::f64\00"
-@"type_info::<core::f64>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"`\DBF\9C?YJ%G\AD4\9F\D5\92%A", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::f64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::f64>"]
+@"type_info::<core::f64>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"`\DBF\9C?YJ%G\AD4\9F\D5\92%A", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::f64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::f64>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_i128.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_i128.snap
index f63ca4064..93a3aae7e 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_i128.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_i128.snap
@@ -6,9 +6,9 @@ expression: "pub fn assign(a: i128, b: i128) -> i128 {\n    a = b;\n    a\n}\npu
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i128 @assign(i128, i128) {
 body:
@@ -50,9 +50,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i128>::name" = private unnamed_addr constant [11 x i8] c"core::i128\00"
-@"type_info::<core::i128>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\BDkp\09RRM\EBc\02\A0\DB47\A7\E3", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::i128>::name", i32 0, i32 0), i32 128, i8 8, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i128>"]
+@"type_info::<core::i128>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\BDkp\09RRM\EBc\02\A0\DB47\A7\E3", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::i128>::name", i32 0, i32 0), i32 128, i8 8, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i128>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_i16.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_i16.snap
index bd9549915..dc8be9a13 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_i16.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_i16.snap
@@ -6,9 +6,9 @@ expression: "pub fn assign(a: i16, b: i16) -> i16 {\n    a = b;\n    a\n}\npub f
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i16 @assign(i16, i16) {
 body:
@@ -50,9 +50,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i16>::name" = private unnamed_addr constant [10 x i8] c"core::i16\00"
-@"type_info::<core::i16>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\05\CD|\F8Bv\D8\B1\E8\8B\8C\D8\8D\B5\89\B0", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i16>::name", i32 0, i32 0), i32 16, i8 2, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i16>"]
+@"type_info::<core::i16>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\05\CD|\F8Bv\D8\B1\E8\8B\8C\D8\8D\B5\89\B0", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i16>::name", i32 0, i32 0), i32 16, i8 2, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i16>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_i32.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_i32.snap
index f91933ba7..657749a11 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_i32.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_i32.snap
@@ -6,9 +6,9 @@ expression: "pub fn assign(a: i32, b: i32) -> i32 {\n    a = b;\n    a\n}\npub f
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i32 @assign(i32, i32) {
 body:
@@ -50,9 +50,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i32>::name" = private unnamed_addr constant [10 x i8] c"core::i32\00"
-@"type_info::<core::i32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i32>"]
+@"type_info::<core::i32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_i64.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_i64.snap
index 658bad811..57b12966f 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_i64.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_i64.snap
@@ -6,9 +6,9 @@ expression: "pub fn assign(a: i64, b: i64) -> i64 {\n    a = b;\n    a\n}\npub f
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i64 @assign(i64, i64) {
 body:
@@ -50,9 +50,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i64>::name" = private unnamed_addr constant [10 x i8] c"core::i64\00"
-@"type_info::<core::i64>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"G\13;t\97j8\18\D7M\83`\1D\C8\19%", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i64>"]
+@"type_info::<core::i64>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"G\13;t\97j8\18\D7M\83`\1D\C8\19%", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i64>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_i8.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_i8.snap
index 87086578c..01e1bc273 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_i8.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_i8.snap
@@ -6,9 +6,9 @@ expression: "pub fn assign(a: i8, b: i8) -> i8 {\n    a = b;\n    a\n}\npub fn a
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i8 @assign(i8, i8) {
 body:
@@ -50,9 +50,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i8>::name" = private unnamed_addr constant [9 x i8] c"core::i8\00"
-@"type_info::<core::i8>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\EF\C4\B1Z\E7\12\B1\91q\F1\0B\80U\FC\A6\0F", i8* getelementptr inbounds ([9 x i8], [9 x i8]* @"type_info::<core::i8>::name", i32 0, i32 0), i32 8, i8 1, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i8>"]
+@"type_info::<core::i8>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\EF\C4\B1Z\E7\12\B1\91q\F1\0B\80U\FC\A6\0F", i8* getelementptr inbounds ([9 x i8], [9 x i8]* @"type_info::<core::i8>::name", i32 0, i32 0), i32 8, i8 1, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i8>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_struct.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_struct.snap
index 9bf451647..a2b45f5e4 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_struct.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_struct.snap
@@ -7,13 +7,13 @@ expression: "struct(value) Value(i32, i32);\nstruct(gc) Heap(f64, f64);\n\npub f
 source_filename = "main.mun"
 
 %DispatchTable = type { i8** (i8*, i8*)* }
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 %Value = type { i32, i32 }
 %Heap = type { double, double }
 
 @allocatorHandle = external global i8*
 @dispatchTable = external global %DispatchTable
-@global_type_table = external global [7 x %struct.MunTypeInfo*]
+@global_type_table = external global [7 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define %Value @assign_value(%Value, %Value) {
 body:
@@ -30,8 +30,8 @@ body:
   %deref2 = load %Value, %Value* %mem_ptr1
   %assign_value = call %Value @assign_value(%Value %deref, %Value %deref2)
   %new_ptr = load i8** (i8*, i8*)*, i8** (i8*, i8*)** getelementptr inbounds (%DispatchTable, %DispatchTable* @dispatchTable, i32 0, i32 0)
-  %Value_ptr = load %struct.MunTypeInfo*, %struct.MunTypeInfo** getelementptr inbounds ([7 x %struct.MunTypeInfo*], [7 x %struct.MunTypeInfo*]* @global_type_table, i32 0, i32 1)
-  %type_info_ptr_to_i8_ptr = bitcast %struct.MunTypeInfo* %Value_ptr to i8*
+  %Value_ptr = load %"mun_codegen::ir::types::TypeInfo"*, %"mun_codegen::ir::types::TypeInfo"** getelementptr inbounds ([7 x %"mun_codegen::ir::types::TypeInfo"*], [7 x %"mun_codegen::ir::types::TypeInfo"*]* @global_type_table, i64 0, i64 1)
+  %type_info_ptr_to_i8_ptr = bitcast %"mun_codegen::ir::types::TypeInfo"* %Value_ptr to i8*
   %allocator_handle = load i8*, i8** @allocatorHandle
   %new = call i8** %new_ptr(i8* %type_info_ptr_to_i8_ptr, i8* %allocator_handle)
   %Value_ptr_ptr = bitcast i8** %new to %Value**
@@ -51,34 +51,34 @@ body:
 source_filename = "group_name"
 
 %DispatchTable = type { i8** (i8*, i8*)* }
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
-%struct.MunStructInfo = type { i8**, %struct.MunTypeInfo**, i16*, i16, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::StructInfo" = type { i8**, %"mun_codegen::ir::types::TypeInfo"**, i16*, i16, i8 }
 
 @dispatchTable = global %DispatchTable zeroinitializer
 @"type_info::<core::i32>::name" = private unnamed_addr constant [10 x i8] c"core::i32\00"
-@"type_info::<core::i32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@"type_info::<core::i32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
 @"type_info::<Value>::name" = private unnamed_addr constant [6 x i8] c"Value\00"
 @"struct_info::<Value>::field_names.0" = private unnamed_addr constant [2 x i8] c"0\00"
 @"struct_info::<Value>::field_names.1" = private unnamed_addr constant [2 x i8] c"1\00"
 @"struct_info::<Value>::field_names" = private unnamed_addr constant [2 x i8*] [i8* getelementptr inbounds ([2 x i8], [2 x i8]* @"struct_info::<Value>::field_names.0", i32 0, i32 0), i8* getelementptr inbounds ([2 x i8], [2 x i8]* @"struct_info::<Value>::field_names.1", i32 0, i32 0)]
-@"struct_info::<Value>::field_types" = private unnamed_addr constant [2 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i32>", %struct.MunTypeInfo* @"type_info::<core::i32>"]
+@"struct_info::<Value>::field_types" = private unnamed_addr constant [2 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>", %"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>"]
 @"struct_info::<Value>::field_offsets" = private unnamed_addr constant [2 x i16] [i16 0, i16 4]
-@"type_info::<Value>" = private unnamed_addr constant { %struct.MunTypeInfo, %struct.MunStructInfo } { %struct.MunTypeInfo { [16 x i8] c"(3\1C%q\83\13+L\A6Q\F7\1DX\A6\9B", i8* getelementptr inbounds ([6 x i8], [6 x i8]* @"type_info::<Value>::name", i32 0, i32 0), i32 64, i8 4, i8 1 }, %struct.MunStructInfo { i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"struct_info::<Value>::field_names", i32 0, i32 0), %struct.MunTypeInfo** getelementptr inbounds ([2 x %struct.MunTypeInfo*], [2 x %struct.MunTypeInfo*]* @"struct_info::<Value>::field_types", i32 0, i32 0), i16* getelementptr inbounds ([2 x i16], [2 x i16]* @"struct_info::<Value>::field_offsets", i32 0, i32 0), i16 2, i8 1 } }
+@"type_info::<Value>" = private unnamed_addr constant { %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" } { %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"(3\1C%q\83\13+L\A6Q\F7\1DX\A6\9B", i8* getelementptr inbounds ([6 x i8], [6 x i8]* @"type_info::<Value>::name", i32 0, i32 0), i32 64, i8 4, i8 1 }, %"mun_codegen::ir::types::StructInfo" { i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"struct_info::<Value>::field_names", i32 0, i32 0), %"mun_codegen::ir::types::TypeInfo"** getelementptr inbounds ([2 x %"mun_codegen::ir::types::TypeInfo"*], [2 x %"mun_codegen::ir::types::TypeInfo"*]* @"struct_info::<Value>::field_types", i32 0, i32 0), i16* getelementptr inbounds ([2 x i16], [2 x i16]* @"struct_info::<Value>::field_offsets", i32 0, i32 0), i16 2, i8 1 } }
 @"type_info::<Heap>::name" = private unnamed_addr constant [5 x i8] c"Heap\00"
 @"struct_info::<Heap>::field_names.0" = private unnamed_addr constant [2 x i8] c"0\00"
 @"struct_info::<Heap>::field_names.1" = private unnamed_addr constant [2 x i8] c"1\00"
 @"struct_info::<Heap>::field_names" = private unnamed_addr constant [2 x i8*] [i8* getelementptr inbounds ([2 x i8], [2 x i8]* @"struct_info::<Heap>::field_names.0", i32 0, i32 0), i8* getelementptr inbounds ([2 x i8], [2 x i8]* @"struct_info::<Heap>::field_names.1", i32 0, i32 0)]
 @"type_info::<core::f64>::name" = private unnamed_addr constant [10 x i8] c"core::f64\00"
-@"type_info::<core::f64>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"`\DBF\9C?YJ%G\AD4\9F\D5\92%A", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::f64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
-@"struct_info::<Heap>::field_types" = private unnamed_addr constant [2 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::f64>", %struct.MunTypeInfo* @"type_info::<core::f64>"]
+@"type_info::<core::f64>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"`\DBF\9C?YJ%G\AD4\9F\D5\92%A", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::f64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@"struct_info::<Heap>::field_types" = private unnamed_addr constant [2 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::f64>", %"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::f64>"]
 @"struct_info::<Heap>::field_offsets" = private unnamed_addr constant [2 x i16] [i16 0, i16 8]
-@"type_info::<Heap>" = private unnamed_addr constant { %struct.MunTypeInfo, %struct.MunStructInfo } { %struct.MunTypeInfo { [16 x i8] c"1\1CC\F80{\17\AFq\94\89\FB4\AC\A8\F3", i8* getelementptr inbounds ([5 x i8], [5 x i8]* @"type_info::<Heap>::name", i32 0, i32 0), i32 128, i8 8, i8 1 }, %struct.MunStructInfo { i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"struct_info::<Heap>::field_names", i32 0, i32 0), %struct.MunTypeInfo** getelementptr inbounds ([2 x %struct.MunTypeInfo*], [2 x %struct.MunTypeInfo*]* @"struct_info::<Heap>::field_types", i32 0, i32 0), i16* getelementptr inbounds ([2 x i16], [2 x i16]* @"struct_info::<Heap>::field_offsets", i32 0, i32 0), i16 2, i8 0 } }
+@"type_info::<Heap>" = private unnamed_addr constant { %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" } { %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"1\1CC\F80{\17\AFq\94\89\FB4\AC\A8\F3", i8* getelementptr inbounds ([5 x i8], [5 x i8]* @"type_info::<Heap>::name", i32 0, i32 0), i32 128, i8 8, i8 1 }, %"mun_codegen::ir::types::StructInfo" { i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"struct_info::<Heap>::field_names", i32 0, i32 0), %"mun_codegen::ir::types::TypeInfo"** getelementptr inbounds ([2 x %"mun_codegen::ir::types::TypeInfo"*], [2 x %"mun_codegen::ir::types::TypeInfo"*]* @"struct_info::<Heap>::field_types", i32 0, i32 0), i16* getelementptr inbounds ([2 x i16], [2 x i16]* @"struct_info::<Heap>::field_offsets", i32 0, i32 0), i16 2, i8 0 } }
 @"type_info::<*const TypeInfo>::name" = private unnamed_addr constant [16 x i8] c"*const TypeInfo\00"
-@"type_info::<*const TypeInfo>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"=\A1-\1F\C2\A7\88`d\90\F4\B5\BEE}x", i8* getelementptr inbounds ([16 x i8], [16 x i8]* @"type_info::<*const TypeInfo>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@"type_info::<*const TypeInfo>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"=\A1-\1F\C2\A7\88`d\90\F4\B5\BEE}x", i8* getelementptr inbounds ([16 x i8], [16 x i8]* @"type_info::<*const TypeInfo>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
 @"type_info::<*const *mut core::void>::name" = private unnamed_addr constant [23 x i8] c"*const *mut core::void\00"
-@"type_info::<*const *mut core::void>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\C5fO\BD\84\DF\06\BFd+\B1\9Abv\CE\00", i8* getelementptr inbounds ([23 x i8], [23 x i8]* @"type_info::<*const *mut core::void>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@"type_info::<*const *mut core::void>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\C5fO\BD\84\DF\06\BFd+\B1\9Abv\CE\00", i8* getelementptr inbounds ([23 x i8], [23 x i8]* @"type_info::<*const *mut core::void>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
 @"type_info::<*mut core::void>::name" = private unnamed_addr constant [16 x i8] c"*mut core::void\00"
-@"type_info::<*mut core::void>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\F0Y\22\FC\95\9E\7F\CE\08T\B1\A2\CD\A7\FAz", i8* getelementptr inbounds ([16 x i8], [16 x i8]* @"type_info::<*mut core::void>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
-@global_type_table = constant [7 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i32>", %struct.MunTypeInfo* getelementptr inbounds ({ %struct.MunTypeInfo, %struct.MunStructInfo }, { %struct.MunTypeInfo, %struct.MunStructInfo }* @"type_info::<Value>", i32 0, i32 0), %struct.MunTypeInfo* getelementptr inbounds ({ %struct.MunTypeInfo, %struct.MunStructInfo }, { %struct.MunTypeInfo, %struct.MunStructInfo }* @"type_info::<Heap>", i32 0, i32 0), %struct.MunTypeInfo* @"type_info::<*const TypeInfo>", %struct.MunTypeInfo* @"type_info::<core::f64>", %struct.MunTypeInfo* @"type_info::<*const *mut core::void>", %struct.MunTypeInfo* @"type_info::<*mut core::void>"]
+@"type_info::<*mut core::void>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\F0Y\22\FC\95\9E\7F\CE\08T\B1\A2\CD\A7\FAz", i8* getelementptr inbounds ([16 x i8], [16 x i8]* @"type_info::<*mut core::void>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@global_type_table = constant [7 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>", %"mun_codegen::ir::types::TypeInfo"* getelementptr inbounds ({ %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }, { %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }* @"type_info::<Value>", i32 0, i32 0), %"mun_codegen::ir::types::TypeInfo"* getelementptr inbounds ({ %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }, { %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }* @"type_info::<Heap>", i32 0, i32 0), %"mun_codegen::ir::types::TypeInfo"* @"type_info::<*const TypeInfo>", %"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::f64>", %"mun_codegen::ir::types::TypeInfo"* @"type_info::<*const *mut core::void>", %"mun_codegen::ir::types::TypeInfo"* @"type_info::<*mut core::void>"]
 @allocatorHandle = unnamed_addr global i8* null
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_u128.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_u128.snap
index 6a1e16892..e0ef33d09 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_u128.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_u128.snap
@@ -6,9 +6,9 @@ expression: "pub fn assign(a: u128, b: u128) -> u128 {\n    a = b;\n    a\n}\npu
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i128 @assign(i128, i128) {
 body:
@@ -50,9 +50,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::u128>::name" = private unnamed_addr constant [11 x i8] c"core::u128\00"
-@"type_info::<core::u128>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\E67\1BU\E9k\95\93d\14}\1C\96S\95\F0", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::u128>::name", i32 0, i32 0), i32 128, i8 8, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::u128>"]
+@"type_info::<core::u128>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\E67\1BU\E9k\95\93d\14}\1C\96S\95\F0", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::u128>::name", i32 0, i32 0), i32 128, i8 8, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::u128>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_u16.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_u16.snap
index eeb718026..65718baa2 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_u16.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_u16.snap
@@ -6,9 +6,9 @@ expression: "pub fn assign(a: u16, b: u16) -> u16 {\n    a = b;\n    a\n}\npub f
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i16 @assign(i16, i16) {
 body:
@@ -50,9 +50,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::u16>::name" = private unnamed_addr constant [10 x i8] c"core::u16\00"
-@"type_info::<core::u16>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"0\01\BC\BBK\E0\F2\7F&l\01\CD|q\F2\B3", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u16>::name", i32 0, i32 0), i32 16, i8 2, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::u16>"]
+@"type_info::<core::u16>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"0\01\BC\BBK\E0\F2\7F&l\01\CD|q\F2\B3", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u16>::name", i32 0, i32 0), i32 16, i8 2, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::u16>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_u32.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_u32.snap
index e5c79a1db..f8ed6686b 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_u32.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_u32.snap
@@ -6,9 +6,9 @@ expression: "pub fn assign(a: u32, b: u32) -> u32 {\n    a = b;\n    a\n}\npub f
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i32 @assign(i32, i32) {
 body:
@@ -50,9 +50,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::u32>::name" = private unnamed_addr constant [10 x i8] c"core::u32\00"
-@"type_info::<core::u32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"daz5d\A6\BE\88\81=&Y\A1+\C6\1D", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::u32>"]
+@"type_info::<core::u32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"daz5d\A6\BE\88\81=&Y\A1+\C6\1D", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::u32>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_u64.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_u64.snap
index e364ecf54..31686e7bf 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_u64.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_u64.snap
@@ -6,9 +6,9 @@ expression: "pub fn assign(a: u64, b: u64) -> u64 {\n    a = b;\n    a\n}\npub f
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i64 @assign(i64, i64) {
 body:
@@ -50,9 +50,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::u64>::name" = private unnamed_addr constant [10 x i8] c"core::u64\00"
-@"type_info::<core::u64>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\A6\E7g \D1\8B\1Aq`\1F\1E\07\BB5@q", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::u64>"]
+@"type_info::<core::u64>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\A6\E7g \D1\8B\1Aq`\1F\1E\07\BB5@q", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::u64>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_u8.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_u8.snap
index 7a9c668e5..801512206 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_u8.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__assignment_op_u8.snap
@@ -6,9 +6,9 @@ expression: "pub fn assign(a: u8, b: u8) -> u8 {\n    a = b;\n    a\n}\npub fn a
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i8 @assign(i8, i8) {
 body:
@@ -50,9 +50,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::u8>::name" = private unnamed_addr constant [9 x i8] c"core::u8\00"
-@"type_info::<core::u8>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\A0y\A7S\B6(n\F7f&H\E1\F9\AD\04>", i8* getelementptr inbounds ([9 x i8], [9 x i8]* @"type_info::<core::u8>::name", i32 0, i32 0), i32 8, i8 1, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::u8>"]
+@"type_info::<core::u8>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\A0y\A7S\B6(n\F7f&H\E1\F9\AD\04>", i8* getelementptr inbounds ([9 x i8], [9 x i8]* @"type_info::<core::u8>::name", i32 0, i32 0), i32 8, i8 1, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::u8>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_bool.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_bool.snap
index 657ec8dd3..b71ad2af9 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_bool.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_bool.snap
@@ -6,9 +6,9 @@ expression: "pub fn not(a: bool) -> bool { !a }\npub fn bitand(a: bool, b: bool)
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i1 @not(i1) {
 body:
@@ -39,9 +39,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::bool>::name" = private unnamed_addr constant [11 x i8] c"core::bool\00"
-@"type_info::<core::bool>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::bool>::name", i32 0, i32 0), i32 1, i8 1, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::bool>"]
+@"type_info::<core::bool>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::bool>::name", i32 0, i32 0), i32 1, i8 1, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::bool>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_i128.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_i128.snap
index a2aabe411..bf25bc396 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_i128.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_i128.snap
@@ -6,9 +6,9 @@ expression: "pub fn not(a: i128) -> i128 { !a }\npub fn bitand(a: i128, b: i128)
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i128 @not(i128) {
 body:
@@ -39,9 +39,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i128>::name" = private unnamed_addr constant [11 x i8] c"core::i128\00"
-@"type_info::<core::i128>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\BDkp\09RRM\EBc\02\A0\DB47\A7\E3", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::i128>::name", i32 0, i32 0), i32 128, i8 8, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i128>"]
+@"type_info::<core::i128>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\BDkp\09RRM\EBc\02\A0\DB47\A7\E3", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::i128>::name", i32 0, i32 0), i32 128, i8 8, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i128>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_i16.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_i16.snap
index ac1fceab3..bd2433cf6 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_i16.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_i16.snap
@@ -6,9 +6,9 @@ expression: "pub fn not(a: i16) -> i16 { !a }\npub fn bitand(a: i16, b: i16) ->
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i16 @not(i16) {
 body:
@@ -39,9 +39,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i16>::name" = private unnamed_addr constant [10 x i8] c"core::i16\00"
-@"type_info::<core::i16>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\05\CD|\F8Bv\D8\B1\E8\8B\8C\D8\8D\B5\89\B0", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i16>::name", i32 0, i32 0), i32 16, i8 2, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i16>"]
+@"type_info::<core::i16>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\05\CD|\F8Bv\D8\B1\E8\8B\8C\D8\8D\B5\89\B0", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i16>::name", i32 0, i32 0), i32 16, i8 2, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i16>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_i32.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_i32.snap
index b1f2be5b8..ae6d41140 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_i32.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_i32.snap
@@ -6,9 +6,9 @@ expression: "pub fn not(a: i32) -> i32 { !a }\npub fn bitand(a: i32, b: i32) ->
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i32 @not(i32) {
 body:
@@ -39,9 +39,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i32>::name" = private unnamed_addr constant [10 x i8] c"core::i32\00"
-@"type_info::<core::i32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i32>"]
+@"type_info::<core::i32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_i64.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_i64.snap
index 6aa89ed43..a98e0b22f 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_i64.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_i64.snap
@@ -6,9 +6,9 @@ expression: "pub fn not(a: i64) -> i64 { !a }\npub fn bitand(a: i64, b: i64) ->
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i64 @not(i64) {
 body:
@@ -39,9 +39,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i64>::name" = private unnamed_addr constant [10 x i8] c"core::i64\00"
-@"type_info::<core::i64>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"G\13;t\97j8\18\D7M\83`\1D\C8\19%", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i64>"]
+@"type_info::<core::i64>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"G\13;t\97j8\18\D7M\83`\1D\C8\19%", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i64>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_i8.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_i8.snap
index 1b998a120..657d961d5 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_i8.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_i8.snap
@@ -6,9 +6,9 @@ expression: "pub fn not(a: i8) -> i8 { !a }\npub fn bitand(a: i8, b: i8) -> i8 {
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i8 @not(i8) {
 body:
@@ -39,9 +39,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i8>::name" = private unnamed_addr constant [9 x i8] c"core::i8\00"
-@"type_info::<core::i8>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\EF\C4\B1Z\E7\12\B1\91q\F1\0B\80U\FC\A6\0F", i8* getelementptr inbounds ([9 x i8], [9 x i8]* @"type_info::<core::i8>::name", i32 0, i32 0), i32 8, i8 1, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i8>"]
+@"type_info::<core::i8>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\EF\C4\B1Z\E7\12\B1\91q\F1\0B\80U\FC\A6\0F", i8* getelementptr inbounds ([9 x i8], [9 x i8]* @"type_info::<core::i8>::name", i32 0, i32 0), i32 8, i8 1, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i8>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_u128.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_u128.snap
index 2fb9df4e0..7bf46fe84 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_u128.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_u128.snap
@@ -6,9 +6,9 @@ expression: "pub fn not(a: u128) -> u128 { !a }\npub fn bitand(a: u128, b: u128)
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i128 @not(i128) {
 body:
@@ -39,9 +39,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::u128>::name" = private unnamed_addr constant [11 x i8] c"core::u128\00"
-@"type_info::<core::u128>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\E67\1BU\E9k\95\93d\14}\1C\96S\95\F0", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::u128>::name", i32 0, i32 0), i32 128, i8 8, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::u128>"]
+@"type_info::<core::u128>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\E67\1BU\E9k\95\93d\14}\1C\96S\95\F0", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::u128>::name", i32 0, i32 0), i32 128, i8 8, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::u128>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_u16.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_u16.snap
index 6bd724ef2..a99396013 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_u16.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_u16.snap
@@ -6,9 +6,9 @@ expression: "pub fn not(a: u16) -> u16 { !a }\npub fn bitand(a: u16, b: u16) ->
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i16 @not(i16) {
 body:
@@ -39,9 +39,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::u16>::name" = private unnamed_addr constant [10 x i8] c"core::u16\00"
-@"type_info::<core::u16>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"0\01\BC\BBK\E0\F2\7F&l\01\CD|q\F2\B3", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u16>::name", i32 0, i32 0), i32 16, i8 2, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::u16>"]
+@"type_info::<core::u16>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"0\01\BC\BBK\E0\F2\7F&l\01\CD|q\F2\B3", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u16>::name", i32 0, i32 0), i32 16, i8 2, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::u16>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_u32.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_u32.snap
index 502bdf3c9..5106e8111 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_u32.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_u32.snap
@@ -6,9 +6,9 @@ expression: "pub fn not(a: u32) -> u32 { !a }\npub fn bitand(a: u32, b: u32) ->
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i32 @not(i32) {
 body:
@@ -39,9 +39,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::u32>::name" = private unnamed_addr constant [10 x i8] c"core::u32\00"
-@"type_info::<core::u32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"daz5d\A6\BE\88\81=&Y\A1+\C6\1D", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::u32>"]
+@"type_info::<core::u32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"daz5d\A6\BE\88\81=&Y\A1+\C6\1D", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::u32>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_u64.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_u64.snap
index 941853d66..d59a5e38b 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_u64.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_u64.snap
@@ -6,9 +6,9 @@ expression: "pub fn not(a: u64) -> u64 { !a }\npub fn bitand(a: u64, b: u64) ->
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i64 @not(i64) {
 body:
@@ -39,9 +39,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::u64>::name" = private unnamed_addr constant [10 x i8] c"core::u64\00"
-@"type_info::<core::u64>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\A6\E7g \D1\8B\1Aq`\1F\1E\07\BB5@q", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::u64>"]
+@"type_info::<core::u64>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\A6\E7g \D1\8B\1Aq`\1F\1E\07\BB5@q", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::u64>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_u8.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_u8.snap
index 4ec4f10ee..07df38e86 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_u8.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__bit_op_u8.snap
@@ -6,9 +6,9 @@ expression: "pub fn not(a: u8) -> u8 { !a }\npub fn bitand(a: u8, b: u8) -> u8 {
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i8 @not(i8) {
 body:
@@ -39,9 +39,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::u8>::name" = private unnamed_addr constant [9 x i8] c"core::u8\00"
-@"type_info::<core::u8>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\A0y\A7S\B6(n\F7f&H\E1\F9\AD\04>", i8* getelementptr inbounds ([9 x i8], [9 x i8]* @"type_info::<core::u8>::name", i32 0, i32 0), i32 8, i8 1, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::u8>"]
+@"type_info::<core::u8>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\A0y\A7S\B6(n\F7f&H\E1\F9\AD\04>", i8* getelementptr inbounds ([9 x i8], [9 x i8]* @"type_info::<core::u8>::name", i32 0, i32 0), i32 8, i8 1, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::u8>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_bool.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_bool.snap
index cc2e04f3f..709f8f910 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_bool.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_bool.snap
@@ -6,9 +6,9 @@ expression: "pub fn equals(a: bool, b: bool) -> bool { a == b }\npub fn not_equa
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i1 @equals(i1, i1) {
 body:
@@ -51,9 +51,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::bool>::name" = private unnamed_addr constant [11 x i8] c"core::bool\00"
-@"type_info::<core::bool>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::bool>::name", i32 0, i32 0), i32 1, i8 1, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::bool>"]
+@"type_info::<core::bool>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::bool>::name", i32 0, i32 0), i32 1, i8 1, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::bool>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_f32.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_f32.snap
index 7b6bc708a..f2e570c09 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_f32.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_f32.snap
@@ -6,9 +6,9 @@ expression: "pub fn equals(a: f32, b: f32) -> bool { a == b }\npub fn not_equal(
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [2 x %struct.MunTypeInfo*]
+@global_type_table = external global [2 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i1 @equals(float, float) {
 body:
@@ -51,11 +51,11 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::f32>::name" = private unnamed_addr constant [10 x i8] c"core::f32\00"
-@"type_info::<core::f32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"P\19b7\A8k\F2\81P\FB\83\F5P\B0\82!", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::f32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@"type_info::<core::f32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"P\19b7\A8k\F2\81P\FB\83\F5P\B0\82!", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::f32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
 @"type_info::<core::bool>::name" = private unnamed_addr constant [11 x i8] c"core::bool\00"
-@"type_info::<core::bool>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::bool>::name", i32 0, i32 0), i32 1, i8 1, i8 0 }
-@global_type_table = constant [2 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::f32>", %struct.MunTypeInfo* @"type_info::<core::bool>"]
+@"type_info::<core::bool>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::bool>::name", i32 0, i32 0), i32 1, i8 1, i8 0 }
+@global_type_table = constant [2 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::f32>", %"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::bool>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_f64.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_f64.snap
index 8dc39af4d..f52c4b29b 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_f64.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_f64.snap
@@ -6,9 +6,9 @@ expression: "pub fn equals(a: f64, b: f64) -> bool { a == b }\npub fn not_equal(
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [2 x %struct.MunTypeInfo*]
+@global_type_table = external global [2 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i1 @equals(double, double) {
 body:
@@ -51,11 +51,11 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::f64>::name" = private unnamed_addr constant [10 x i8] c"core::f64\00"
-@"type_info::<core::f64>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"`\DBF\9C?YJ%G\AD4\9F\D5\92%A", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::f64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@"type_info::<core::f64>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"`\DBF\9C?YJ%G\AD4\9F\D5\92%A", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::f64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
 @"type_info::<core::bool>::name" = private unnamed_addr constant [11 x i8] c"core::bool\00"
-@"type_info::<core::bool>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::bool>::name", i32 0, i32 0), i32 1, i8 1, i8 0 }
-@global_type_table = constant [2 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::f64>", %struct.MunTypeInfo* @"type_info::<core::bool>"]
+@"type_info::<core::bool>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::bool>::name", i32 0, i32 0), i32 1, i8 1, i8 0 }
+@global_type_table = constant [2 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::f64>", %"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::bool>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_i128.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_i128.snap
index b4c77e1fa..14700d513 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_i128.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_i128.snap
@@ -6,9 +6,9 @@ expression: "pub fn equals(a: i128, b: i128) -> bool { a == b }\npub fn not_equa
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [2 x %struct.MunTypeInfo*]
+@global_type_table = external global [2 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i1 @equals(i128, i128) {
 body:
@@ -51,11 +51,11 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::bool>::name" = private unnamed_addr constant [11 x i8] c"core::bool\00"
-@"type_info::<core::bool>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::bool>::name", i32 0, i32 0), i32 1, i8 1, i8 0 }
+@"type_info::<core::bool>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::bool>::name", i32 0, i32 0), i32 1, i8 1, i8 0 }
 @"type_info::<core::i128>::name" = private unnamed_addr constant [11 x i8] c"core::i128\00"
-@"type_info::<core::i128>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\BDkp\09RRM\EBc\02\A0\DB47\A7\E3", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::i128>::name", i32 0, i32 0), i32 128, i8 8, i8 0 }
-@global_type_table = constant [2 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::bool>", %struct.MunTypeInfo* @"type_info::<core::i128>"]
+@"type_info::<core::i128>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\BDkp\09RRM\EBc\02\A0\DB47\A7\E3", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::i128>::name", i32 0, i32 0), i32 128, i8 8, i8 0 }
+@global_type_table = constant [2 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::bool>", %"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i128>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_i16.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_i16.snap
index 50becc604..b6e2897a2 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_i16.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_i16.snap
@@ -6,9 +6,9 @@ expression: "pub fn equals(a: i16, b: i16) -> bool { a == b }\npub fn not_equal(
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [2 x %struct.MunTypeInfo*]
+@global_type_table = external global [2 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i1 @equals(i16, i16) {
 body:
@@ -51,11 +51,11 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i16>::name" = private unnamed_addr constant [10 x i8] c"core::i16\00"
-@"type_info::<core::i16>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\05\CD|\F8Bv\D8\B1\E8\8B\8C\D8\8D\B5\89\B0", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i16>::name", i32 0, i32 0), i32 16, i8 2, i8 0 }
+@"type_info::<core::i16>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\05\CD|\F8Bv\D8\B1\E8\8B\8C\D8\8D\B5\89\B0", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i16>::name", i32 0, i32 0), i32 16, i8 2, i8 0 }
 @"type_info::<core::bool>::name" = private unnamed_addr constant [11 x i8] c"core::bool\00"
-@"type_info::<core::bool>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::bool>::name", i32 0, i32 0), i32 1, i8 1, i8 0 }
-@global_type_table = constant [2 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i16>", %struct.MunTypeInfo* @"type_info::<core::bool>"]
+@"type_info::<core::bool>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::bool>::name", i32 0, i32 0), i32 1, i8 1, i8 0 }
+@global_type_table = constant [2 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i16>", %"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::bool>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_i32.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_i32.snap
index 6555c7b66..9a79e0cf1 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_i32.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_i32.snap
@@ -6,9 +6,9 @@ expression: "pub fn equals(a: i32, b: i32) -> bool { a == b }\npub fn not_equal(
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [2 x %struct.MunTypeInfo*]
+@global_type_table = external global [2 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i1 @equals(i32, i32) {
 body:
@@ -51,11 +51,11 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i32>::name" = private unnamed_addr constant [10 x i8] c"core::i32\00"
-@"type_info::<core::i32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@"type_info::<core::i32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
 @"type_info::<core::bool>::name" = private unnamed_addr constant [11 x i8] c"core::bool\00"
-@"type_info::<core::bool>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::bool>::name", i32 0, i32 0), i32 1, i8 1, i8 0 }
-@global_type_table = constant [2 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i32>", %struct.MunTypeInfo* @"type_info::<core::bool>"]
+@"type_info::<core::bool>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::bool>::name", i32 0, i32 0), i32 1, i8 1, i8 0 }
+@global_type_table = constant [2 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>", %"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::bool>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_i64.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_i64.snap
index f8dfa49d2..f52b6ec42 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_i64.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_i64.snap
@@ -6,9 +6,9 @@ expression: "pub fn equals(a: i64, b: i64) -> bool { a == b }\npub fn not_equal(
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [2 x %struct.MunTypeInfo*]
+@global_type_table = external global [2 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i1 @equals(i64, i64) {
 body:
@@ -51,11 +51,11 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i64>::name" = private unnamed_addr constant [10 x i8] c"core::i64\00"
-@"type_info::<core::i64>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"G\13;t\97j8\18\D7M\83`\1D\C8\19%", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@"type_info::<core::i64>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"G\13;t\97j8\18\D7M\83`\1D\C8\19%", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
 @"type_info::<core::bool>::name" = private unnamed_addr constant [11 x i8] c"core::bool\00"
-@"type_info::<core::bool>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::bool>::name", i32 0, i32 0), i32 1, i8 1, i8 0 }
-@global_type_table = constant [2 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i64>", %struct.MunTypeInfo* @"type_info::<core::bool>"]
+@"type_info::<core::bool>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::bool>::name", i32 0, i32 0), i32 1, i8 1, i8 0 }
+@global_type_table = constant [2 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i64>", %"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::bool>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_i8.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_i8.snap
index a15e4cf47..60ea40f6d 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_i8.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_i8.snap
@@ -6,9 +6,9 @@ expression: "pub fn equals(a: i8, b: i8) -> bool { a == b }\npub fn not_equal(a:
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [2 x %struct.MunTypeInfo*]
+@global_type_table = external global [2 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i1 @equals(i8, i8) {
 body:
@@ -51,11 +51,11 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::bool>::name" = private unnamed_addr constant [11 x i8] c"core::bool\00"
-@"type_info::<core::bool>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::bool>::name", i32 0, i32 0), i32 1, i8 1, i8 0 }
+@"type_info::<core::bool>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::bool>::name", i32 0, i32 0), i32 1, i8 1, i8 0 }
 @"type_info::<core::i8>::name" = private unnamed_addr constant [9 x i8] c"core::i8\00"
-@"type_info::<core::i8>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\EF\C4\B1Z\E7\12\B1\91q\F1\0B\80U\FC\A6\0F", i8* getelementptr inbounds ([9 x i8], [9 x i8]* @"type_info::<core::i8>::name", i32 0, i32 0), i32 8, i8 1, i8 0 }
-@global_type_table = constant [2 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::bool>", %struct.MunTypeInfo* @"type_info::<core::i8>"]
+@"type_info::<core::i8>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\EF\C4\B1Z\E7\12\B1\91q\F1\0B\80U\FC\A6\0F", i8* getelementptr inbounds ([9 x i8], [9 x i8]* @"type_info::<core::i8>::name", i32 0, i32 0), i32 8, i8 1, i8 0 }
+@global_type_table = constant [2 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::bool>", %"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i8>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_u128.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_u128.snap
index 578be6fee..fcdb8eef9 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_u128.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_u128.snap
@@ -6,9 +6,9 @@ expression: "pub fn equals(a: u128, b: u128) -> bool { a == b }\npub fn not_equa
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [2 x %struct.MunTypeInfo*]
+@global_type_table = external global [2 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i1 @equals(i128, i128) {
 body:
@@ -51,11 +51,11 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::bool>::name" = private unnamed_addr constant [11 x i8] c"core::bool\00"
-@"type_info::<core::bool>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::bool>::name", i32 0, i32 0), i32 1, i8 1, i8 0 }
+@"type_info::<core::bool>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::bool>::name", i32 0, i32 0), i32 1, i8 1, i8 0 }
 @"type_info::<core::u128>::name" = private unnamed_addr constant [11 x i8] c"core::u128\00"
-@"type_info::<core::u128>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\E67\1BU\E9k\95\93d\14}\1C\96S\95\F0", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::u128>::name", i32 0, i32 0), i32 128, i8 8, i8 0 }
-@global_type_table = constant [2 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::bool>", %struct.MunTypeInfo* @"type_info::<core::u128>"]
+@"type_info::<core::u128>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\E67\1BU\E9k\95\93d\14}\1C\96S\95\F0", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::u128>::name", i32 0, i32 0), i32 128, i8 8, i8 0 }
+@global_type_table = constant [2 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::bool>", %"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::u128>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_u16.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_u16.snap
index a59c86b72..d4e938eb5 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_u16.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_u16.snap
@@ -6,9 +6,9 @@ expression: "pub fn equals(a: u16, b: u16) -> bool { a == b }\npub fn not_equal(
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [2 x %struct.MunTypeInfo*]
+@global_type_table = external global [2 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i1 @equals(i16, i16) {
 body:
@@ -51,11 +51,11 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::u16>::name" = private unnamed_addr constant [10 x i8] c"core::u16\00"
-@"type_info::<core::u16>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"0\01\BC\BBK\E0\F2\7F&l\01\CD|q\F2\B3", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u16>::name", i32 0, i32 0), i32 16, i8 2, i8 0 }
+@"type_info::<core::u16>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"0\01\BC\BBK\E0\F2\7F&l\01\CD|q\F2\B3", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u16>::name", i32 0, i32 0), i32 16, i8 2, i8 0 }
 @"type_info::<core::bool>::name" = private unnamed_addr constant [11 x i8] c"core::bool\00"
-@"type_info::<core::bool>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::bool>::name", i32 0, i32 0), i32 1, i8 1, i8 0 }
-@global_type_table = constant [2 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::u16>", %struct.MunTypeInfo* @"type_info::<core::bool>"]
+@"type_info::<core::bool>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::bool>::name", i32 0, i32 0), i32 1, i8 1, i8 0 }
+@global_type_table = constant [2 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::u16>", %"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::bool>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_u32.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_u32.snap
index 0e56e4e97..2f16e5f44 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_u32.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_u32.snap
@@ -6,9 +6,9 @@ expression: "pub fn equals(a: u32, b: u32) -> bool { a == b }\npub fn not_equal(
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [2 x %struct.MunTypeInfo*]
+@global_type_table = external global [2 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i1 @equals(i32, i32) {
 body:
@@ -51,11 +51,11 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::u32>::name" = private unnamed_addr constant [10 x i8] c"core::u32\00"
-@"type_info::<core::u32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"daz5d\A6\BE\88\81=&Y\A1+\C6\1D", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@"type_info::<core::u32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"daz5d\A6\BE\88\81=&Y\A1+\C6\1D", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
 @"type_info::<core::bool>::name" = private unnamed_addr constant [11 x i8] c"core::bool\00"
-@"type_info::<core::bool>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::bool>::name", i32 0, i32 0), i32 1, i8 1, i8 0 }
-@global_type_table = constant [2 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::u32>", %struct.MunTypeInfo* @"type_info::<core::bool>"]
+@"type_info::<core::bool>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::bool>::name", i32 0, i32 0), i32 1, i8 1, i8 0 }
+@global_type_table = constant [2 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::u32>", %"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::bool>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_u64.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_u64.snap
index 61f60370d..fbdac9167 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_u64.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_u64.snap
@@ -6,9 +6,9 @@ expression: "pub fn equals(a: u64, b: u64) -> bool { a == b }\npub fn not_equal(
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [2 x %struct.MunTypeInfo*]
+@global_type_table = external global [2 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i1 @equals(i64, i64) {
 body:
@@ -51,11 +51,11 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::bool>::name" = private unnamed_addr constant [11 x i8] c"core::bool\00"
-@"type_info::<core::bool>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::bool>::name", i32 0, i32 0), i32 1, i8 1, i8 0 }
+@"type_info::<core::bool>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::bool>::name", i32 0, i32 0), i32 1, i8 1, i8 0 }
 @"type_info::<core::u64>::name" = private unnamed_addr constant [10 x i8] c"core::u64\00"
-@"type_info::<core::u64>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\A6\E7g \D1\8B\1Aq`\1F\1E\07\BB5@q", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
-@global_type_table = constant [2 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::bool>", %struct.MunTypeInfo* @"type_info::<core::u64>"]
+@"type_info::<core::u64>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\A6\E7g \D1\8B\1Aq`\1F\1E\07\BB5@q", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@global_type_table = constant [2 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::bool>", %"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::u64>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_u8.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_u8.snap
index ebd94f5dc..9ff89a685 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_u8.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__compare_op_u8.snap
@@ -6,9 +6,9 @@ expression: "pub fn equals(a: u8, b: u8) -> bool { a == b }\npub fn not_equal(a:
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [2 x %struct.MunTypeInfo*]
+@global_type_table = external global [2 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i1 @equals(i8, i8) {
 body:
@@ -51,11 +51,11 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::bool>::name" = private unnamed_addr constant [11 x i8] c"core::bool\00"
-@"type_info::<core::bool>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::bool>::name", i32 0, i32 0), i32 1, i8 1, i8 0 }
+@"type_info::<core::bool>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::bool>::name", i32 0, i32 0), i32 1, i8 1, i8 0 }
 @"type_info::<core::u8>::name" = private unnamed_addr constant [9 x i8] c"core::u8\00"
-@"type_info::<core::u8>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\A0y\A7S\B6(n\F7f&H\E1\F9\AD\04>", i8* getelementptr inbounds ([9 x i8], [9 x i8]* @"type_info::<core::u8>::name", i32 0, i32 0), i32 8, i8 1, i8 0 }
-@global_type_table = constant [2 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::bool>", %struct.MunTypeInfo* @"type_info::<core::u8>"]
+@"type_info::<core::u8>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\A0y\A7S\B6(n\F7f&H\E1\F9\AD\04>", i8* getelementptr inbounds ([9 x i8], [9 x i8]* @"type_info::<core::u8>::name", i32 0, i32 0), i32 8, i8 1, i8 0 }
+@global_type_table = constant [2 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::bool>", %"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::u8>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__conditional_return_expr.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__conditional_return_expr.snap
index be231216c..6c66269e6 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__conditional_return_expr.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__conditional_return_expr.snap
@@ -6,9 +6,9 @@ expression: "pub fn main(a:i32) -> i32 {\n    if a > 4 {\n        return a;\n
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i32 @main(i32) {
 body:
@@ -28,9 +28,9 @@ if_merge:                                         ; preds = %body
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i32>::name" = private unnamed_addr constant [10 x i8] c"core::i32\00"
-@"type_info::<core::i32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i32>"]
+@"type_info::<core::i32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__extern_fn.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__extern_fn.snap
index 7e46dc95e..ded97102c 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__extern_fn.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__extern_fn.snap
@@ -7,10 +7,10 @@ expression: "extern fn add(a:i32, b:i32) -> i32;\npub fn main() {\n    add(3,4);
 source_filename = "main.mun"
 
 %DispatchTable = type { i32 (i32, i32)* }
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @dispatchTable = external global %DispatchTable
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define void @main() {
 body:
@@ -25,10 +25,10 @@ body:
 source_filename = "group_name"
 
 %DispatchTable = type { i32 (i32, i32)* }
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @dispatchTable = global %DispatchTable zeroinitializer
 @"type_info::<core::i32>::name" = private unnamed_addr constant [10 x i8] c"core::i32\00"
-@"type_info::<core::i32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i32>"]
+@"type_info::<core::i32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__fibonacci.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__fibonacci.snap
index 1685aa56b..ed69429b1 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__fibonacci.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__fibonacci.snap
@@ -7,10 +7,10 @@ expression: "pub fn fibonacci(n:i32) -> i32 {\n    if n <= 1 {\n        n\n    }
 source_filename = "main.mun"
 
 %DispatchTable = type { i32 (i32)* }
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @dispatchTable = external global %DispatchTable
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i32 @fibonacci(i32) {
 body:
@@ -38,12 +38,12 @@ if_merge:                                         ; preds = %body, %else
 source_filename = "group_name"
 
 %DispatchTable = type { i32 (i32)* }
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @dispatchTable = global %DispatchTable { i32 (i32)* @fibonacci }
 @"type_info::<core::i32>::name" = private unnamed_addr constant [10 x i8] c"core::i32\00"
-@"type_info::<core::i32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i32>"]
+@"type_info::<core::i32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>"]
 
 declare i32 @fibonacci(i32)
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__fibonacci_loop.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__fibonacci_loop.snap
index 0242f4152..e95f7564a 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__fibonacci_loop.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__fibonacci_loop.snap
@@ -6,9 +6,9 @@ expression: "pub fn fibonacci(n:i32) -> i32 {\n    let a = 0;\n    let b = 1;\n
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i32 @fibonacci(i32) {
 body:
@@ -35,9 +35,9 @@ if_merge:                                         ; preds = %loop
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i32>::name" = private unnamed_addr constant [10 x i8] c"core::i32\00"
-@"type_info::<core::i32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i32>"]
+@"type_info::<core::i32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__field_crash.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__field_crash.snap
index c810d0908..dd22d2ad1 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__field_crash.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__field_crash.snap
@@ -7,12 +7,12 @@ expression: "struct(gc) Foo { a: i32 };\n\npub fn main(c:i32) -> i32 {\n    let
 source_filename = "main.mun"
 
 %DispatchTable = type { i8** (i8*, i8*)* }
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 %Foo = type { i32 }
 
 @allocatorHandle = external global i8*
 @dispatchTable = external global %DispatchTable
-@global_type_table = external global [5 x %struct.MunTypeInfo*]
+@global_type_table = external global [5 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i32 @main(i32) {
 body:
@@ -23,8 +23,8 @@ body:
   %add = add i32 %c1, 5
   %init = insertvalue %Foo undef, i32 %add, 0
   %new_ptr = load i8** (i8*, i8*)*, i8** (i8*, i8*)** getelementptr inbounds (%DispatchTable, %DispatchTable* @dispatchTable, i32 0, i32 0)
-  %Foo_ptr = load %struct.MunTypeInfo*, %struct.MunTypeInfo** getelementptr inbounds ([5 x %struct.MunTypeInfo*], [5 x %struct.MunTypeInfo*]* @global_type_table, i32 0, i32 1)
-  %type_info_ptr_to_i8_ptr = bitcast %struct.MunTypeInfo* %Foo_ptr to i8*
+  %Foo_ptr = load %"mun_codegen::ir::types::TypeInfo"*, %"mun_codegen::ir::types::TypeInfo"** getelementptr inbounds ([5 x %"mun_codegen::ir::types::TypeInfo"*], [5 x %"mun_codegen::ir::types::TypeInfo"*]* @global_type_table, i64 0, i64 1)
+  %type_info_ptr_to_i8_ptr = bitcast %"mun_codegen::ir::types::TypeInfo"* %Foo_ptr to i8*
   %allocator_handle = load i8*, i8** @allocatorHandle
   %new = call i8** %new_ptr(i8* %type_info_ptr_to_i8_ptr, i8* %allocator_handle)
   %Foo_ptr_ptr = bitcast i8** %new to %Foo**
@@ -44,24 +44,24 @@ body:
 source_filename = "group_name"
 
 %DispatchTable = type { i8** (i8*, i8*)* }
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
-%struct.MunStructInfo = type { i8**, %struct.MunTypeInfo**, i16*, i16, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::StructInfo" = type { i8**, %"mun_codegen::ir::types::TypeInfo"**, i16*, i16, i8 }
 
 @dispatchTable = global %DispatchTable zeroinitializer
 @"type_info::<core::i32>::name" = private unnamed_addr constant [10 x i8] c"core::i32\00"
-@"type_info::<core::i32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@"type_info::<core::i32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
 @"type_info::<Foo>::name" = private unnamed_addr constant [4 x i8] c"Foo\00"
 @"struct_info::<Foo>::field_names.0" = private unnamed_addr constant [2 x i8] c"a\00"
 @"struct_info::<Foo>::field_names" = private unnamed_addr constant [1 x i8*] [i8* getelementptr inbounds ([2 x i8], [2 x i8]* @"struct_info::<Foo>::field_names.0", i32 0, i32 0)]
-@"struct_info::<Foo>::field_types" = private unnamed_addr constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i32>"]
+@"struct_info::<Foo>::field_types" = private unnamed_addr constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>"]
 @"struct_info::<Foo>::field_offsets" = private unnamed_addr constant [1 x i16] zeroinitializer
-@"type_info::<Foo>" = private unnamed_addr constant { %struct.MunTypeInfo, %struct.MunStructInfo } { %struct.MunTypeInfo { [16 x i8] c"8\AD\C9\D9Y\D2\AA<ME}\CD.\A5\A0\DB", i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"type_info::<Foo>::name", i32 0, i32 0), i32 32, i8 4, i8 1 }, %struct.MunStructInfo { i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"struct_info::<Foo>::field_names", i32 0, i32 0), %struct.MunTypeInfo** getelementptr inbounds ([1 x %struct.MunTypeInfo*], [1 x %struct.MunTypeInfo*]* @"struct_info::<Foo>::field_types", i32 0, i32 0), i16* getelementptr inbounds ([1 x i16], [1 x i16]* @"struct_info::<Foo>::field_offsets", i32 0, i32 0), i16 1, i8 0 } }
+@"type_info::<Foo>" = private unnamed_addr constant { %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" } { %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"8\AD\C9\D9Y\D2\AA<ME}\CD.\A5\A0\DB", i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"type_info::<Foo>::name", i32 0, i32 0), i32 32, i8 4, i8 1 }, %"mun_codegen::ir::types::StructInfo" { i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"struct_info::<Foo>::field_names", i32 0, i32 0), %"mun_codegen::ir::types::TypeInfo"** getelementptr inbounds ([1 x %"mun_codegen::ir::types::TypeInfo"*], [1 x %"mun_codegen::ir::types::TypeInfo"*]* @"struct_info::<Foo>::field_types", i32 0, i32 0), i16* getelementptr inbounds ([1 x i16], [1 x i16]* @"struct_info::<Foo>::field_offsets", i32 0, i32 0), i16 1, i8 0 } }
 @"type_info::<*const TypeInfo>::name" = private unnamed_addr constant [16 x i8] c"*const TypeInfo\00"
-@"type_info::<*const TypeInfo>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"=\A1-\1F\C2\A7\88`d\90\F4\B5\BEE}x", i8* getelementptr inbounds ([16 x i8], [16 x i8]* @"type_info::<*const TypeInfo>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@"type_info::<*const TypeInfo>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"=\A1-\1F\C2\A7\88`d\90\F4\B5\BEE}x", i8* getelementptr inbounds ([16 x i8], [16 x i8]* @"type_info::<*const TypeInfo>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
 @"type_info::<*const *mut core::void>::name" = private unnamed_addr constant [23 x i8] c"*const *mut core::void\00"
-@"type_info::<*const *mut core::void>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\C5fO\BD\84\DF\06\BFd+\B1\9Abv\CE\00", i8* getelementptr inbounds ([23 x i8], [23 x i8]* @"type_info::<*const *mut core::void>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@"type_info::<*const *mut core::void>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\C5fO\BD\84\DF\06\BFd+\B1\9Abv\CE\00", i8* getelementptr inbounds ([23 x i8], [23 x i8]* @"type_info::<*const *mut core::void>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
 @"type_info::<*mut core::void>::name" = private unnamed_addr constant [16 x i8] c"*mut core::void\00"
-@"type_info::<*mut core::void>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\F0Y\22\FC\95\9E\7F\CE\08T\B1\A2\CD\A7\FAz", i8* getelementptr inbounds ([16 x i8], [16 x i8]* @"type_info::<*mut core::void>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
-@global_type_table = constant [5 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i32>", %struct.MunTypeInfo* getelementptr inbounds ({ %struct.MunTypeInfo, %struct.MunStructInfo }, { %struct.MunTypeInfo, %struct.MunStructInfo }* @"type_info::<Foo>", i32 0, i32 0), %struct.MunTypeInfo* @"type_info::<*const TypeInfo>", %struct.MunTypeInfo* @"type_info::<*const *mut core::void>", %struct.MunTypeInfo* @"type_info::<*mut core::void>"]
+@"type_info::<*mut core::void>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\F0Y\22\FC\95\9E\7F\CE\08T\B1\A2\CD\A7\FAz", i8* getelementptr inbounds ([16 x i8], [16 x i8]* @"type_info::<*mut core::void>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@global_type_table = constant [5 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>", %"mun_codegen::ir::types::TypeInfo"* getelementptr inbounds ({ %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }, { %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }* @"type_info::<Foo>", i32 0, i32 0), %"mun_codegen::ir::types::TypeInfo"* @"type_info::<*const TypeInfo>", %"mun_codegen::ir::types::TypeInfo"* @"type_info::<*const *mut core::void>", %"mun_codegen::ir::types::TypeInfo"* @"type_info::<*mut core::void>"]
 @allocatorHandle = unnamed_addr global i8* null
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__field_expr.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__field_expr.snap
index be26013b8..2b5c11cf9 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__field_expr.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__field_expr.snap
@@ -9,11 +9,11 @@ source_filename = "main.mun"
 %DispatchTable = type { i8** (i8*, i8*)*, i32 (%Foo)*, %Foo (%Bar)* }
 %Foo = type { i32 }
 %Bar = type { double, %Foo }
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @allocatorHandle = external global i8*
 @dispatchTable = external global %DispatchTable
-@global_type_table = external global [7 x %struct.MunTypeInfo*]
+@global_type_table = external global [7 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define %Foo @bar_1(%Bar) {
 body:
@@ -60,34 +60,34 @@ source_filename = "group_name"
 %DispatchTable = type { i8** (i8*, i8*)*, i32 (%Foo)*, %Foo (%Bar)* }
 %Foo = type { i32 }
 %Bar = type { double, %Foo }
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
-%struct.MunStructInfo = type { i8**, %struct.MunTypeInfo**, i16*, i16, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::StructInfo" = type { i8**, %"mun_codegen::ir::types::TypeInfo"**, i16*, i16, i8 }
 
 @dispatchTable = global %DispatchTable { i8** (i8*, i8*)* null, i32 (%Foo)* @foo_a, %Foo (%Bar)* @bar_1 }
 @"type_info::<core::i32>::name" = private unnamed_addr constant [10 x i8] c"core::i32\00"
-@"type_info::<core::i32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@"type_info::<core::i32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
 @"type_info::<Foo>::name" = private unnamed_addr constant [4 x i8] c"Foo\00"
 @"struct_info::<Foo>::field_names.0" = private unnamed_addr constant [2 x i8] c"a\00"
 @"struct_info::<Foo>::field_names" = private unnamed_addr constant [1 x i8*] [i8* getelementptr inbounds ([2 x i8], [2 x i8]* @"struct_info::<Foo>::field_names.0", i32 0, i32 0)]
-@"struct_info::<Foo>::field_types" = private unnamed_addr constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i32>"]
+@"struct_info::<Foo>::field_types" = private unnamed_addr constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>"]
 @"struct_info::<Foo>::field_offsets" = private unnamed_addr constant [1 x i16] zeroinitializer
-@"type_info::<Foo>" = private unnamed_addr constant { %struct.MunTypeInfo, %struct.MunStructInfo } { %struct.MunTypeInfo { [16 x i8] c"8\AD\C9\D9Y\D2\AA<ME}\CD.\A5\A0\DB", i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"type_info::<Foo>::name", i32 0, i32 0), i32 32, i8 4, i8 1 }, %struct.MunStructInfo { i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"struct_info::<Foo>::field_names", i32 0, i32 0), %struct.MunTypeInfo** getelementptr inbounds ([1 x %struct.MunTypeInfo*], [1 x %struct.MunTypeInfo*]* @"struct_info::<Foo>::field_types", i32 0, i32 0), i16* getelementptr inbounds ([1 x i16], [1 x i16]* @"struct_info::<Foo>::field_offsets", i32 0, i32 0), i16 1, i8 1 } }
+@"type_info::<Foo>" = private unnamed_addr constant { %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" } { %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"8\AD\C9\D9Y\D2\AA<ME}\CD.\A5\A0\DB", i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"type_info::<Foo>::name", i32 0, i32 0), i32 32, i8 4, i8 1 }, %"mun_codegen::ir::types::StructInfo" { i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"struct_info::<Foo>::field_names", i32 0, i32 0), %"mun_codegen::ir::types::TypeInfo"** getelementptr inbounds ([1 x %"mun_codegen::ir::types::TypeInfo"*], [1 x %"mun_codegen::ir::types::TypeInfo"*]* @"struct_info::<Foo>::field_types", i32 0, i32 0), i16* getelementptr inbounds ([1 x i16], [1 x i16]* @"struct_info::<Foo>::field_offsets", i32 0, i32 0), i16 1, i8 1 } }
 @"type_info::<*const TypeInfo>::name" = private unnamed_addr constant [16 x i8] c"*const TypeInfo\00"
-@"type_info::<*const TypeInfo>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"=\A1-\1F\C2\A7\88`d\90\F4\B5\BEE}x", i8* getelementptr inbounds ([16 x i8], [16 x i8]* @"type_info::<*const TypeInfo>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@"type_info::<*const TypeInfo>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"=\A1-\1F\C2\A7\88`d\90\F4\B5\BEE}x", i8* getelementptr inbounds ([16 x i8], [16 x i8]* @"type_info::<*const TypeInfo>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
 @"type_info::<core::f64>::name" = private unnamed_addr constant [10 x i8] c"core::f64\00"
-@"type_info::<core::f64>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"`\DBF\9C?YJ%G\AD4\9F\D5\92%A", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::f64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@"type_info::<core::f64>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"`\DBF\9C?YJ%G\AD4\9F\D5\92%A", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::f64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
 @"type_info::<*const *mut core::void>::name" = private unnamed_addr constant [23 x i8] c"*const *mut core::void\00"
-@"type_info::<*const *mut core::void>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\C5fO\BD\84\DF\06\BFd+\B1\9Abv\CE\00", i8* getelementptr inbounds ([23 x i8], [23 x i8]* @"type_info::<*const *mut core::void>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@"type_info::<*const *mut core::void>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\C5fO\BD\84\DF\06\BFd+\B1\9Abv\CE\00", i8* getelementptr inbounds ([23 x i8], [23 x i8]* @"type_info::<*const *mut core::void>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
 @"type_info::<*mut core::void>::name" = private unnamed_addr constant [16 x i8] c"*mut core::void\00"
-@"type_info::<*mut core::void>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\F0Y\22\FC\95\9E\7F\CE\08T\B1\A2\CD\A7\FAz", i8* getelementptr inbounds ([16 x i8], [16 x i8]* @"type_info::<*mut core::void>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@"type_info::<*mut core::void>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\F0Y\22\FC\95\9E\7F\CE\08T\B1\A2\CD\A7\FAz", i8* getelementptr inbounds ([16 x i8], [16 x i8]* @"type_info::<*mut core::void>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
 @"type_info::<Bar>::name" = private unnamed_addr constant [4 x i8] c"Bar\00"
 @"struct_info::<Bar>::field_names.0" = private unnamed_addr constant [2 x i8] c"0\00"
 @"struct_info::<Bar>::field_names.1" = private unnamed_addr constant [2 x i8] c"1\00"
 @"struct_info::<Bar>::field_names" = private unnamed_addr constant [2 x i8*] [i8* getelementptr inbounds ([2 x i8], [2 x i8]* @"struct_info::<Bar>::field_names.0", i32 0, i32 0), i8* getelementptr inbounds ([2 x i8], [2 x i8]* @"struct_info::<Bar>::field_names.1", i32 0, i32 0)]
-@"struct_info::<Bar>::field_types" = private unnamed_addr constant [2 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::f64>", %struct.MunTypeInfo* getelementptr inbounds ({ %struct.MunTypeInfo, %struct.MunStructInfo }, { %struct.MunTypeInfo, %struct.MunStructInfo }* @"type_info::<Foo>", i32 0, i32 0)]
+@"struct_info::<Bar>::field_types" = private unnamed_addr constant [2 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::f64>", %"mun_codegen::ir::types::TypeInfo"* getelementptr inbounds ({ %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }, { %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }* @"type_info::<Foo>", i32 0, i32 0)]
 @"struct_info::<Bar>::field_offsets" = private unnamed_addr constant [2 x i16] [i16 0, i16 8]
-@"type_info::<Bar>" = private unnamed_addr constant { %struct.MunTypeInfo, %struct.MunStructInfo } { %struct.MunTypeInfo { [16 x i8] c"\FC8#Lvd)F\B1Q\06\8B\02pl\10", i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"type_info::<Bar>::name", i32 0, i32 0), i32 128, i8 8, i8 1 }, %struct.MunStructInfo { i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"struct_info::<Bar>::field_names", i32 0, i32 0), %struct.MunTypeInfo** getelementptr inbounds ([2 x %struct.MunTypeInfo*], [2 x %struct.MunTypeInfo*]* @"struct_info::<Bar>::field_types", i32 0, i32 0), i16* getelementptr inbounds ([2 x i16], [2 x i16]* @"struct_info::<Bar>::field_offsets", i32 0, i32 0), i16 2, i8 1 } }
-@global_type_table = constant [7 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i32>", %struct.MunTypeInfo* getelementptr inbounds ({ %struct.MunTypeInfo, %struct.MunStructInfo }, { %struct.MunTypeInfo, %struct.MunStructInfo }* @"type_info::<Foo>", i32 0, i32 0), %struct.MunTypeInfo* @"type_info::<*const TypeInfo>", %struct.MunTypeInfo* @"type_info::<core::f64>", %struct.MunTypeInfo* @"type_info::<*const *mut core::void>", %struct.MunTypeInfo* @"type_info::<*mut core::void>", %struct.MunTypeInfo* getelementptr inbounds ({ %struct.MunTypeInfo, %struct.MunStructInfo }, { %struct.MunTypeInfo, %struct.MunStructInfo }* @"type_info::<Bar>", i32 0, i32 0)]
+@"type_info::<Bar>" = private unnamed_addr constant { %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" } { %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\FC8#Lvd)F\B1Q\06\8B\02pl\10", i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"type_info::<Bar>::name", i32 0, i32 0), i32 128, i8 8, i8 1 }, %"mun_codegen::ir::types::StructInfo" { i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"struct_info::<Bar>::field_names", i32 0, i32 0), %"mun_codegen::ir::types::TypeInfo"** getelementptr inbounds ([2 x %"mun_codegen::ir::types::TypeInfo"*], [2 x %"mun_codegen::ir::types::TypeInfo"*]* @"struct_info::<Bar>::field_types", i32 0, i32 0), i16* getelementptr inbounds ([2 x i16], [2 x i16]* @"struct_info::<Bar>::field_offsets", i32 0, i32 0), i16 2, i8 1 } }
+@global_type_table = constant [7 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>", %"mun_codegen::ir::types::TypeInfo"* getelementptr inbounds ({ %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }, { %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }* @"type_info::<Foo>", i32 0, i32 0), %"mun_codegen::ir::types::TypeInfo"* @"type_info::<*const TypeInfo>", %"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::f64>", %"mun_codegen::ir::types::TypeInfo"* @"type_info::<*const *mut core::void>", %"mun_codegen::ir::types::TypeInfo"* @"type_info::<*mut core::void>", %"mun_codegen::ir::types::TypeInfo"* getelementptr inbounds ({ %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }, { %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }* @"type_info::<Bar>", i32 0, i32 0)]
 @allocatorHandle = unnamed_addr global i8* null
 
 declare i32 @foo_a(%Foo)
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__function_arguments.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__function_arguments.snap
index bab47886b..9ec36554b 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__function_arguments.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__function_arguments.snap
@@ -6,9 +6,9 @@ expression: "pub fn main(a:i32) -> i32 {\n  a\n}"
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i32 @main(i32) {
 body:
@@ -20,9 +20,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i32>::name" = private unnamed_addr constant [10 x i8] c"core::i32\00"
-@"type_info::<core::i32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i32>"]
+@"type_info::<core::i32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__function_calls.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__function_calls.snap
index 88233c1bc..f519b7fb3 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__function_calls.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__function_calls.snap
@@ -7,10 +7,10 @@ expression: "fn add_impl(a:i32, b:i32) -> i32 {\n    a+b\n}\n\nfn add(a:i32, b:i
 source_filename = "main.mun"
 
 %DispatchTable = type { i32 (i32, i32)*, i32 (i32, i32)* }
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @dispatchTable = external global %DispatchTable
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i32 @add_impl(i32, i32) {
 body:
@@ -42,12 +42,12 @@ body:
 source_filename = "group_name"
 
 %DispatchTable = type { i32 (i32, i32)*, i32 (i32, i32)* }
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @dispatchTable = global %DispatchTable { i32 (i32, i32)* @add, i32 (i32, i32)* @add_impl }
 @"type_info::<core::i32>::name" = private unnamed_addr constant [10 x i8] c"core::i32\00"
-@"type_info::<core::i32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i32>"]
+@"type_info::<core::i32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>"]
 
 declare i32 @add(i32, i32)
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__gc_struct.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__gc_struct.snap
index bb99065ea..3ff98a11c 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__gc_struct.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__gc_struct.snap
@@ -7,20 +7,20 @@ expression: "struct(gc) Foo { a: i32, b: i32 };\n\npub fn foo() {\n    let a = F
 source_filename = "main.mun"
 
 %DispatchTable = type { i8** (i8*, i8*)* }
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 %Foo = type { i32, i32 }
 
 @allocatorHandle = external global i8*
 @dispatchTable = external global %DispatchTable
-@global_type_table = external global [5 x %struct.MunTypeInfo*]
+@global_type_table = external global [5 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define void @foo() {
 body:
   %b = alloca %Foo**
   %a = alloca %Foo**
   %new_ptr = load i8** (i8*, i8*)*, i8** (i8*, i8*)** getelementptr inbounds (%DispatchTable, %DispatchTable* @dispatchTable, i32 0, i32 0)
-  %Foo_ptr = load %struct.MunTypeInfo*, %struct.MunTypeInfo** getelementptr inbounds ([5 x %struct.MunTypeInfo*], [5 x %struct.MunTypeInfo*]* @global_type_table, i32 0, i32 0)
-  %type_info_ptr_to_i8_ptr = bitcast %struct.MunTypeInfo* %Foo_ptr to i8*
+  %Foo_ptr = load %"mun_codegen::ir::types::TypeInfo"*, %"mun_codegen::ir::types::TypeInfo"** getelementptr inbounds ([5 x %"mun_codegen::ir::types::TypeInfo"*], [5 x %"mun_codegen::ir::types::TypeInfo"*]* @global_type_table, i64 0, i64 0)
+  %type_info_ptr_to_i8_ptr = bitcast %"mun_codegen::ir::types::TypeInfo"* %Foo_ptr to i8*
   %allocator_handle = load i8*, i8** @allocatorHandle
   %new = call i8** %new_ptr(i8* %type_info_ptr_to_i8_ptr, i8* %allocator_handle)
   %Foo_ptr_ptr = bitcast i8** %new to %Foo**
@@ -47,8 +47,8 @@ body:
 source_filename = "group_name"
 
 %DispatchTable = type { i8** (i8*, i8*)* }
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
-%struct.MunStructInfo = type { i8**, %struct.MunTypeInfo**, i16*, i16, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::StructInfo" = type { i8**, %"mun_codegen::ir::types::TypeInfo"**, i16*, i16, i8 }
 
 @dispatchTable = global %DispatchTable zeroinitializer
 @"type_info::<Foo>::name" = private unnamed_addr constant [4 x i8] c"Foo\00"
@@ -56,16 +56,16 @@ source_filename = "group_name"
 @"struct_info::<Foo>::field_names.1" = private unnamed_addr constant [2 x i8] c"b\00"
 @"struct_info::<Foo>::field_names" = private unnamed_addr constant [2 x i8*] [i8* getelementptr inbounds ([2 x i8], [2 x i8]* @"struct_info::<Foo>::field_names.0", i32 0, i32 0), i8* getelementptr inbounds ([2 x i8], [2 x i8]* @"struct_info::<Foo>::field_names.1", i32 0, i32 0)]
 @"type_info::<core::i32>::name" = private unnamed_addr constant [10 x i8] c"core::i32\00"
-@"type_info::<core::i32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@"struct_info::<Foo>::field_types" = private unnamed_addr constant [2 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i32>", %struct.MunTypeInfo* @"type_info::<core::i32>"]
+@"type_info::<core::i32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@"struct_info::<Foo>::field_types" = private unnamed_addr constant [2 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>", %"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>"]
 @"struct_info::<Foo>::field_offsets" = private unnamed_addr constant [2 x i16] [i16 0, i16 4]
-@"type_info::<Foo>" = private unnamed_addr constant { %struct.MunTypeInfo, %struct.MunStructInfo } { %struct.MunTypeInfo { [16 x i8] c"\03\FC\D1_\DB\DB\0AJ1r,\F0m\CBQ\D0", i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"type_info::<Foo>::name", i32 0, i32 0), i32 64, i8 4, i8 1 }, %struct.MunStructInfo { i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"struct_info::<Foo>::field_names", i32 0, i32 0), %struct.MunTypeInfo** getelementptr inbounds ([2 x %struct.MunTypeInfo*], [2 x %struct.MunTypeInfo*]* @"struct_info::<Foo>::field_types", i32 0, i32 0), i16* getelementptr inbounds ([2 x i16], [2 x i16]* @"struct_info::<Foo>::field_offsets", i32 0, i32 0), i16 2, i8 0 } }
+@"type_info::<Foo>" = private unnamed_addr constant { %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" } { %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\03\FC\D1_\DB\DB\0AJ1r,\F0m\CBQ\D0", i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"type_info::<Foo>::name", i32 0, i32 0), i32 64, i8 4, i8 1 }, %"mun_codegen::ir::types::StructInfo" { i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"struct_info::<Foo>::field_names", i32 0, i32 0), %"mun_codegen::ir::types::TypeInfo"** getelementptr inbounds ([2 x %"mun_codegen::ir::types::TypeInfo"*], [2 x %"mun_codegen::ir::types::TypeInfo"*]* @"struct_info::<Foo>::field_types", i32 0, i32 0), i16* getelementptr inbounds ([2 x i16], [2 x i16]* @"struct_info::<Foo>::field_offsets", i32 0, i32 0), i16 2, i8 0 } }
 @"type_info::<*const TypeInfo>::name" = private unnamed_addr constant [16 x i8] c"*const TypeInfo\00"
-@"type_info::<*const TypeInfo>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"=\A1-\1F\C2\A7\88`d\90\F4\B5\BEE}x", i8* getelementptr inbounds ([16 x i8], [16 x i8]* @"type_info::<*const TypeInfo>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@"type_info::<*const TypeInfo>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"=\A1-\1F\C2\A7\88`d\90\F4\B5\BEE}x", i8* getelementptr inbounds ([16 x i8], [16 x i8]* @"type_info::<*const TypeInfo>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
 @"type_info::<*const *mut core::void>::name" = private unnamed_addr constant [23 x i8] c"*const *mut core::void\00"
-@"type_info::<*const *mut core::void>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\C5fO\BD\84\DF\06\BFd+\B1\9Abv\CE\00", i8* getelementptr inbounds ([23 x i8], [23 x i8]* @"type_info::<*const *mut core::void>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@"type_info::<*const *mut core::void>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\C5fO\BD\84\DF\06\BFd+\B1\9Abv\CE\00", i8* getelementptr inbounds ([23 x i8], [23 x i8]* @"type_info::<*const *mut core::void>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
 @"type_info::<*mut core::void>::name" = private unnamed_addr constant [16 x i8] c"*mut core::void\00"
-@"type_info::<*mut core::void>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\F0Y\22\FC\95\9E\7F\CE\08T\B1\A2\CD\A7\FAz", i8* getelementptr inbounds ([16 x i8], [16 x i8]* @"type_info::<*mut core::void>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
-@global_type_table = constant [5 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* getelementptr inbounds ({ %struct.MunTypeInfo, %struct.MunStructInfo }, { %struct.MunTypeInfo, %struct.MunStructInfo }* @"type_info::<Foo>", i32 0, i32 0), %struct.MunTypeInfo* @"type_info::<core::i32>", %struct.MunTypeInfo* @"type_info::<*const TypeInfo>", %struct.MunTypeInfo* @"type_info::<*const *mut core::void>", %struct.MunTypeInfo* @"type_info::<*mut core::void>"]
+@"type_info::<*mut core::void>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\F0Y\22\FC\95\9E\7F\CE\08T\B1\A2\CD\A7\FAz", i8* getelementptr inbounds ([16 x i8], [16 x i8]* @"type_info::<*mut core::void>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@global_type_table = constant [5 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* getelementptr inbounds ({ %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }, { %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }* @"type_info::<Foo>", i32 0, i32 0), %"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>", %"mun_codegen::ir::types::TypeInfo"* @"type_info::<*const TypeInfo>", %"mun_codegen::ir::types::TypeInfo"* @"type_info::<*const *mut core::void>", %"mun_codegen::ir::types::TypeInfo"* @"type_info::<*mut core::void>"]
 @allocatorHandle = unnamed_addr global i8* null
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__if_statement.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__if_statement.snap
index 129e7d4da..c688f53d0 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__if_statement.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__if_statement.snap
@@ -6,9 +6,9 @@ expression: "pub fn foo(a:i32) -> i32 {\n    let b = if a > 3 {\n        let c =
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i32 @foo(i32) {
 body:
@@ -36,9 +36,9 @@ if_merge:                                         ; preds = %else, %then
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i32>::name" = private unnamed_addr constant [10 x i8] c"core::i32\00"
-@"type_info::<core::i32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i32>"]
+@"type_info::<core::i32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__issue_128.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__issue_128.snap
index 2de7b8f7d..b0418ddbd 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__issue_128.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__issue_128.snap
@@ -7,10 +7,10 @@ expression: "// resources/script.mun\nextern fn thing(n: i32);\nextern fn print(
 source_filename = "main.mun"
 
 %DispatchTable = type { i32 (i32)*, void (i32)* }
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @dispatchTable = external global %DispatchTable
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define void @main() {
 body:
@@ -31,10 +31,10 @@ body:
 source_filename = "group_name"
 
 %DispatchTable = type { i32 (i32)*, void (i32)* }
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @dispatchTable = global %DispatchTable zeroinitializer
 @"type_info::<core::i32>::name" = private unnamed_addr constant [10 x i8] c"core::i32\00"
-@"type_info::<core::i32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i32>"]
+@"type_info::<core::i32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__issue_133.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__issue_133.snap
index 48ac12b30..1d8e808e2 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__issue_133.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__issue_133.snap
@@ -7,10 +7,10 @@ expression: "fn do_the_things(n: i32) -> i32 {\n    n + 7\n}\n\npub fn main() {\
 source_filename = "main.mun"
 
 %DispatchTable = type { i32 (i32)* }
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @dispatchTable = external global %DispatchTable
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i32 @do_the_things(i32) {
 body:
@@ -31,12 +31,12 @@ body:
 source_filename = "group_name"
 
 %DispatchTable = type { i32 (i32)* }
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @dispatchTable = global %DispatchTable { i32 (i32)* @do_the_things }
 @"type_info::<core::i32>::name" = private unnamed_addr constant [10 x i8] c"core::i32\00"
-@"type_info::<core::i32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i32>"]
+@"type_info::<core::i32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>"]
 
 declare i32 @do_the_things(i32)
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__issue_225.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__issue_225.snap
index 827bb7853..d2315179c 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__issue_225.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__issue_225.snap
@@ -7,19 +7,19 @@ expression: "struct Num {\n    value: i64,\n}\n\npub fn foo(b: i64) {\n    Num {
 source_filename = "main.mun"
 
 %DispatchTable = type { i8** (i8*, i8*)* }
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 %Num = type { i64 }
 
 @allocatorHandle = external global i8*
 @dispatchTable = external global %DispatchTable
-@global_type_table = external global [5 x %struct.MunTypeInfo*]
+@global_type_table = external global [5 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define void @foo(i64) {
 body:
   %init = insertvalue %Num undef, i64 %0, 0
   %new_ptr = load i8** (i8*, i8*)*, i8** (i8*, i8*)** getelementptr inbounds (%DispatchTable, %DispatchTable* @dispatchTable, i32 0, i32 0)
-  %Num_ptr = load %struct.MunTypeInfo*, %struct.MunTypeInfo** getelementptr inbounds ([5 x %struct.MunTypeInfo*], [5 x %struct.MunTypeInfo*]* @global_type_table, i32 0, i32 2)
-  %type_info_ptr_to_i8_ptr = bitcast %struct.MunTypeInfo* %Num_ptr to i8*
+  %Num_ptr = load %"mun_codegen::ir::types::TypeInfo"*, %"mun_codegen::ir::types::TypeInfo"** getelementptr inbounds ([5 x %"mun_codegen::ir::types::TypeInfo"*], [5 x %"mun_codegen::ir::types::TypeInfo"*]* @global_type_table, i64 0, i64 2)
+  %type_info_ptr_to_i8_ptr = bitcast %"mun_codegen::ir::types::TypeInfo"* %Num_ptr to i8*
   %allocator_handle = load i8*, i8** @allocatorHandle
   %new = call i8** %new_ptr(i8* %type_info_ptr_to_i8_ptr, i8* %allocator_handle)
   %Num_ptr_ptr = bitcast i8** %new to %Num**
@@ -34,8 +34,8 @@ define void @bar(i64) {
 body:
   %init = insertvalue %Num undef, i64 %0, 0
   %new_ptr = load i8** (i8*, i8*)*, i8** (i8*, i8*)** getelementptr inbounds (%DispatchTable, %DispatchTable* @dispatchTable, i32 0, i32 0)
-  %Num_ptr = load %struct.MunTypeInfo*, %struct.MunTypeInfo** getelementptr inbounds ([5 x %struct.MunTypeInfo*], [5 x %struct.MunTypeInfo*]* @global_type_table, i32 0, i32 2)
-  %type_info_ptr_to_i8_ptr = bitcast %struct.MunTypeInfo* %Num_ptr to i8*
+  %Num_ptr = load %"mun_codegen::ir::types::TypeInfo"*, %"mun_codegen::ir::types::TypeInfo"** getelementptr inbounds ([5 x %"mun_codegen::ir::types::TypeInfo"*], [5 x %"mun_codegen::ir::types::TypeInfo"*]* @global_type_table, i64 0, i64 2)
+  %type_info_ptr_to_i8_ptr = bitcast %"mun_codegen::ir::types::TypeInfo"* %Num_ptr to i8*
   %allocator_handle = load i8*, i8** @allocatorHandle
   %new = call i8** %new_ptr(i8* %type_info_ptr_to_i8_ptr, i8* %allocator_handle)
   %Num_ptr_ptr = bitcast i8** %new to %Num**
@@ -52,24 +52,24 @@ body:
 source_filename = "group_name"
 
 %DispatchTable = type { i8** (i8*, i8*)* }
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
-%struct.MunStructInfo = type { i8**, %struct.MunTypeInfo**, i16*, i16, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::StructInfo" = type { i8**, %"mun_codegen::ir::types::TypeInfo"**, i16*, i16, i8 }
 
 @dispatchTable = global %DispatchTable zeroinitializer
 @"type_info::<*const TypeInfo>::name" = private unnamed_addr constant [16 x i8] c"*const TypeInfo\00"
-@"type_info::<*const TypeInfo>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"=\A1-\1F\C2\A7\88`d\90\F4\B5\BEE}x", i8* getelementptr inbounds ([16 x i8], [16 x i8]* @"type_info::<*const TypeInfo>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@"type_info::<*const TypeInfo>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"=\A1-\1F\C2\A7\88`d\90\F4\B5\BEE}x", i8* getelementptr inbounds ([16 x i8], [16 x i8]* @"type_info::<*const TypeInfo>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
 @"type_info::<core::i64>::name" = private unnamed_addr constant [10 x i8] c"core::i64\00"
-@"type_info::<core::i64>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"G\13;t\97j8\18\D7M\83`\1D\C8\19%", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@"type_info::<core::i64>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"G\13;t\97j8\18\D7M\83`\1D\C8\19%", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
 @"type_info::<Num>::name" = private unnamed_addr constant [4 x i8] c"Num\00"
 @"struct_info::<Num>::field_names.0" = private unnamed_addr constant [6 x i8] c"value\00"
 @"struct_info::<Num>::field_names" = private unnamed_addr constant [1 x i8*] [i8* getelementptr inbounds ([6 x i8], [6 x i8]* @"struct_info::<Num>::field_names.0", i32 0, i32 0)]
-@"struct_info::<Num>::field_types" = private unnamed_addr constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i64>"]
+@"struct_info::<Num>::field_types" = private unnamed_addr constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i64>"]
 @"struct_info::<Num>::field_offsets" = private unnamed_addr constant [1 x i16] zeroinitializer
-@"type_info::<Num>" = private unnamed_addr constant { %struct.MunTypeInfo, %struct.MunStructInfo } { %struct.MunTypeInfo { [16 x i8] c"\A92\E2p\B0\98\B2\C4\0C\A2\F5=x\904\00", i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"type_info::<Num>::name", i32 0, i32 0), i32 64, i8 8, i8 1 }, %struct.MunStructInfo { i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"struct_info::<Num>::field_names", i32 0, i32 0), %struct.MunTypeInfo** getelementptr inbounds ([1 x %struct.MunTypeInfo*], [1 x %struct.MunTypeInfo*]* @"struct_info::<Num>::field_types", i32 0, i32 0), i16* getelementptr inbounds ([1 x i16], [1 x i16]* @"struct_info::<Num>::field_offsets", i32 0, i32 0), i16 1, i8 0 } }
+@"type_info::<Num>" = private unnamed_addr constant { %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" } { %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\A92\E2p\B0\98\B2\C4\0C\A2\F5=x\904\00", i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"type_info::<Num>::name", i32 0, i32 0), i32 64, i8 8, i8 1 }, %"mun_codegen::ir::types::StructInfo" { i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"struct_info::<Num>::field_names", i32 0, i32 0), %"mun_codegen::ir::types::TypeInfo"** getelementptr inbounds ([1 x %"mun_codegen::ir::types::TypeInfo"*], [1 x %"mun_codegen::ir::types::TypeInfo"*]* @"struct_info::<Num>::field_types", i32 0, i32 0), i16* getelementptr inbounds ([1 x i16], [1 x i16]* @"struct_info::<Num>::field_offsets", i32 0, i32 0), i16 1, i8 0 } }
 @"type_info::<*const *mut core::void>::name" = private unnamed_addr constant [23 x i8] c"*const *mut core::void\00"
-@"type_info::<*const *mut core::void>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\C5fO\BD\84\DF\06\BFd+\B1\9Abv\CE\00", i8* getelementptr inbounds ([23 x i8], [23 x i8]* @"type_info::<*const *mut core::void>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@"type_info::<*const *mut core::void>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\C5fO\BD\84\DF\06\BFd+\B1\9Abv\CE\00", i8* getelementptr inbounds ([23 x i8], [23 x i8]* @"type_info::<*const *mut core::void>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
 @"type_info::<*mut core::void>::name" = private unnamed_addr constant [16 x i8] c"*mut core::void\00"
-@"type_info::<*mut core::void>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\F0Y\22\FC\95\9E\7F\CE\08T\B1\A2\CD\A7\FAz", i8* getelementptr inbounds ([16 x i8], [16 x i8]* @"type_info::<*mut core::void>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
-@global_type_table = constant [5 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<*const TypeInfo>", %struct.MunTypeInfo* @"type_info::<core::i64>", %struct.MunTypeInfo* getelementptr inbounds ({ %struct.MunTypeInfo, %struct.MunStructInfo }, { %struct.MunTypeInfo, %struct.MunStructInfo }* @"type_info::<Num>", i32 0, i32 0), %struct.MunTypeInfo* @"type_info::<*const *mut core::void>", %struct.MunTypeInfo* @"type_info::<*mut core::void>"]
+@"type_info::<*mut core::void>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\F0Y\22\FC\95\9E\7F\CE\08T\B1\A2\CD\A7\FAz", i8* getelementptr inbounds ([16 x i8], [16 x i8]* @"type_info::<*mut core::void>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@global_type_table = constant [5 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<*const TypeInfo>", %"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i64>", %"mun_codegen::ir::types::TypeInfo"* getelementptr inbounds ({ %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }, { %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }* @"type_info::<Num>", i32 0, i32 0), %"mun_codegen::ir::types::TypeInfo"* @"type_info::<*const *mut core::void>", %"mun_codegen::ir::types::TypeInfo"* @"type_info::<*mut core::void>"]
 @allocatorHandle = unnamed_addr global i8* null
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__issue_228.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__issue_228.snap
index 58af2951a..ddb92e897 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__issue_228.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__issue_228.snap
@@ -6,9 +6,9 @@ expression: "pub  fn fact(n: usize) -> usize {\n   \t    if n == 0 {return 1} el
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i64 @fact(i64) {
 body:
@@ -30,9 +30,9 @@ else:                                             ; preds = %body
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::u64>::name" = private unnamed_addr constant [10 x i8] c"core::u64\00"
-@"type_info::<core::u64>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\A6\E7g \D1\8B\1Aq`\1F\1E\07\BB5@q", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::u64>"]
+@"type_info::<core::u64>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\A6\E7g \D1\8B\1Aq`\1F\1E\07\BB5@q", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::u64>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__issue_228_never_if.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__issue_228_never_if.snap
index b42857884..d1a94b386 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__issue_228_never_if.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__issue_228_never_if.snap
@@ -6,9 +6,9 @@ expression: "pub  fn fact(n: usize) -> usize {\n   \t    if n == 0 {return 1} el
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i64 @fact(i64) {
 body:
@@ -29,9 +29,9 @@ else:                                             ; preds = %body
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::u64>::name" = private unnamed_addr constant [10 x i8] c"core::u64\00"
-@"type_info::<core::u64>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\A6\E7g \D1\8B\1Aq`\1F\1E\07\BB5@q", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::u64>"]
+@"type_info::<core::u64>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\A6\E7g \D1\8B\1Aq`\1F\1E\07\BB5@q", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::u64>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__let_statement.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__let_statement.snap
index 9d113450b..25fc3827f 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__let_statement.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__let_statement.snap
@@ -6,9 +6,9 @@ expression: "pub fn main(a:i32) -> i32 {\n  let b = a+1\n  b\n}"
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i32 @main(i32) {
 body:
@@ -21,9 +21,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i32>::name" = private unnamed_addr constant [10 x i8] c"core::i32\00"
-@"type_info::<core::i32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i32>"]
+@"type_info::<core::i32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__literal_types.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__literal_types.snap
index 4d6a148ac..041bc213c 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__literal_types.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__literal_types.snap
@@ -6,9 +6,9 @@ expression: "pub fn main(){\n    let a = 123;\n    let a = 123u8;\n    let a = 1
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define void @main() {
 body:
@@ -61,9 +61,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::u32>::name" = private unnamed_addr constant [10 x i8] c"core::u32\00"
-@"type_info::<core::u32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"daz5d\A6\BE\88\81=&Y\A1+\C6\1D", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::u32>"]
+@"type_info::<core::u32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"daz5d\A6\BE\88\81=&Y\A1+\C6\1D", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::u32>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__logic_op_bool.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__logic_op_bool.snap
index 37f21dca4..1a410379b 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__logic_op_bool.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__logic_op_bool.snap
@@ -6,9 +6,9 @@ expression: "pub fn and(a: bool, b: bool) -> bool {\n    a && b\n}\npub fn or(a:
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i1 @and(i1, i1) {
 body:
@@ -27,9 +27,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::bool>::name" = private unnamed_addr constant [11 x i8] c"core::bool\00"
-@"type_info::<core::bool>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::bool>::name", i32 0, i32 0), i32 1, i8 1, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::bool>"]
+@"type_info::<core::bool>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::bool>::name", i32 0, i32 0), i32 1, i8 1, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::bool>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__loop_break_expr.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__loop_break_expr.snap
index efd33a1ac..dc4827543 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__loop_break_expr.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__loop_break_expr.snap
@@ -6,9 +6,9 @@ expression: "pub fn foo(n:i32) -> i32 {\n    loop {\n        if n > 5 {\n
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i32 @foo(i32) {
 body:
@@ -37,9 +37,9 @@ if_merge6:                                        ; preds = %if_merge
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i32>::name" = private unnamed_addr constant [10 x i8] c"core::i32\00"
-@"type_info::<core::i32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i32>"]
+@"type_info::<core::i32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__negate_op_f32.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__negate_op_f32.snap
index f3995f57e..74e3e54ac 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__negate_op_f32.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__negate_op_f32.snap
@@ -6,9 +6,9 @@ expression: "pub fn negate(a: f32) -> f32 { -a }"
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define float @negate(float) {
 body:
@@ -21,9 +21,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::f32>::name" = private unnamed_addr constant [10 x i8] c"core::f32\00"
-@"type_info::<core::f32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"P\19b7\A8k\F2\81P\FB\83\F5P\B0\82!", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::f32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::f32>"]
+@"type_info::<core::f32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"P\19b7\A8k\F2\81P\FB\83\F5P\B0\82!", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::f32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::f32>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__negate_op_f64.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__negate_op_f64.snap
index f6bd5054d..1fdca76ae 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__negate_op_f64.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__negate_op_f64.snap
@@ -6,9 +6,9 @@ expression: "pub fn negate(a: f64) -> f64 { -a }"
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define double @negate(double) {
 body:
@@ -21,9 +21,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::f64>::name" = private unnamed_addr constant [10 x i8] c"core::f64\00"
-@"type_info::<core::f64>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"`\DBF\9C?YJ%G\AD4\9F\D5\92%A", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::f64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::f64>"]
+@"type_info::<core::f64>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"`\DBF\9C?YJ%G\AD4\9F\D5\92%A", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::f64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::f64>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__negate_op_i128.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__negate_op_i128.snap
index b784587d6..369941aab 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__negate_op_i128.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__negate_op_i128.snap
@@ -6,9 +6,9 @@ expression: "pub fn negate(a: i128) -> i128 { -a }"
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i128 @negate(i128) {
 body:
@@ -21,9 +21,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i128>::name" = private unnamed_addr constant [11 x i8] c"core::i128\00"
-@"type_info::<core::i128>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\BDkp\09RRM\EBc\02\A0\DB47\A7\E3", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::i128>::name", i32 0, i32 0), i32 128, i8 8, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i128>"]
+@"type_info::<core::i128>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\BDkp\09RRM\EBc\02\A0\DB47\A7\E3", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::i128>::name", i32 0, i32 0), i32 128, i8 8, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i128>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__negate_op_i16.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__negate_op_i16.snap
index d12d97936..293ce21b5 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__negate_op_i16.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__negate_op_i16.snap
@@ -6,9 +6,9 @@ expression: "pub fn negate(a: i16) -> i16 { -a }"
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i16 @negate(i16) {
 body:
@@ -21,9 +21,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i16>::name" = private unnamed_addr constant [10 x i8] c"core::i16\00"
-@"type_info::<core::i16>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\05\CD|\F8Bv\D8\B1\E8\8B\8C\D8\8D\B5\89\B0", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i16>::name", i32 0, i32 0), i32 16, i8 2, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i16>"]
+@"type_info::<core::i16>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\05\CD|\F8Bv\D8\B1\E8\8B\8C\D8\8D\B5\89\B0", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i16>::name", i32 0, i32 0), i32 16, i8 2, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i16>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__negate_op_i32.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__negate_op_i32.snap
index 606963f8f..49ab312c5 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__negate_op_i32.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__negate_op_i32.snap
@@ -6,9 +6,9 @@ expression: "pub fn negate(a: i32) -> i32 { -a }"
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i32 @negate(i32) {
 body:
@@ -21,9 +21,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i32>::name" = private unnamed_addr constant [10 x i8] c"core::i32\00"
-@"type_info::<core::i32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i32>"]
+@"type_info::<core::i32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__negate_op_i64.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__negate_op_i64.snap
index 2da06017c..a047d8a72 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__negate_op_i64.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__negate_op_i64.snap
@@ -6,9 +6,9 @@ expression: "pub fn negate(a: i64) -> i64 { -a }"
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i64 @negate(i64) {
 body:
@@ -21,9 +21,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i64>::name" = private unnamed_addr constant [10 x i8] c"core::i64\00"
-@"type_info::<core::i64>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"G\13;t\97j8\18\D7M\83`\1D\C8\19%", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i64>"]
+@"type_info::<core::i64>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"G\13;t\97j8\18\D7M\83`\1D\C8\19%", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i64>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__negate_op_i8.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__negate_op_i8.snap
index 389103f7b..9cfd29353 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__negate_op_i8.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__negate_op_i8.snap
@@ -6,9 +6,9 @@ expression: "pub fn negate(a: i8) -> i8 { -a }"
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i8 @negate(i8) {
 body:
@@ -21,9 +21,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i8>::name" = private unnamed_addr constant [9 x i8] c"core::i8\00"
-@"type_info::<core::i8>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\EF\C4\B1Z\E7\12\B1\91q\F1\0B\80U\FC\A6\0F", i8* getelementptr inbounds ([9 x i8], [9 x i8]* @"type_info::<core::i8>::name", i32 0, i32 0), i32 8, i8 1, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i8>"]
+@"type_info::<core::i8>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\EF\C4\B1Z\E7\12\B1\91q\F1\0B\80U\FC\A6\0F", i8* getelementptr inbounds ([9 x i8], [9 x i8]* @"type_info::<core::i8>::name", i32 0, i32 0), i32 8, i8 1, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i8>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__nested_private_extern_fn.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__nested_private_extern_fn.snap
index 304fff41b..a9bdb2c9f 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__nested_private_extern_fn.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__nested_private_extern_fn.snap
@@ -7,10 +7,10 @@ expression: "extern fn extern_fn() -> f32;\n\nfn private_fn() -> f32 {\n    exte
 source_filename = "main.mun"
 
 %DispatchTable = type { float ()*, float ()* }
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @dispatchTable = external global %DispatchTable
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define float @private_fn() {
 body:
@@ -32,12 +32,12 @@ body:
 source_filename = "group_name"
 
 %DispatchTable = type { float ()*, float ()* }
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @dispatchTable = global %DispatchTable { float ()* @private_fn, float ()* null }
 @"type_info::<core::f32>::name" = private unnamed_addr constant [10 x i8] c"core::f32\00"
-@"type_info::<core::f32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"P\19b7\A8k\F2\81P\FB\83\F5P\B0\82!", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::f32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::f32>"]
+@"type_info::<core::f32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"P\19b7\A8k\F2\81P\FB\83\F5P\B0\82!", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::f32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::f32>"]
 
 declare float @private_fn()
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__nested_private_fn.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__nested_private_fn.snap
index be1eb5f32..a60091185 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__nested_private_fn.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__nested_private_fn.snap
@@ -7,10 +7,10 @@ expression: "fn nested_private_fn() -> i32 {\n    1\n}\n\nfn private_fn() -> i32
 source_filename = "main.mun"
 
 %DispatchTable = type { i32 ()*, i32 ()* }
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @dispatchTable = external global %DispatchTable
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i32 @nested_private_fn() {
 body:
@@ -37,12 +37,12 @@ body:
 source_filename = "group_name"
 
 %DispatchTable = type { i32 ()*, i32 ()* }
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @dispatchTable = global %DispatchTable { i32 ()* @private_fn, i32 ()* @nested_private_fn }
 @"type_info::<core::i32>::name" = private unnamed_addr constant [10 x i8] c"core::i32\00"
-@"type_info::<core::i32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i32>"]
+@"type_info::<core::i32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>"]
 
 declare i32 @private_fn()
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__nested_private_recursive_fn.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__nested_private_recursive_fn.snap
index edd594fb3..225cd2634 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__nested_private_recursive_fn.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__nested_private_recursive_fn.snap
@@ -7,10 +7,10 @@ expression: "fn private_fn() -> f32 {\n    private_fn()\n}\n\npub fn main() -> f
 source_filename = "main.mun"
 
 %DispatchTable = type { float ()* }
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @dispatchTable = external global %DispatchTable
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define float @private_fn() {
 body:
@@ -32,12 +32,12 @@ body:
 source_filename = "group_name"
 
 %DispatchTable = type { float ()* }
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @dispatchTable = global %DispatchTable { float ()* @private_fn }
 @"type_info::<core::f32>::name" = private unnamed_addr constant [10 x i8] c"core::f32\00"
-@"type_info::<core::f32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"P\19b7\A8k\F2\81P\FB\83\F5P\B0\82!", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::f32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::f32>"]
+@"type_info::<core::f32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"P\19b7\A8k\F2\81P\FB\83\F5P\B0\82!", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::f32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::f32>"]
 
 declare float @private_fn()
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__nested_private_recursive_fn_with_args.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__nested_private_recursive_fn_with_args.snap
index 36fd5fdcf..59d1e01b6 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__nested_private_recursive_fn_with_args.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__nested_private_recursive_fn_with_args.snap
@@ -7,10 +7,10 @@ expression: "extern fn other() -> i32;\n\nfn private_fn(a: i32) -> f32 {\n    pr
 source_filename = "main.mun"
 
 %DispatchTable = type { float (i32)*, i32 ()* }
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @dispatchTable = external global %DispatchTable
-@global_type_table = external global [2 x %struct.MunTypeInfo*]
+@global_type_table = external global [2 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define float @private_fn(i32) {
 body:
@@ -34,14 +34,14 @@ body:
 source_filename = "group_name"
 
 %DispatchTable = type { float (i32)*, i32 ()* }
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @dispatchTable = global %DispatchTable { float (i32)* @private_fn, i32 ()* null }
 @"type_info::<core::i32>::name" = private unnamed_addr constant [10 x i8] c"core::i32\00"
-@"type_info::<core::i32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@"type_info::<core::i32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
 @"type_info::<core::f32>::name" = private unnamed_addr constant [10 x i8] c"core::f32\00"
-@"type_info::<core::f32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"P\19b7\A8k\F2\81P\FB\83\F5P\B0\82!", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::f32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [2 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i32>", %struct.MunTypeInfo* @"type_info::<core::f32>"]
+@"type_info::<core::f32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"P\19b7\A8k\F2\81P\FB\83\F5P\B0\82!", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::f32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [2 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>", %"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::f32>"]
 
 declare float @private_fn(i32)
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__nested_structs.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__nested_structs.snap
index 5d2c9103e..ee955bce9 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__nested_structs.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__nested_structs.snap
@@ -7,7 +7,7 @@ expression: "struct(gc) GcStruct(f32, f32);\nstruct(value) ValueStruct(f32, f32)
 source_filename = "main.mun"
 
 %DispatchTable = type { i8** (i8*, i8*)* }
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 %GcStruct = type { float, float }
 %ValueStruct = type { float, float }
 %GcWrapper = type { %GcStruct**, %ValueStruct }
@@ -15,15 +15,15 @@ source_filename = "main.mun"
 
 @allocatorHandle = external global i8*
 @dispatchTable = external global %DispatchTable
-@global_type_table = external global [8 x %struct.MunTypeInfo*]
+@global_type_table = external global [8 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define %GcStruct** @new_gc_struct(float, float) {
 body:
   %init = insertvalue %GcStruct undef, float %0, 0
   %init3 = insertvalue %GcStruct %init, float %1, 1
   %new_ptr = load i8** (i8*, i8*)*, i8** (i8*, i8*)** getelementptr inbounds (%DispatchTable, %DispatchTable* @dispatchTable, i32 0, i32 0)
-  %GcStruct_ptr = load %struct.MunTypeInfo*, %struct.MunTypeInfo** getelementptr inbounds ([8 x %struct.MunTypeInfo*], [8 x %struct.MunTypeInfo*]* @global_type_table, i32 0, i32 5)
-  %type_info_ptr_to_i8_ptr = bitcast %struct.MunTypeInfo* %GcStruct_ptr to i8*
+  %GcStruct_ptr = load %"mun_codegen::ir::types::TypeInfo"*, %"mun_codegen::ir::types::TypeInfo"** getelementptr inbounds ([8 x %"mun_codegen::ir::types::TypeInfo"*], [8 x %"mun_codegen::ir::types::TypeInfo"*]* @global_type_table, i64 0, i64 5)
+  %type_info_ptr_to_i8_ptr = bitcast %"mun_codegen::ir::types::TypeInfo"* %GcStruct_ptr to i8*
   %allocator_handle = load i8*, i8** @allocatorHandle
   %new = call i8** %new_ptr(i8* %type_info_ptr_to_i8_ptr, i8* %allocator_handle)
   %GcStruct_ptr_ptr = bitcast i8** %new to %GcStruct**
@@ -43,8 +43,8 @@ define %ValueStruct** @new_value_struct_wrapper(float, float) {
 body:
   %new_value_struct = call %ValueStruct @new_value_struct(float %0, float %1)
   %new_ptr = load i8** (i8*, i8*)*, i8** (i8*, i8*)** getelementptr inbounds (%DispatchTable, %DispatchTable* @dispatchTable, i32 0, i32 0)
-  %ValueStruct_ptr = load %struct.MunTypeInfo*, %struct.MunTypeInfo** getelementptr inbounds ([8 x %struct.MunTypeInfo*], [8 x %struct.MunTypeInfo*]* @global_type_table, i32 0, i32 4)
-  %type_info_ptr_to_i8_ptr = bitcast %struct.MunTypeInfo* %ValueStruct_ptr to i8*
+  %ValueStruct_ptr = load %"mun_codegen::ir::types::TypeInfo"*, %"mun_codegen::ir::types::TypeInfo"** getelementptr inbounds ([8 x %"mun_codegen::ir::types::TypeInfo"*], [8 x %"mun_codegen::ir::types::TypeInfo"*]* @global_type_table, i64 0, i64 4)
+  %type_info_ptr_to_i8_ptr = bitcast %"mun_codegen::ir::types::TypeInfo"* %ValueStruct_ptr to i8*
   %allocator_handle = load i8*, i8** @allocatorHandle
   %new = call i8** %new_ptr(i8* %type_info_ptr_to_i8_ptr, i8* %allocator_handle)
   %ValueStruct_ptr_ptr = bitcast i8** %new to %ValueStruct**
@@ -60,8 +60,8 @@ body:
   %init = insertvalue %GcWrapper undef, %GcStruct** %0, 0
   %init3 = insertvalue %GcWrapper %init, %ValueStruct %1, 1
   %new_ptr = load i8** (i8*, i8*)*, i8** (i8*, i8*)** getelementptr inbounds (%DispatchTable, %DispatchTable* @dispatchTable, i32 0, i32 0)
-  %GcWrapper_ptr = load %struct.MunTypeInfo*, %struct.MunTypeInfo** getelementptr inbounds ([8 x %struct.MunTypeInfo*], [8 x %struct.MunTypeInfo*]* @global_type_table, i32 0, i32 0)
-  %type_info_ptr_to_i8_ptr = bitcast %struct.MunTypeInfo* %GcWrapper_ptr to i8*
+  %GcWrapper_ptr = load %"mun_codegen::ir::types::TypeInfo"*, %"mun_codegen::ir::types::TypeInfo"** getelementptr inbounds ([8 x %"mun_codegen::ir::types::TypeInfo"*], [8 x %"mun_codegen::ir::types::TypeInfo"*]* @global_type_table, i64 0, i64 0)
+  %type_info_ptr_to_i8_ptr = bitcast %"mun_codegen::ir::types::TypeInfo"* %GcWrapper_ptr to i8*
   %allocator_handle = load i8*, i8** @allocatorHandle
   %new = call i8** %new_ptr(i8* %type_info_ptr_to_i8_ptr, i8* %allocator_handle)
   %GcWrapper_ptr_ptr = bitcast i8** %new to %GcWrapper**
@@ -93,8 +93,8 @@ body:
   %deref = load %ValueStruct, %ValueStruct* %mem_ptr
   %new_value_wrapper = call %ValueWrapper @new_value_wrapper(%GcStruct** %0, %ValueStruct %deref)
   %new_ptr = load i8** (i8*, i8*)*, i8** (i8*, i8*)** getelementptr inbounds (%DispatchTable, %DispatchTable* @dispatchTable, i32 0, i32 0)
-  %ValueWrapper_ptr = load %struct.MunTypeInfo*, %struct.MunTypeInfo** getelementptr inbounds ([8 x %struct.MunTypeInfo*], [8 x %struct.MunTypeInfo*]* @global_type_table, i32 0, i32 2)
-  %type_info_ptr_to_i8_ptr = bitcast %struct.MunTypeInfo* %ValueWrapper_ptr to i8*
+  %ValueWrapper_ptr = load %"mun_codegen::ir::types::TypeInfo"*, %"mun_codegen::ir::types::TypeInfo"** getelementptr inbounds ([8 x %"mun_codegen::ir::types::TypeInfo"*], [8 x %"mun_codegen::ir::types::TypeInfo"*]* @global_type_table, i64 0, i64 2)
+  %type_info_ptr_to_i8_ptr = bitcast %"mun_codegen::ir::types::TypeInfo"* %ValueWrapper_ptr to i8*
   %allocator_handle = load i8*, i8** @allocatorHandle
   %new = call i8** %new_ptr(i8* %type_info_ptr_to_i8_ptr, i8* %allocator_handle)
   %ValueWrapper_ptr_ptr = bitcast i8** %new to %ValueWrapper**
@@ -109,8 +109,8 @@ body:
 source_filename = "group_name"
 
 %DispatchTable = type { i8** (i8*, i8*)* }
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
-%struct.MunStructInfo = type { i8**, %struct.MunTypeInfo**, i16*, i16, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::StructInfo" = type { i8**, %"mun_codegen::ir::types::TypeInfo"**, i16*, i16, i8 }
 
 @dispatchTable = global %DispatchTable zeroinitializer
 @"type_info::<GcWrapper>::name" = private unnamed_addr constant [10 x i8] c"GcWrapper\00"
@@ -122,33 +122,33 @@ source_filename = "group_name"
 @"struct_info::<GcStruct>::field_names.1" = private unnamed_addr constant [2 x i8] c"1\00"
 @"struct_info::<GcStruct>::field_names" = private unnamed_addr constant [2 x i8*] [i8* getelementptr inbounds ([2 x i8], [2 x i8]* @"struct_info::<GcStruct>::field_names.0", i32 0, i32 0), i8* getelementptr inbounds ([2 x i8], [2 x i8]* @"struct_info::<GcStruct>::field_names.1", i32 0, i32 0)]
 @"type_info::<core::f32>::name" = private unnamed_addr constant [10 x i8] c"core::f32\00"
-@"type_info::<core::f32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"P\19b7\A8k\F2\81P\FB\83\F5P\B0\82!", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::f32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@"struct_info::<GcStruct>::field_types" = private unnamed_addr constant [2 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::f32>", %struct.MunTypeInfo* @"type_info::<core::f32>"]
+@"type_info::<core::f32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"P\19b7\A8k\F2\81P\FB\83\F5P\B0\82!", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::f32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@"struct_info::<GcStruct>::field_types" = private unnamed_addr constant [2 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::f32>", %"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::f32>"]
 @"struct_info::<GcStruct>::field_offsets" = private unnamed_addr constant [2 x i16] [i16 0, i16 4]
-@"type_info::<GcStruct>" = private unnamed_addr constant { %struct.MunTypeInfo, %struct.MunStructInfo } { %struct.MunTypeInfo { [16 x i8] c"\B9)lg\01\95k@E\B4(\CB\CAGX\E1", i8* getelementptr inbounds ([9 x i8], [9 x i8]* @"type_info::<GcStruct>::name", i32 0, i32 0), i32 64, i8 4, i8 1 }, %struct.MunStructInfo { i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"struct_info::<GcStruct>::field_names", i32 0, i32 0), %struct.MunTypeInfo** getelementptr inbounds ([2 x %struct.MunTypeInfo*], [2 x %struct.MunTypeInfo*]* @"struct_info::<GcStruct>::field_types", i32 0, i32 0), i16* getelementptr inbounds ([2 x i16], [2 x i16]* @"struct_info::<GcStruct>::field_offsets", i32 0, i32 0), i16 2, i8 0 } }
+@"type_info::<GcStruct>" = private unnamed_addr constant { %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" } { %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\B9)lg\01\95k@E\B4(\CB\CAGX\E1", i8* getelementptr inbounds ([9 x i8], [9 x i8]* @"type_info::<GcStruct>::name", i32 0, i32 0), i32 64, i8 4, i8 1 }, %"mun_codegen::ir::types::StructInfo" { i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"struct_info::<GcStruct>::field_names", i32 0, i32 0), %"mun_codegen::ir::types::TypeInfo"** getelementptr inbounds ([2 x %"mun_codegen::ir::types::TypeInfo"*], [2 x %"mun_codegen::ir::types::TypeInfo"*]* @"struct_info::<GcStruct>::field_types", i32 0, i32 0), i16* getelementptr inbounds ([2 x i16], [2 x i16]* @"struct_info::<GcStruct>::field_offsets", i32 0, i32 0), i16 2, i8 0 } }
 @"type_info::<ValueStruct>::name" = private unnamed_addr constant [12 x i8] c"ValueStruct\00"
 @"struct_info::<ValueStruct>::field_names.0" = private unnamed_addr constant [2 x i8] c"0\00"
 @"struct_info::<ValueStruct>::field_names.1" = private unnamed_addr constant [2 x i8] c"1\00"
 @"struct_info::<ValueStruct>::field_names" = private unnamed_addr constant [2 x i8*] [i8* getelementptr inbounds ([2 x i8], [2 x i8]* @"struct_info::<ValueStruct>::field_names.0", i32 0, i32 0), i8* getelementptr inbounds ([2 x i8], [2 x i8]* @"struct_info::<ValueStruct>::field_names.1", i32 0, i32 0)]
-@"struct_info::<ValueStruct>::field_types" = private unnamed_addr constant [2 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::f32>", %struct.MunTypeInfo* @"type_info::<core::f32>"]
+@"struct_info::<ValueStruct>::field_types" = private unnamed_addr constant [2 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::f32>", %"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::f32>"]
 @"struct_info::<ValueStruct>::field_offsets" = private unnamed_addr constant [2 x i16] [i16 0, i16 4]
-@"type_info::<ValueStruct>" = private unnamed_addr constant { %struct.MunTypeInfo, %struct.MunStructInfo } { %struct.MunTypeInfo { [16 x i8] c"U0{\87\5C\04Q/\95!$\A2\F1\A9\F9W", i8* getelementptr inbounds ([12 x i8], [12 x i8]* @"type_info::<ValueStruct>::name", i32 0, i32 0), i32 64, i8 4, i8 1 }, %struct.MunStructInfo { i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"struct_info::<ValueStruct>::field_names", i32 0, i32 0), %struct.MunTypeInfo** getelementptr inbounds ([2 x %struct.MunTypeInfo*], [2 x %struct.MunTypeInfo*]* @"struct_info::<ValueStruct>::field_types", i32 0, i32 0), i16* getelementptr inbounds ([2 x i16], [2 x i16]* @"struct_info::<ValueStruct>::field_offsets", i32 0, i32 0), i16 2, i8 1 } }
-@"struct_info::<GcWrapper>::field_types" = private unnamed_addr constant [2 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* getelementptr inbounds ({ %struct.MunTypeInfo, %struct.MunStructInfo }, { %struct.MunTypeInfo, %struct.MunStructInfo }* @"type_info::<GcStruct>", i32 0, i32 0), %struct.MunTypeInfo* getelementptr inbounds ({ %struct.MunTypeInfo, %struct.MunStructInfo }, { %struct.MunTypeInfo, %struct.MunStructInfo }* @"type_info::<ValueStruct>", i32 0, i32 0)]
+@"type_info::<ValueStruct>" = private unnamed_addr constant { %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" } { %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"U0{\87\5C\04Q/\95!$\A2\F1\A9\F9W", i8* getelementptr inbounds ([12 x i8], [12 x i8]* @"type_info::<ValueStruct>::name", i32 0, i32 0), i32 64, i8 4, i8 1 }, %"mun_codegen::ir::types::StructInfo" { i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"struct_info::<ValueStruct>::field_names", i32 0, i32 0), %"mun_codegen::ir::types::TypeInfo"** getelementptr inbounds ([2 x %"mun_codegen::ir::types::TypeInfo"*], [2 x %"mun_codegen::ir::types::TypeInfo"*]* @"struct_info::<ValueStruct>::field_types", i32 0, i32 0), i16* getelementptr inbounds ([2 x i16], [2 x i16]* @"struct_info::<ValueStruct>::field_offsets", i32 0, i32 0), i16 2, i8 1 } }
+@"struct_info::<GcWrapper>::field_types" = private unnamed_addr constant [2 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* getelementptr inbounds ({ %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }, { %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }* @"type_info::<GcStruct>", i32 0, i32 0), %"mun_codegen::ir::types::TypeInfo"* getelementptr inbounds ({ %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }, { %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }* @"type_info::<ValueStruct>", i32 0, i32 0)]
 @"struct_info::<GcWrapper>::field_offsets" = private unnamed_addr constant [2 x i16] [i16 0, i16 8]
-@"type_info::<GcWrapper>" = private unnamed_addr constant { %struct.MunTypeInfo, %struct.MunStructInfo } { %struct.MunTypeInfo { [16 x i8] c"!\14\93\A7H1?90\B7\EA\DB0\82\A0\C7", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<GcWrapper>::name", i32 0, i32 0), i32 128, i8 8, i8 1 }, %struct.MunStructInfo { i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"struct_info::<GcWrapper>::field_names", i32 0, i32 0), %struct.MunTypeInfo** getelementptr inbounds ([2 x %struct.MunTypeInfo*], [2 x %struct.MunTypeInfo*]* @"struct_info::<GcWrapper>::field_types", i32 0, i32 0), i16* getelementptr inbounds ([2 x i16], [2 x i16]* @"struct_info::<GcWrapper>::field_offsets", i32 0, i32 0), i16 2, i8 0 } }
+@"type_info::<GcWrapper>" = private unnamed_addr constant { %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" } { %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"!\14\93\A7H1?90\B7\EA\DB0\82\A0\C7", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<GcWrapper>::name", i32 0, i32 0), i32 128, i8 8, i8 1 }, %"mun_codegen::ir::types::StructInfo" { i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"struct_info::<GcWrapper>::field_names", i32 0, i32 0), %"mun_codegen::ir::types::TypeInfo"** getelementptr inbounds ([2 x %"mun_codegen::ir::types::TypeInfo"*], [2 x %"mun_codegen::ir::types::TypeInfo"*]* @"struct_info::<GcWrapper>::field_types", i32 0, i32 0), i16* getelementptr inbounds ([2 x i16], [2 x i16]* @"struct_info::<GcWrapper>::field_offsets", i32 0, i32 0), i16 2, i8 0 } }
 @"type_info::<*const TypeInfo>::name" = private unnamed_addr constant [16 x i8] c"*const TypeInfo\00"
-@"type_info::<*const TypeInfo>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"=\A1-\1F\C2\A7\88`d\90\F4\B5\BEE}x", i8* getelementptr inbounds ([16 x i8], [16 x i8]* @"type_info::<*const TypeInfo>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@"type_info::<*const TypeInfo>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"=\A1-\1F\C2\A7\88`d\90\F4\B5\BEE}x", i8* getelementptr inbounds ([16 x i8], [16 x i8]* @"type_info::<*const TypeInfo>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
 @"type_info::<ValueWrapper>::name" = private unnamed_addr constant [13 x i8] c"ValueWrapper\00"
 @"struct_info::<ValueWrapper>::field_names.0" = private unnamed_addr constant [2 x i8] c"0\00"
 @"struct_info::<ValueWrapper>::field_names.1" = private unnamed_addr constant [2 x i8] c"1\00"
 @"struct_info::<ValueWrapper>::field_names" = private unnamed_addr constant [2 x i8*] [i8* getelementptr inbounds ([2 x i8], [2 x i8]* @"struct_info::<ValueWrapper>::field_names.0", i32 0, i32 0), i8* getelementptr inbounds ([2 x i8], [2 x i8]* @"struct_info::<ValueWrapper>::field_names.1", i32 0, i32 0)]
-@"struct_info::<ValueWrapper>::field_types" = private unnamed_addr constant [2 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* getelementptr inbounds ({ %struct.MunTypeInfo, %struct.MunStructInfo }, { %struct.MunTypeInfo, %struct.MunStructInfo }* @"type_info::<GcStruct>", i32 0, i32 0), %struct.MunTypeInfo* getelementptr inbounds ({ %struct.MunTypeInfo, %struct.MunStructInfo }, { %struct.MunTypeInfo, %struct.MunStructInfo }* @"type_info::<ValueStruct>", i32 0, i32 0)]
+@"struct_info::<ValueWrapper>::field_types" = private unnamed_addr constant [2 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* getelementptr inbounds ({ %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }, { %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }* @"type_info::<GcStruct>", i32 0, i32 0), %"mun_codegen::ir::types::TypeInfo"* getelementptr inbounds ({ %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }, { %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }* @"type_info::<ValueStruct>", i32 0, i32 0)]
 @"struct_info::<ValueWrapper>::field_offsets" = private unnamed_addr constant [2 x i16] [i16 0, i16 8]
-@"type_info::<ValueWrapper>" = private unnamed_addr constant { %struct.MunTypeInfo, %struct.MunStructInfo } { %struct.MunTypeInfo { [16 x i8] c"@j\D8\CD~-\12\87|A\E8\DBp\EC}\AA", i8* getelementptr inbounds ([13 x i8], [13 x i8]* @"type_info::<ValueWrapper>::name", i32 0, i32 0), i32 128, i8 8, i8 1 }, %struct.MunStructInfo { i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"struct_info::<ValueWrapper>::field_names", i32 0, i32 0), %struct.MunTypeInfo** getelementptr inbounds ([2 x %struct.MunTypeInfo*], [2 x %struct.MunTypeInfo*]* @"struct_info::<ValueWrapper>::field_types", i32 0, i32 0), i16* getelementptr inbounds ([2 x i16], [2 x i16]* @"struct_info::<ValueWrapper>::field_offsets", i32 0, i32 0), i16 2, i8 1 } }
+@"type_info::<ValueWrapper>" = private unnamed_addr constant { %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" } { %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"@j\D8\CD~-\12\87|A\E8\DBp\EC}\AA", i8* getelementptr inbounds ([13 x i8], [13 x i8]* @"type_info::<ValueWrapper>::name", i32 0, i32 0), i32 128, i8 8, i8 1 }, %"mun_codegen::ir::types::StructInfo" { i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"struct_info::<ValueWrapper>::field_names", i32 0, i32 0), %"mun_codegen::ir::types::TypeInfo"** getelementptr inbounds ([2 x %"mun_codegen::ir::types::TypeInfo"*], [2 x %"mun_codegen::ir::types::TypeInfo"*]* @"struct_info::<ValueWrapper>::field_types", i32 0, i32 0), i16* getelementptr inbounds ([2 x i16], [2 x i16]* @"struct_info::<ValueWrapper>::field_offsets", i32 0, i32 0), i16 2, i8 1 } }
 @"type_info::<*const *mut core::void>::name" = private unnamed_addr constant [23 x i8] c"*const *mut core::void\00"
-@"type_info::<*const *mut core::void>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\C5fO\BD\84\DF\06\BFd+\B1\9Abv\CE\00", i8* getelementptr inbounds ([23 x i8], [23 x i8]* @"type_info::<*const *mut core::void>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@"type_info::<*const *mut core::void>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\C5fO\BD\84\DF\06\BFd+\B1\9Abv\CE\00", i8* getelementptr inbounds ([23 x i8], [23 x i8]* @"type_info::<*const *mut core::void>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
 @"type_info::<*mut core::void>::name" = private unnamed_addr constant [16 x i8] c"*mut core::void\00"
-@"type_info::<*mut core::void>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\F0Y\22\FC\95\9E\7F\CE\08T\B1\A2\CD\A7\FAz", i8* getelementptr inbounds ([16 x i8], [16 x i8]* @"type_info::<*mut core::void>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
-@global_type_table = constant [8 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* getelementptr inbounds ({ %struct.MunTypeInfo, %struct.MunStructInfo }, { %struct.MunTypeInfo, %struct.MunStructInfo }* @"type_info::<GcWrapper>", i32 0, i32 0), %struct.MunTypeInfo* @"type_info::<*const TypeInfo>", %struct.MunTypeInfo* getelementptr inbounds ({ %struct.MunTypeInfo, %struct.MunStructInfo }, { %struct.MunTypeInfo, %struct.MunStructInfo }* @"type_info::<ValueWrapper>", i32 0, i32 0), %struct.MunTypeInfo* @"type_info::<core::f32>", %struct.MunTypeInfo* getelementptr inbounds ({ %struct.MunTypeInfo, %struct.MunStructInfo }, { %struct.MunTypeInfo, %struct.MunStructInfo }* @"type_info::<ValueStruct>", i32 0, i32 0), %struct.MunTypeInfo* getelementptr inbounds ({ %struct.MunTypeInfo, %struct.MunStructInfo }, { %struct.MunTypeInfo, %struct.MunStructInfo }* @"type_info::<GcStruct>", i32 0, i32 0), %struct.MunTypeInfo* @"type_info::<*const *mut core::void>", %struct.MunTypeInfo* @"type_info::<*mut core::void>"]
+@"type_info::<*mut core::void>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\F0Y\22\FC\95\9E\7F\CE\08T\B1\A2\CD\A7\FAz", i8* getelementptr inbounds ([16 x i8], [16 x i8]* @"type_info::<*mut core::void>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@global_type_table = constant [8 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* getelementptr inbounds ({ %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }, { %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }* @"type_info::<GcWrapper>", i32 0, i32 0), %"mun_codegen::ir::types::TypeInfo"* @"type_info::<*const TypeInfo>", %"mun_codegen::ir::types::TypeInfo"* getelementptr inbounds ({ %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }, { %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }* @"type_info::<ValueWrapper>", i32 0, i32 0), %"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::f32>", %"mun_codegen::ir::types::TypeInfo"* getelementptr inbounds ({ %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }, { %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }* @"type_info::<ValueStruct>", i32 0, i32 0), %"mun_codegen::ir::types::TypeInfo"* getelementptr inbounds ({ %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }, { %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }* @"type_info::<GcStruct>", i32 0, i32 0), %"mun_codegen::ir::types::TypeInfo"* @"type_info::<*const *mut core::void>", %"mun_codegen::ir::types::TypeInfo"* @"type_info::<*mut core::void>"]
 @allocatorHandle = unnamed_addr global i8* null
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__never_conditional_return_expr.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__never_conditional_return_expr.snap
index af09ad422..689d216db 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__never_conditional_return_expr.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__never_conditional_return_expr.snap
@@ -6,9 +6,9 @@ expression: "pub fn main(a:i32) -> i32 {\n    if a > 4 {\n        return a;\n
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i32 @main(i32) {
 body:
@@ -28,9 +28,9 @@ else:                                             ; preds = %body
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i32>::name" = private unnamed_addr constant [10 x i8] c"core::i32\00"
-@"type_info::<core::i32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i32>"]
+@"type_info::<core::i32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__return_expr.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__return_expr.snap
index 343ee610b..8cb14c05b 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__return_expr.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__return_expr.snap
@@ -6,9 +6,9 @@ expression: "pub fn main() -> i32 {\n    return 5;\n    let a = 3; // Nothing re
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i32 @main() {
 body:
@@ -20,9 +20,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i32>::name" = private unnamed_addr constant [10 x i8] c"core::i32\00"
-@"type_info::<core::i32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i32>"]
+@"type_info::<core::i32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__return_type.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__return_type.snap
index 72da60f3f..4cb4df3e9 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__return_type.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__return_type.snap
@@ -6,9 +6,9 @@ expression: "pub fn main() -> i32 {\n  0\n}"
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i32 @main() {
 body:
@@ -20,9 +20,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i32>::name" = private unnamed_addr constant [10 x i8] c"core::i32\00"
-@"type_info::<core::i32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i32>"]
+@"type_info::<core::i32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__shadowing.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__shadowing.snap
index 623b87171..ea22f4a69 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__shadowing.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__shadowing.snap
@@ -6,9 +6,9 @@ expression: "pub fn foo(a:i32) -> i32 {\n    let a = a+1;\n    {\n        let a
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i32 @foo(i32) {
 body:
@@ -30,9 +30,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i32>::name" = private unnamed_addr constant [10 x i8] c"core::i32\00"
-@"type_info::<core::i32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i32>"]
+@"type_info::<core::i32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_i128.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_i128.snap
index f0a1aa0de..1f0efdbc5 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_i128.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_i128.snap
@@ -6,9 +6,9 @@ expression: "pub fn leftshift(a: i128, b: i128) -> i128 { a << b }\npub fn right
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i128 @leftshift(i128, i128) {
 body:
@@ -27,9 +27,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i128>::name" = private unnamed_addr constant [11 x i8] c"core::i128\00"
-@"type_info::<core::i128>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\BDkp\09RRM\EBc\02\A0\DB47\A7\E3", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::i128>::name", i32 0, i32 0), i32 128, i8 8, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i128>"]
+@"type_info::<core::i128>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\BDkp\09RRM\EBc\02\A0\DB47\A7\E3", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::i128>::name", i32 0, i32 0), i32 128, i8 8, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i128>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_i16.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_i16.snap
index 98f3b2e4c..fc9519fdb 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_i16.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_i16.snap
@@ -6,9 +6,9 @@ expression: "pub fn leftshift(a: i16, b: i16) -> i16 { a << b }\npub fn rightshi
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i16 @leftshift(i16, i16) {
 body:
@@ -27,9 +27,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i16>::name" = private unnamed_addr constant [10 x i8] c"core::i16\00"
-@"type_info::<core::i16>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\05\CD|\F8Bv\D8\B1\E8\8B\8C\D8\8D\B5\89\B0", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i16>::name", i32 0, i32 0), i32 16, i8 2, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i16>"]
+@"type_info::<core::i16>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\05\CD|\F8Bv\D8\B1\E8\8B\8C\D8\8D\B5\89\B0", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i16>::name", i32 0, i32 0), i32 16, i8 2, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i16>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_i32.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_i32.snap
index 1cf64d412..c7d1df799 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_i32.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_i32.snap
@@ -6,9 +6,9 @@ expression: "pub fn leftshift(a: i32, b: i32) -> i32 { a << b }\npub fn rightshi
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i32 @leftshift(i32, i32) {
 body:
@@ -27,9 +27,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i32>::name" = private unnamed_addr constant [10 x i8] c"core::i32\00"
-@"type_info::<core::i32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i32>"]
+@"type_info::<core::i32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_i64.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_i64.snap
index 7e1e71d5c..f6fa09982 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_i64.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_i64.snap
@@ -6,9 +6,9 @@ expression: "pub fn leftshift(a: i64, b: i64) -> i64 { a << b }\npub fn rightshi
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i64 @leftshift(i64, i64) {
 body:
@@ -27,9 +27,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i64>::name" = private unnamed_addr constant [10 x i8] c"core::i64\00"
-@"type_info::<core::i64>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"G\13;t\97j8\18\D7M\83`\1D\C8\19%", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i64>"]
+@"type_info::<core::i64>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"G\13;t\97j8\18\D7M\83`\1D\C8\19%", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i64>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_i8.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_i8.snap
index 48a1c27de..3387c2535 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_i8.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_i8.snap
@@ -6,9 +6,9 @@ expression: "pub fn leftshift(a: i8, b: i8) -> i8 { a << b }\npub fn rightshift(
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i8 @leftshift(i8, i8) {
 body:
@@ -27,9 +27,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i8>::name" = private unnamed_addr constant [9 x i8] c"core::i8\00"
-@"type_info::<core::i8>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\EF\C4\B1Z\E7\12\B1\91q\F1\0B\80U\FC\A6\0F", i8* getelementptr inbounds ([9 x i8], [9 x i8]* @"type_info::<core::i8>::name", i32 0, i32 0), i32 8, i8 1, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i8>"]
+@"type_info::<core::i8>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\EF\C4\B1Z\E7\12\B1\91q\F1\0B\80U\FC\A6\0F", i8* getelementptr inbounds ([9 x i8], [9 x i8]* @"type_info::<core::i8>::name", i32 0, i32 0), i32 8, i8 1, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i8>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_u128.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_u128.snap
index 774acd6da..cd8760e58 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_u128.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_u128.snap
@@ -6,9 +6,9 @@ expression: "pub fn leftshift(a: u128, b: u128) -> u128 { a << b }\npub fn right
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i128 @leftshift(i128, i128) {
 body:
@@ -27,9 +27,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::u128>::name" = private unnamed_addr constant [11 x i8] c"core::u128\00"
-@"type_info::<core::u128>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\E67\1BU\E9k\95\93d\14}\1C\96S\95\F0", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::u128>::name", i32 0, i32 0), i32 128, i8 8, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::u128>"]
+@"type_info::<core::u128>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\E67\1BU\E9k\95\93d\14}\1C\96S\95\F0", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::u128>::name", i32 0, i32 0), i32 128, i8 8, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::u128>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_u16.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_u16.snap
index bbaf4dce4..f61524a45 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_u16.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_u16.snap
@@ -6,9 +6,9 @@ expression: "pub fn leftshift(a: u16, b: u16) -> u16 { a << b }\npub fn rightshi
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i16 @leftshift(i16, i16) {
 body:
@@ -27,9 +27,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::u16>::name" = private unnamed_addr constant [10 x i8] c"core::u16\00"
-@"type_info::<core::u16>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"0\01\BC\BBK\E0\F2\7F&l\01\CD|q\F2\B3", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u16>::name", i32 0, i32 0), i32 16, i8 2, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::u16>"]
+@"type_info::<core::u16>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"0\01\BC\BBK\E0\F2\7F&l\01\CD|q\F2\B3", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u16>::name", i32 0, i32 0), i32 16, i8 2, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::u16>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_u32.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_u32.snap
index 2be90eaf8..4faf2527c 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_u32.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_u32.snap
@@ -6,9 +6,9 @@ expression: "pub fn leftshift(a: u32, b: u32) -> u32 { a << b }\npub fn rightshi
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i32 @leftshift(i32, i32) {
 body:
@@ -27,9 +27,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::u32>::name" = private unnamed_addr constant [10 x i8] c"core::u32\00"
-@"type_info::<core::u32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"daz5d\A6\BE\88\81=&Y\A1+\C6\1D", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::u32>"]
+@"type_info::<core::u32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"daz5d\A6\BE\88\81=&Y\A1+\C6\1D", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::u32>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_u64.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_u64.snap
index 7ec0de9a0..f358afb04 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_u64.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_u64.snap
@@ -6,9 +6,9 @@ expression: "pub fn leftshift(a: u64, b: u64) -> u64 { a << b }\npub fn rightshi
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i64 @leftshift(i64, i64) {
 body:
@@ -27,9 +27,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::u64>::name" = private unnamed_addr constant [10 x i8] c"core::u64\00"
-@"type_info::<core::u64>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\A6\E7g \D1\8B\1Aq`\1F\1E\07\BB5@q", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::u64>"]
+@"type_info::<core::u64>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\A6\E7g \D1\8B\1Aq`\1F\1E\07\BB5@q", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::u64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::u64>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_u8.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_u8.snap
index fdb25d3ec..ea8653bf3 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_u8.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__shift_op_u8.snap
@@ -6,9 +6,9 @@ expression: "pub fn leftshift(a: u8, b: u8) -> u8 { a << b }\npub fn rightshift(
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i8 @leftshift(i8, i8) {
 body:
@@ -27,9 +27,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::u8>::name" = private unnamed_addr constant [9 x i8] c"core::u8\00"
-@"type_info::<core::u8>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\A0y\A7S\B6(n\F7f&H\E1\F9\AD\04>", i8* getelementptr inbounds ([9 x i8], [9 x i8]* @"type_info::<core::u8>::name", i32 0, i32 0), i32 8, i8 1, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::u8>"]
+@"type_info::<core::u8>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\A0y\A7S\B6(n\F7f&H\E1\F9\AD\04>", i8* getelementptr inbounds ([9 x i8], [9 x i8]* @"type_info::<core::u8>::name", i32 0, i32 0), i32 8, i8 1, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::u8>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__struct_test.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__struct_test.snap
index c9cfa86af..cc62c2366 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__struct_test.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__struct_test.snap
@@ -7,14 +7,14 @@ expression: "struct(value) Bar(f64, i32, bool, Foo);\nstruct(value) Foo { a: i32
 source_filename = "main.mun"
 
 %DispatchTable = type { i8** (i8*, i8*)* }
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 %Baz = type {}
 %Bar = type { double, i32, i1, %Foo }
 %Foo = type { i32 }
 
 @allocatorHandle = external global i8*
 @dispatchTable = external global %DispatchTable
-@global_type_table = external global [9 x %struct.MunTypeInfo*]
+@global_type_table = external global [9 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define void @foo() {
 body:
@@ -39,39 +39,39 @@ body:
 source_filename = "group_name"
 
 %DispatchTable = type { i8** (i8*, i8*)* }
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
-%struct.MunStructInfo = type { i8**, %struct.MunTypeInfo**, i16*, i16, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::StructInfo" = type { i8**, %"mun_codegen::ir::types::TypeInfo"**, i16*, i16, i8 }
 
 @dispatchTable = global %DispatchTable zeroinitializer
 @"type_info::<core::i32>::name" = private unnamed_addr constant [10 x i8] c"core::i32\00"
-@"type_info::<core::i32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@"type_info::<core::i32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
 @"type_info::<Foo>::name" = private unnamed_addr constant [4 x i8] c"Foo\00"
 @"struct_info::<Foo>::field_names.0" = private unnamed_addr constant [2 x i8] c"a\00"
 @"struct_info::<Foo>::field_names" = private unnamed_addr constant [1 x i8*] [i8* getelementptr inbounds ([2 x i8], [2 x i8]* @"struct_info::<Foo>::field_names.0", i32 0, i32 0)]
-@"struct_info::<Foo>::field_types" = private unnamed_addr constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i32>"]
+@"struct_info::<Foo>::field_types" = private unnamed_addr constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>"]
 @"struct_info::<Foo>::field_offsets" = private unnamed_addr constant [1 x i16] zeroinitializer
-@"type_info::<Foo>" = private unnamed_addr constant { %struct.MunTypeInfo, %struct.MunStructInfo } { %struct.MunTypeInfo { [16 x i8] c"8\AD\C9\D9Y\D2\AA<ME}\CD.\A5\A0\DB", i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"type_info::<Foo>::name", i32 0, i32 0), i32 32, i8 4, i8 1 }, %struct.MunStructInfo { i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"struct_info::<Foo>::field_names", i32 0, i32 0), %struct.MunTypeInfo** getelementptr inbounds ([1 x %struct.MunTypeInfo*], [1 x %struct.MunTypeInfo*]* @"struct_info::<Foo>::field_types", i32 0, i32 0), i16* getelementptr inbounds ([1 x i16], [1 x i16]* @"struct_info::<Foo>::field_offsets", i32 0, i32 0), i16 1, i8 1 } }
+@"type_info::<Foo>" = private unnamed_addr constant { %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" } { %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"8\AD\C9\D9Y\D2\AA<ME}\CD.\A5\A0\DB", i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"type_info::<Foo>::name", i32 0, i32 0), i32 32, i8 4, i8 1 }, %"mun_codegen::ir::types::StructInfo" { i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"struct_info::<Foo>::field_names", i32 0, i32 0), %"mun_codegen::ir::types::TypeInfo"** getelementptr inbounds ([1 x %"mun_codegen::ir::types::TypeInfo"*], [1 x %"mun_codegen::ir::types::TypeInfo"*]* @"struct_info::<Foo>::field_types", i32 0, i32 0), i16* getelementptr inbounds ([1 x i16], [1 x i16]* @"struct_info::<Foo>::field_offsets", i32 0, i32 0), i16 1, i8 1 } }
 @"type_info::<*const TypeInfo>::name" = private unnamed_addr constant [16 x i8] c"*const TypeInfo\00"
-@"type_info::<*const TypeInfo>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"=\A1-\1F\C2\A7\88`d\90\F4\B5\BEE}x", i8* getelementptr inbounds ([16 x i8], [16 x i8]* @"type_info::<*const TypeInfo>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@"type_info::<*const TypeInfo>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"=\A1-\1F\C2\A7\88`d\90\F4\B5\BEE}x", i8* getelementptr inbounds ([16 x i8], [16 x i8]* @"type_info::<*const TypeInfo>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
 @"type_info::<Baz>::name" = private unnamed_addr constant [4 x i8] c"Baz\00"
-@"type_info::<Baz>" = private unnamed_addr constant { %struct.MunTypeInfo, %struct.MunStructInfo } { %struct.MunTypeInfo { [16 x i8] c">\1A\BC\E5\C9\D3n\D8\8C?\86\22\FA\0DtV", i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"type_info::<Baz>::name", i32 0, i32 0), i32 0, i8 1, i8 1 }, %struct.MunStructInfo { i8** null, %struct.MunTypeInfo** null, i16* null, i16 0, i8 1 } }
+@"type_info::<Baz>" = private unnamed_addr constant { %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" } { %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c">\1A\BC\E5\C9\D3n\D8\8C?\86\22\FA\0DtV", i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"type_info::<Baz>::name", i32 0, i32 0), i32 0, i8 1, i8 1 }, %"mun_codegen::ir::types::StructInfo" { i8** null, %"mun_codegen::ir::types::TypeInfo"** null, i16* null, i16 0, i8 1 } }
 @"type_info::<core::f64>::name" = private unnamed_addr constant [10 x i8] c"core::f64\00"
-@"type_info::<core::f64>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"`\DBF\9C?YJ%G\AD4\9F\D5\92%A", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::f64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@"type_info::<core::f64>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"`\DBF\9C?YJ%G\AD4\9F\D5\92%A", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::f64>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
 @"type_info::<core::bool>::name" = private unnamed_addr constant [11 x i8] c"core::bool\00"
-@"type_info::<core::bool>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::bool>::name", i32 0, i32 0), i32 1, i8 1, i8 0 }
+@"type_info::<core::bool>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::bool>::name", i32 0, i32 0), i32 1, i8 1, i8 0 }
 @"type_info::<*const *mut core::void>::name" = private unnamed_addr constant [23 x i8] c"*const *mut core::void\00"
-@"type_info::<*const *mut core::void>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\C5fO\BD\84\DF\06\BFd+\B1\9Abv\CE\00", i8* getelementptr inbounds ([23 x i8], [23 x i8]* @"type_info::<*const *mut core::void>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@"type_info::<*const *mut core::void>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\C5fO\BD\84\DF\06\BFd+\B1\9Abv\CE\00", i8* getelementptr inbounds ([23 x i8], [23 x i8]* @"type_info::<*const *mut core::void>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
 @"type_info::<Bar>::name" = private unnamed_addr constant [4 x i8] c"Bar\00"
 @"struct_info::<Bar>::field_names.0" = private unnamed_addr constant [2 x i8] c"0\00"
 @"struct_info::<Bar>::field_names.1" = private unnamed_addr constant [2 x i8] c"1\00"
 @"struct_info::<Bar>::field_names.2" = private unnamed_addr constant [2 x i8] c"2\00"
 @"struct_info::<Bar>::field_names.3" = private unnamed_addr constant [2 x i8] c"3\00"
 @"struct_info::<Bar>::field_names" = private unnamed_addr constant [4 x i8*] [i8* getelementptr inbounds ([2 x i8], [2 x i8]* @"struct_info::<Bar>::field_names.0", i32 0, i32 0), i8* getelementptr inbounds ([2 x i8], [2 x i8]* @"struct_info::<Bar>::field_names.1", i32 0, i32 0), i8* getelementptr inbounds ([2 x i8], [2 x i8]* @"struct_info::<Bar>::field_names.2", i32 0, i32 0), i8* getelementptr inbounds ([2 x i8], [2 x i8]* @"struct_info::<Bar>::field_names.3", i32 0, i32 0)]
-@"struct_info::<Bar>::field_types" = private unnamed_addr constant [4 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::f64>", %struct.MunTypeInfo* @"type_info::<core::i32>", %struct.MunTypeInfo* @"type_info::<core::bool>", %struct.MunTypeInfo* getelementptr inbounds ({ %struct.MunTypeInfo, %struct.MunStructInfo }, { %struct.MunTypeInfo, %struct.MunStructInfo }* @"type_info::<Foo>", i32 0, i32 0)]
+@"struct_info::<Bar>::field_types" = private unnamed_addr constant [4 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::f64>", %"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>", %"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::bool>", %"mun_codegen::ir::types::TypeInfo"* getelementptr inbounds ({ %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }, { %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }* @"type_info::<Foo>", i32 0, i32 0)]
 @"struct_info::<Bar>::field_offsets" = private unnamed_addr constant [4 x i16] [i16 0, i16 8, i16 12, i16 16]
-@"type_info::<Bar>" = private unnamed_addr constant { %struct.MunTypeInfo, %struct.MunStructInfo } { %struct.MunTypeInfo { [16 x i8] c"\D6\CA\E2\C3Ht\09\EA\AEh\E50L\F7\EE\B5", i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"type_info::<Bar>::name", i32 0, i32 0), i32 192, i8 8, i8 1 }, %struct.MunStructInfo { i8** getelementptr inbounds ([4 x i8*], [4 x i8*]* @"struct_info::<Bar>::field_names", i32 0, i32 0), %struct.MunTypeInfo** getelementptr inbounds ([4 x %struct.MunTypeInfo*], [4 x %struct.MunTypeInfo*]* @"struct_info::<Bar>::field_types", i32 0, i32 0), i16* getelementptr inbounds ([4 x i16], [4 x i16]* @"struct_info::<Bar>::field_offsets", i32 0, i32 0), i16 4, i8 1 } }
+@"type_info::<Bar>" = private unnamed_addr constant { %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" } { %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\D6\CA\E2\C3Ht\09\EA\AEh\E50L\F7\EE\B5", i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"type_info::<Bar>::name", i32 0, i32 0), i32 192, i8 8, i8 1 }, %"mun_codegen::ir::types::StructInfo" { i8** getelementptr inbounds ([4 x i8*], [4 x i8*]* @"struct_info::<Bar>::field_names", i32 0, i32 0), %"mun_codegen::ir::types::TypeInfo"** getelementptr inbounds ([4 x %"mun_codegen::ir::types::TypeInfo"*], [4 x %"mun_codegen::ir::types::TypeInfo"*]* @"struct_info::<Bar>::field_types", i32 0, i32 0), i16* getelementptr inbounds ([4 x i16], [4 x i16]* @"struct_info::<Bar>::field_offsets", i32 0, i32 0), i16 4, i8 1 } }
 @"type_info::<*mut core::void>::name" = private unnamed_addr constant [16 x i8] c"*mut core::void\00"
-@"type_info::<*mut core::void>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\F0Y\22\FC\95\9E\7F\CE\08T\B1\A2\CD\A7\FAz", i8* getelementptr inbounds ([16 x i8], [16 x i8]* @"type_info::<*mut core::void>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
-@global_type_table = constant [9 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i32>", %struct.MunTypeInfo* getelementptr inbounds ({ %struct.MunTypeInfo, %struct.MunStructInfo }, { %struct.MunTypeInfo, %struct.MunStructInfo }* @"type_info::<Foo>", i32 0, i32 0), %struct.MunTypeInfo* @"type_info::<*const TypeInfo>", %struct.MunTypeInfo* getelementptr inbounds ({ %struct.MunTypeInfo, %struct.MunStructInfo }, { %struct.MunTypeInfo, %struct.MunStructInfo }* @"type_info::<Baz>", i32 0, i32 0), %struct.MunTypeInfo* @"type_info::<core::f64>", %struct.MunTypeInfo* @"type_info::<core::bool>", %struct.MunTypeInfo* @"type_info::<*const *mut core::void>", %struct.MunTypeInfo* getelementptr inbounds ({ %struct.MunTypeInfo, %struct.MunStructInfo }, { %struct.MunTypeInfo, %struct.MunStructInfo }* @"type_info::<Bar>", i32 0, i32 0), %struct.MunTypeInfo* @"type_info::<*mut core::void>"]
+@"type_info::<*mut core::void>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\F0Y\22\FC\95\9E\7F\CE\08T\B1\A2\CD\A7\FAz", i8* getelementptr inbounds ([16 x i8], [16 x i8]* @"type_info::<*mut core::void>::name", i32 0, i32 0), i32 64, i8 8, i8 0 }
+@global_type_table = constant [9 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>", %"mun_codegen::ir::types::TypeInfo"* getelementptr inbounds ({ %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }, { %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }* @"type_info::<Foo>", i32 0, i32 0), %"mun_codegen::ir::types::TypeInfo"* @"type_info::<*const TypeInfo>", %"mun_codegen::ir::types::TypeInfo"* getelementptr inbounds ({ %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }, { %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }* @"type_info::<Baz>", i32 0, i32 0), %"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::f64>", %"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::bool>", %"mun_codegen::ir::types::TypeInfo"* @"type_info::<*const *mut core::void>", %"mun_codegen::ir::types::TypeInfo"* getelementptr inbounds ({ %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }, { %"mun_codegen::ir::types::TypeInfo", %"mun_codegen::ir::types::StructInfo" }* @"type_info::<Bar>", i32 0, i32 0), %"mun_codegen::ir::types::TypeInfo"* @"type_info::<*mut core::void>"]
 @allocatorHandle = unnamed_addr global i8* null
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__true_is_true.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__true_is_true.snap
index fc688f3ac..006b03b4a 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__true_is_true.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__true_is_true.snap
@@ -6,9 +6,9 @@ expression: "pub fn test_true() -> bool {\n    true\n}\n\npub fn test_false() ->
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i1 @test_true() {
 body:
@@ -25,9 +25,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::bool>::name" = private unnamed_addr constant [11 x i8] c"core::bool\00"
-@"type_info::<core::bool>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::bool>::name", i32 0, i32 0), i32 1, i8 1, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::bool>"]
+@"type_info::<core::bool>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"x\82\81m t7\03\CB\F8k\81-;\C9\84", i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"type_info::<core::bool>::name", i32 0, i32 0), i32 1, i8 1, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::bool>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__update_operators.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__update_operators.snap
index 1dcbbce7b..eaeafb285 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__update_operators.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__update_operators.snap
@@ -6,9 +6,9 @@ expression: "pub fn add(a:i32, b:i32) -> i32 {\n  let result = a\n  result += b\
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i32 @add(i32, i32) {
 body:
@@ -45,9 +45,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i32>::name" = private unnamed_addr constant [10 x i8] c"core::i32\00"
-@"type_info::<core::i32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i32>"]
+@"type_info::<core::i32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__update_parameter.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__update_parameter.snap
index 5c6799dcd..e0fa08b89 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__update_parameter.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__update_parameter.snap
@@ -6,9 +6,9 @@ expression: "pub fn add_three(a:i32) -> i32 {\n  a += 3;\n  a\n}"
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define i32 @add_three(i32) {
 body:
@@ -21,9 +21,9 @@ body:
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i32>::name" = private unnamed_addr constant [10 x i8] c"core::i32\00"
-@"type_info::<core::i32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i32>"]
+@"type_info::<core::i32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>"]
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__void_return.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__void_return.snap
index 0adbeeb7b..057327600 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__void_return.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__void_return.snap
@@ -7,10 +7,10 @@ expression: "fn bar() {\n    let a = 3;\n}\npub fn foo(a:i32) {\n    let c = bar
 source_filename = "main.mun"
 
 %DispatchTable = type { void ()* }
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @dispatchTable = external global %DispatchTable
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define void @bar() {
 body:
@@ -30,12 +30,12 @@ body:
 source_filename = "group_name"
 
 %DispatchTable = type { void ()* }
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @dispatchTable = global %DispatchTable { void ()* @bar }
 @"type_info::<core::i32>::name" = private unnamed_addr constant [10 x i8] c"core::i32\00"
-@"type_info::<core::i32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i32>"]
+@"type_info::<core::i32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>"]
 
 declare void @bar()
 
diff --git a/crates/mun_codegen/src/snapshots/mun_codegen__test__while_expr.snap b/crates/mun_codegen/src/snapshots/mun_codegen__test__while_expr.snap
index 23eb4947e..290e9f500 100644
--- a/crates/mun_codegen/src/snapshots/mun_codegen__test__while_expr.snap
+++ b/crates/mun_codegen/src/snapshots/mun_codegen__test__while_expr.snap
@@ -6,9 +6,9 @@ expression: "pub fn foo(n:i32) {\n    while n<3 {\n        n += 1;\n    };\n\n
 ; ModuleID = 'main.mun'
 source_filename = "main.mun"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
-@global_type_table = external global [1 x %struct.MunTypeInfo*]
+@global_type_table = external global [1 x %"mun_codegen::ir::types::TypeInfo"*]
 
 define void @foo(i32) {
 body:
@@ -32,9 +32,9 @@ whilecond3:                                       ; preds = %whilecond
 ; ModuleID = 'group_name'
 source_filename = "group_name"
 
-%struct.MunTypeInfo = type { [16 x i8], i8*, i32, i8, i8 }
+%"mun_codegen::ir::types::TypeInfo" = type { [16 x i8], i8*, i32, i8, i8 }
 
 @"type_info::<core::i32>::name" = private unnamed_addr constant [10 x i8] c"core::i32\00"
-@"type_info::<core::i32>" = private unnamed_addr constant %struct.MunTypeInfo { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
-@global_type_table = constant [1 x %struct.MunTypeInfo*] [%struct.MunTypeInfo* @"type_info::<core::i32>"]
+@"type_info::<core::i32>" = private unnamed_addr constant %"mun_codegen::ir::types::TypeInfo" { [16 x i8] c"\17yzt\19\D62\17\D25\95C\17\88[\FA", i8* getelementptr inbounds ([10 x i8], [10 x i8]* @"type_info::<core::i32>::name", i32 0, i32 0), i32 32, i8 4, i8 0 }
+@global_type_table = constant [1 x %"mun_codegen::ir::types::TypeInfo"*] [%"mun_codegen::ir::types::TypeInfo"* @"type_info::<core::i32>"]
 
diff --git a/crates/mun_codegen/src/test.rs b/crates/mun_codegen/src/test.rs
index b16c7267d..6d3f19865 100644
--- a/crates/mun_codegen/src/test.rs
+++ b/crates/mun_codegen/src/test.rs
@@ -1,11 +1,16 @@
-use crate::{mock::MockDatabase, IrDatabase, ModuleBuilder};
+use crate::{
+    code_gen::{CodeGenContext, ModuleBuilder},
+    ir::file::gen_file_ir,
+    ir::file_group::gen_file_group_ir,
+    mock::MockDatabase,
+    CodeGenDatabase,
+};
 use hir::{
-    diagnostics::DiagnosticSink, line_index::LineIndex, HirDatabase, Module, SourceDatabase,
+    diagnostics::DiagnosticSink, line_index::LineIndex, HirDatabase, Module, SourceDatabase, Upcast,
 };
-use inkwell::OptimizationLevel;
+use inkwell::{context::Context, OptimizationLevel};
 use mun_target::spec::Target;
-use std::cell::RefCell;
-use std::sync::Arc;
+use std::{cell::RefCell, sync::Arc};
 
 #[test]
 fn issue_225() {
@@ -784,34 +789,42 @@ fn incremental_compilation() {
         }
         "#,
     );
-    db.set_optimization_lvl(OptimizationLevel::Default);
+    db.set_optimization_level(OptimizationLevel::Default);
     db.set_target(Target::host_target().unwrap());
 
     {
         let events = db.log_executed(|| {
-            db.file_ir(file_id);
+            db.assembly(file_id);
         });
         assert!(
-            format!("{:?}", events).contains("group_ir"),
+            format!("{:?}", events).contains("module_data"),
+            "{:#?}",
+            events
+        );
+        assert!(
+            format!("{:?}", events).contains("assembly"),
             "{:#?}",
             events
         );
-        assert!(format!("{:?}", events).contains("file_ir"), "{:#?}", events);
     }
 
-    db.set_optimization_lvl(OptimizationLevel::Aggressive);
+    db.set_optimization_level(OptimizationLevel::Aggressive);
 
     {
         let events = db.log_executed(|| {
-            db.file_ir(file_id);
+            db.assembly(file_id);
         });
         println!("events: {:?}", events);
         assert!(
-            !format!("{:?}", events).contains("group_ir"),
+            !format!("{:?}", events).contains("module_data"),
+            "{:#?}",
+            events
+        );
+        assert!(
+            format!("{:?}", events).contains("assembly"),
             "{:#?}",
             events
         );
-        assert!(format!("{:?}", events).contains("file_ir"), "{:#?}", events);
     }
 
     // TODO: Try to disconnect `group_ir` and `file_ir`
@@ -927,7 +940,7 @@ fn test_snapshot_with_optimization(text: &str, opt: OptimizationLevel) {
     let text = text.trim().replace("\n    ", "\n");
 
     let (mut db, file_id) = MockDatabase::with_single_file(&text);
-    db.set_optimization_lvl(opt);
+    db.set_optimization_level(opt);
     db.set_target(Target::host_target().unwrap());
 
     let line_index: Arc<LineIndex> = db.line_index(file_id);
@@ -945,8 +958,8 @@ fn test_snapshot_with_optimization(text: &str, opt: OptimizationLevel) {
     drop(sink);
     let messages = messages.into_inner();
 
-    let module_builder =
-        ModuleBuilder::new(&db, file_id).expect("Failed to initialize module builder");
+    let llvm_context = Context::create();
+    let code_gen = CodeGenContext::new(&llvm_context, db.upcast());
 
     // The thread is named after the test case, so we can use it to name our snapshots.
     let thread_name = std::thread::current()
@@ -954,32 +967,22 @@ fn test_snapshot_with_optimization(text: &str, opt: OptimizationLevel) {
         .expect("The current thread does not have a name.")
         .replace("test::", "");
 
-    let group_ir_value = if !messages.is_empty() {
-        "".to_owned()
+    let (group_ir_value, file_ir_value) = if !messages.is_empty() {
+        ("".to_owned(), messages.join("\n"))
     } else {
-        format!(
-            "{}",
-            db.group_ir(file_id)
-                .llvm_module
-                .print_to_string()
-                .to_string()
-        )
-    };
+        let group_ir = gen_file_group_ir(&code_gen, file_id);
+        let file_ir = gen_file_ir(&code_gen, &group_ir, file_id);
 
-    let file_ir_value = if !messages.is_empty() {
-        messages.join("\n")
-    } else {
-        format!(
-            "{}",
-            db.file_ir(file_id)
-                .llvm_module
-                .print_to_string()
-                .to_string()
+        (
+            format!("{}", group_ir.llvm_module.print_to_string().to_string(),),
+            format!("{}", file_ir.llvm_module.print_to_string().to_string(),),
         )
     };
 
     // To ensure that we test symbol generation
     if messages.is_empty() {
+        let module_builder =
+            ModuleBuilder::new(&code_gen, file_id).expect("Failed to initialize module builder");
         let _obj_file = module_builder.build().expect("Failed to build object file");
     }
 
diff --git a/crates/mun_runtime/tests/runtime.rs b/crates/mun_runtime/tests/runtime.rs
index 6f72730a3..65c17a76f 100644
--- a/crates/mun_runtime/tests/runtime.rs
+++ b/crates/mun_runtime/tests/runtime.rs
@@ -11,7 +11,6 @@ fn error_assembly_not_linkable() {
     ",
         |builder| builder,
     );
-
     assert_eq!(
         format!("{}", driver.unwrap_err()),
         format!(
@@ -46,6 +45,5 @@ fn arg_missing_bug() {
     }",
         |builder| builder,
     );
-
     driver.unwrap();
 }